diff --git a/.github/scripts/generate_api_docs.py b/.github/scripts/generate_api_docs.py new file mode 100644 index 0000000..8fc14b4 --- /dev/null +++ b/.github/scripts/generate_api_docs.py @@ -0,0 +1,238 @@ +#!/usr/bin/env python3 +""" +Generate simplified API documentation from FastAPI OpenAPI spec. +This script extracts the OpenAPI specification from the web-api service +and generates clean markdown documentation in docs/api/ +""" + +import json +import os +import sys +from pathlib import Path +from typing import Any + +# Add the web_api module to the path +sys.path.insert(0, str(Path(__file__).parent.parent / "projects" / "web_api")) + + +def extract_openapi_spec() -> dict[str, Any]: + """Extract OpenAPI specification from the FastAPI app.""" + try: + # Set minimal environment variables to avoid database connection issues + os.environ.setdefault("POSTGRES_CONNECTION_URI", "postgresql://test:test@localhost/test") + os.environ.setdefault("MINIO_ROOT_USER", "test") + os.environ.setdefault("MINIO_ROOT_PASSWORD", "test") + os.environ.setdefault("MINIO_SERVER", "localhost:9000") + os.environ.setdefault("BUCKET_NAME", "test") + + # Mock Dapr dependencies to avoid connection issues + import sys + from unittest.mock import MagicMock + + # Mock dapr modules before importing web_api.main + mock_dapr_clients = MagicMock() + mock_dapr_ext_fastapi = MagicMock() + sys.modules["dapr.clients"] = mock_dapr_clients + sys.modules["dapr.ext.fastapi"] = mock_dapr_ext_fastapi + + # Mock the specific classes that are imported + mock_dapr_clients.DaprClient = MagicMock() + mock_dapr_ext_fastapi.DaprApp = MagicMock() + + from web_api.main import app + + return app.openapi() + except ImportError as e: + print(f"Error importing web_api.main: {e}") + print("Make sure to run: cd projects/web_api && poetry install") + sys.exit(1) + except Exception as e: + print(f"Error generating OpenAPI spec: {e}") + sys.exit(1) + + +def format_parameter(param: dict[str, Any]) -> str: + """Format a parameter for documentation.""" + param_type = param.get("schema", {}).get("type", "unknown") + required = "**required**" if param.get("required", False) else "optional" + description = param.get("description", "") + + return f"- `{param['name']}` ({param_type}, {required}): {description}" + + +def format_request_body(request_body: dict[str, Any]) -> str: + """Format request body information.""" + if not request_body: + return "" + + content = request_body.get("content", {}) + if "application/json" in content: + schema = content["application/json"].get("schema", {}) + if "$ref" in schema: + # Extract model name from reference + model_name = schema["$ref"].split("/")[-1] + return f"**Request Body:** `{model_name}` (JSON)" + elif schema.get("type") == "object": + return "**Request Body:** JSON object" + + return "**Request Body:** See OpenAPI spec for details" + + +def generate_endpoint_docs(paths: dict[str, Any]) -> str: + """Generate documentation for all endpoints.""" + docs = [] + + # Group endpoints by tags + tagged_endpoints = {} + untagged_endpoints = [] + + for path, methods in paths.items(): + for method, details in methods.items(): + if method.upper() not in ["GET", "POST", "PUT", "DELETE", "PATCH"]: + continue + + tags = details.get("tags", []) + endpoint_info = {"path": path, "method": method.upper(), "details": details} + + if tags: + tag = tags[0] # Use first tag + if tag not in tagged_endpoints: + tagged_endpoints[tag] = [] + tagged_endpoints[tag].append(endpoint_info) + else: + untagged_endpoints.append(endpoint_info) + + # Generate docs for each tag group + for tag, endpoints in sorted(tagged_endpoints.items()): + docs.append(f"## {tag.title()}") + docs.append("") + + for endpoint in sorted(endpoints, key=lambda x: (x["path"], x["method"])): + docs.extend(format_endpoint(endpoint)) + + docs.append("") + + # Add untagged endpoints if any + if untagged_endpoints: + docs.append("## Other Endpoints") + docs.append("") + + for endpoint in sorted(untagged_endpoints, key=lambda x: (x["path"], x["method"])): + docs.extend(format_endpoint(endpoint)) + + docs.append("") + + return "\n".join(docs) + + +def format_endpoint(endpoint: dict[str, Any]) -> list[str]: + """Format a single endpoint for documentation.""" + path = endpoint["path"] + method = endpoint["method"] + details = endpoint["details"] + + docs = [] + + # Endpoint header + summary = details.get("summary", "") + docs.append(f"### `{method} {path}`") + docs.append("") + + if summary: + docs.append(summary) + docs.append("") + + # Description + description = details.get("description", "") + if description and description != summary: + docs.append(description) + docs.append("") + + # Parameters + parameters = details.get("parameters", []) + if parameters: + docs.append("**Parameters:**") + docs.append("") + for param in parameters: + docs.append(format_parameter(param)) + docs.append("") + + # Request body + request_body = details.get("requestBody", {}) + if request_body: + body_docs = format_request_body(request_body) + if body_docs: + docs.append(body_docs) + docs.append("") + + # Response summary (simplified) + responses = details.get("responses", {}) + if responses: + success_responses = [code for code in responses.keys() if code.startswith("2")] + if success_responses: + docs.append(f"**Returns:** {', '.join(success_responses)} on success") + docs.append("") + + docs.append("---") + docs.append("") + + return docs + + +def generate_markdown_docs(spec: dict[str, Any]) -> str: + """Generate complete markdown documentation.""" + info = spec.get("info", {}) + title = info.get("title", "API Documentation") + version = info.get("version", "Unknown") + description = info.get("description", "") + + docs = [ + f"# {title}", + "", + f"**Version:** {version}", + "", + ] + + if description: + docs.extend([description, ""]) + + docs.extend(["This documentation is automatically generated from the OpenAPI specification.", "", "---", ""]) + + # Add endpoints + paths = spec.get("paths", {}) + if paths: + docs.append(generate_endpoint_docs(paths)) + + return "\n".join(docs) + + +def main(): + """Main function to generate API documentation.""" + print("Extracting OpenAPI specification...") + spec = extract_openapi_spec() + + print("Generating markdown documentation...") + markdown_content = generate_markdown_docs(spec) + + # Ensure docs directory exists (repo root) + docs_dir = Path(__file__).parent.parent.parent / "docs" + docs_dir.mkdir(parents=True, exist_ok=True) + + # Write main API documentation + api_docs_file = docs_dir / "api.md" + with open(api_docs_file, "w", encoding="utf-8") as f: + f.write(markdown_content) + + print(f"API documentation generated: {api_docs_file}") + + # Also save the raw OpenAPI spec for reference + openapi_file = docs_dir / "openapi.json" + with open(openapi_file, "w", encoding="utf-8") as f: + json.dump(spec, f, indent=2) + + print(f"OpenAPI specification saved: {openapi_file}") + print("Documentation generation complete!") + + +if __name__ == "__main__": + main() diff --git a/.github/workflows/docker-build-base.yml b/.github/workflows/docker-build-base.yml index d993ed8..4da6fc5 100644 --- a/.github/workflows/docker-build-base.yml +++ b/.github/workflows/docker-build-base.yml @@ -5,7 +5,6 @@ on: branches: [ "main" ] paths: - 'infra/docker/python_base/**' - - 'projects/InspectAssembly/**' workflow_dispatch: env: @@ -91,29 +90,6 @@ jobs: cache-from: type=gha,scope=python-base-prod-${{ matrix.arch }} cache-to: type=gha,mode=max,scope=python-base-prod-${{ matrix.arch }} - # InspectAssembly Base Image - - name: Extract metadata for InspectAssembly image - id: meta-inspect-assembly - uses: docker/metadata-action@v5 - with: - images: ${{ env.REGISTRY }}/${{ env.IMAGE_PREFIX }}/inspect-assembly - tags: | - type=sha,format=short,suffix=-${{ matrix.arch }} - type=ref,event=branch,suffix=-${{ matrix.arch }} - type=raw,value=latest-${{ matrix.arch }},enable={{is_default_branch}} - - - name: Build and push InspectAssembly image - uses: docker/build-push-action@v5 - with: - context: ./projects/InspectAssembly - file: ./projects/InspectAssembly/Dockerfile - push: true - platforms: ${{ matrix.platform }} - tags: ${{ steps.meta-inspect-assembly.outputs.tags }} - labels: ${{ steps.meta-inspect-assembly.outputs.labels }} - cache-from: type=gha,scope=inspect-assembly-${{ matrix.arch }} - cache-to: type=gha,mode=max,scope=inspect-assembly-${{ matrix.arch }} - # Create multi-arch manifests create-manifests: needs: build-base-images @@ -175,26 +151,3 @@ jobs: "${tag}-arm64" fi done - - # InspectAssembly Manifest - - name: Extract metadata for InspectAssembly manifest - id: meta-inspect-assembly - uses: docker/metadata-action@v5 - with: - images: ${{ env.REGISTRY }}/${{ env.IMAGE_PREFIX }}/inspect-assembly - tags: | - type=sha,format=short - type=ref,event=branch - type=raw,value=latest,enable={{is_default_branch}} - - - name: Create and push InspectAssembly multi-arch manifest - run: | - echo '${{ steps.meta-inspect-assembly.outputs.tags }}' | while IFS= read -r tag; do - if [ -n "$tag" ]; then - echo "Creating manifest for: $tag" - docker buildx imagetools create \ - --tag "$tag" \ - "${tag}-amd64" \ - "${tag}-arm64" - fi - done \ No newline at end of file diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index 165c467..7937037 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -24,36 +24,36 @@ jobs: matrix: runner: [ubuntu-22.04, ubuntu-22.04-arm] service: - - name: web-api + - name: agents context: . - dockerfile: ./projects/web_api/Dockerfile - - name: dotnet-api + dockerfile: ./projects/agents/Dockerfile + - name: alerting context: . - dockerfile: ./projects/dotnet_api/Dockerfile + dockerfile: ./projects/alerting/Dockerfile + - name: cli + context: . + dockerfile: ./projects/cli/Dockerfile + - name: document-conversion + context: . + dockerfile: ./projects/document_conversion/Dockerfile + - name: dotnet-service + context: . + dockerfile: ./projects/dotnet_service/Dockerfile - name: file-enrichment context: . dockerfile: ./projects/file_enrichment/Dockerfile - name: frontend context: ./projects/frontend dockerfile: ./projects/frontend/Dockerfile - - name: jupyter - context: ./projects/jupyter - dockerfile: ./projects/jupyter/Dockerfile - - name: alerting - context: . - dockerfile: ./projects/alerting/Dockerfile - - name: triage - context: . - dockerfile: ./projects/triage/Dockerfile - - name: cli - context: . - dockerfile: ./projects/cli/Dockerfile - name: housekeeping context: . dockerfile: ./projects/housekeeping/Dockerfile - - name: document-conversion + - name: jupyter + context: ./projects/jupyter + dockerfile: ./projects/jupyter/Dockerfile + - name: web-api context: . - dockerfile: ./projects/document_conversion/Dockerfile + dockerfile: ./projects/web_api/Dockerfile steps: - name: Checkout repository diff --git a/.github/workflows/generate-api-docs.yml b/.github/workflows/generate-api-docs.yml new file mode 100644 index 0000000..ad2e869 --- /dev/null +++ b/.github/workflows/generate-api-docs.yml @@ -0,0 +1,46 @@ +name: Generate API Documentation + +on: + push: + branches: [main] + paths: + - 'projects/web_api/**' + - '.github/workflows/generate-api-docs.yml' + - '.github/scripts/generate_api_docs.py' + workflow_dispatch: # For manual triggering + +jobs: + generate-docs: + runs-on: ubuntu-22.04 + + permissions: + contents: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup Python virtualenv + run: | + pip install --upgrade pip + python -m venv env + source env/bin/activate + cd projects/web_api + pip install poetry + poetry install --only main + + - name: Generate API documentation + run: | + source env/bin/activate + cd projects/web_api + poetry run python ../../.github/scripts/generate_api_docs.py + + - name: Commit documentation changes + uses: stefanzweifel/git-auto-commit-action@v5 + with: + commit_message: 'docs: update API documentation [skip ci]' + file_pattern: 'docs/api/*' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.gitignore b/.gitignore index 8b87fa5..1c76536 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .DS_STORE +version.json # Byte-compiled / optimized / DLL files __pycache__/ @@ -175,3 +176,9 @@ cython_debug/ old/ +projects/dotnet_service/obj/ +projects/dotnet_service/bin/ + +projects/jupyter/notebooks/*.csv + +projects/frontend/public/env.js diff --git a/.vscode/nemesis.code-workspace b/.vscode/nemesis.code-workspace index 179f4d8..1784c39 100644 --- a/.vscode/nemesis.code-workspace +++ b/.vscode/nemesis.code-workspace @@ -12,6 +12,10 @@ "path": "../infra", "name": "infra" }, + { + "path": "../libs/chromium", + "name": "libs → chromium" + }, { "path": "../libs/common", "name": "libs → common" @@ -20,6 +24,18 @@ "path": "../libs/file_enrichment_modules", "name": "libs → file_enrichment_modules", }, + { + "path": "../libs/file_linking", + "name": "libs → file_linking" + }, + { + "path": "../libs/nemesis_dpapi", + "name": "libs → nemesis_dpapi" + }, + { + "path": "../projects/agents", + "name": "projects → agents" + }, { "path": "../projects/alerting", "name": "projects → alerting" @@ -56,10 +72,6 @@ "path": "../projects/noseyparker_scanner", "name": "projects → noseyparker_scanner" }, - { - "path": "../projects/triage", - "name": "projects → triage" - }, { "path": "../projects/web_api", "name": "projects → web_api" @@ -72,6 +84,8 @@ "settings": { "files.exclude": { "**/__pycache__": true, + "**/.benchmarks": true, + "**/.ruff_cache": true, "**/.DS_Store": true, "**/.git": true, "**/.hg": true, @@ -81,16 +95,9 @@ "**/.venv": true, "**/CVS": true, "**/Thumbs.db": true, + "projects/frontend/node_modules": true, + "projects/frontend/dist": true }, - "[python]": { - "diffEditor.ignoreTrimWhitespace": false, - "editor.wordBasedSuggestions": "off", - "editor.formatOnSave": true, - "editor.defaultFormatter": "charliermarsh.ruff" - }, - "python.defaultInterpreterPath": "python", - // "python.pixiToolPath": "", - // "python.pipenvPath": "", }, "extensions": { "recommendations": [ diff --git a/.vscode/settings.json b/.vscode/settings.json index 4bd3b88..36429f2 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -6,12 +6,12 @@ "editor.formatOnSave": false }, "[python]": { - "editor.formatOnSave": true, + // "editor.formatOnSave": true, "editor.codeActionsOnSave": { - "source.fixAll": "explicit", - "source.organizeImports": "explicit" + // "source.fixAll": "explicit", + // "source.organizeImports": "explicit" }, - "editor.defaultFormatter": "charliermarsh.ruff" + // "editor.defaultFormatter": "charliermarsh.ruff" }, "autoDocstring.docstringFormat": "google", "editor.formatOnSave": false, diff --git a/CHANGELOG.md b/CHANGELOG.md index ec1a25c..24e69f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,207 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [2.1.4] + +### Added + +- Auto-decryption of Chromium and DPAPI related data: + - Cookie and Login Data(saved passwords) DPAPI value decryption + - Added Chromium UI page to display Cookie/Login Data + - CNG/Chromekey file enrichment module (parser + decryptor) + - Chromium ABE v3 decryption (via decrypted CNG keys) +- nemesis_dpapi support library: + - Added Postgres support for DPAPI backend + - Dapr pubsub integration for DPAPI-related event broadcasting + - Added uniqueness and write constraints to prevent duplicate master/backup keys + - Can now differentiate between user/system masterkeys + - Added docs, examples, tests, and decryption benchmarks + - Added support for v3 masterkeys decrypted with backup key +- New file enrichment modules: + - `dpapi_masterkey` - Extracts encrypted masterkeys from user/system DPAPI masterkey files and decrypts them, if possible. + - `exif_metadata` file enrichment module for supported image files + - Added `cng_file` +- Added support for async code in Dapr activities used in enrichment modules +- Findings Page: Modified the Severities filter button to use checkboxes +- Multi-language Tika OCR support (`TIKA_OCR_LANGUAGES` ENV var, see `compose.yaml`) +- Text translation agent +- Retroactive DPAPI decryption: + - Chromium Local State files when plaintext masterkeys are submitted/decrypted + - Google Chromekeys (CNG file based), including decrypting applicable Local State files + - Chromium Cookies/Login Data files +- File linking: + - Enchanced file linking with placeholders in the path that resolve once a matching file is collected. + - File Viewer: Added ability to delete file linkings from the FileViewer + - File Browser: Added the collection reason and "Linked to by" fields on the "Files that need collection" option +- CLI: add a `--folder` option to the submit command that allows you to specify the path to the root folder of uploaded files. +- Reporting functionality + - SYSTEM wide and per SOURCE + - API endpoints for statistic and PDF generation (via Gotenberg conversion) + - Reporting summarization agent + +### Changed + +- Now use DPAPIck3 for blob decryption +- Bumped Dapr version to 1.16.1 +- DPAPI_SYSTEM key pulled from registry parsing is now registered with the backend +- Updated category filters for frontend +- Collapsed inbound/outbound labels for linked files in dashboard +- Update Prometheus endpoints +- Chromium Local State, Cookies, and Login Data files now don't require hard paths +- Reg hive file linkings now done programmatically instead of via rules +- SYSTEM masterkey file linkings now done programmatically instead of via rules +- Added more details to errors that cause a workflow to die +- Optimized DPAPI masterkey decryption based on the type of masterkey +- Converted several DB calls to async code +- Optimized the housekeeping code to run in parallel and use transactions (where possible) + +### Fixed + +- Properly use entropy for DPAPI blob decryption +- Lots of async issues +- Fixed tag search issue +- Fix to keep strings.txt from sqlite dbs from processing +- Path normalization: Fixed many bugs, removed lots of duplicate normalization, standardized normalization upon initial ingestion. +- Countless linting and other fixes + + +## [2.1.3] + +### Added + +- "Chromium" page in the web frontend to display history, downloads, cookies, logins, and state keys + - Includes filtering + CSV downloads for displayed data +- "File Browser" in the web frontend +- "DPAPI" viewer/submission pages in the web frontend +- Linked file tracking (via the ./libs/file_linking/ library) + - Linked files exposed in the "File Browser" frontend and file viewer pages +- Adaptation of @Dreadnode's .NET reversing agent to `agents` +- "chromium" standard library in ./libs/ for parsing Chromium based files +- "nemesis_dpapi" library in ./libs/ for handling DPAPI related data/decryption + - Includes in-memory storage of keys as well as postgres + - Allows subscriptions to react to DPAPI-related events (e.g. new backup key, new plaintext masterkey, etc.) +- `registry_hive` parsing module that extracts bootkeys + local accounts + lsa secrets from linked hives +- Refactored file enrichment web API code to be more modular +- API route to submit DPAPI credential material +- Auto-building API documents for ./docs/api.md from the FastAPI routes in `web_api` container +- Documentation for "Containers" and LLM functionality +- Changed default FileList view to All Files, added unviewed indicator dot, and change search default to always include wildcards. + +### Changed + +- Standardize logging + fix suppressed logs +- DPAPI keys carved from LSASS dumps now saved via the nemesis_dpapi library + +### Fixed + +- Frontend live file reload +- `certificate` and `keytab` file enrichment modules + + +## [2.1.2] - 2025-08-22 + +### Added + +- Ability to drag/drop folders onto the file upload page +- basic `triage` container greatly expanded to `agents` + - JWT + finding validator agents implemented + - Generalized/expandable agent infrastructure built + - Confidence score, explanation, and risk detail returned by finding triage + - "triage consensus" added for multiple triage values for the same file +- Tracing for `agents` added with Arize Phoenix (/phoenix, if --monitoring is enabled) + - Token costs pulled from LiteLLM instance and manually synced to Phoenix for cost tracking +- Triage details added to finding table entries and findings modal +- Settings frontend page now has commit/build date/etc. info and Slack alert channel info (if configured) +- Repeat option added to submit script +- Conditionally shown "Agents" page in the frontend that shows current agents and token spend stats + - Also allows for editing Agent prompts in the UI + +### Changed + +- "explanation" field added to the findings_triage_history table in the schema +- Help page in frontend only shows routes for services that are enabled +- Logs suppressed during Dapr replay (only show on first run) +- Removed loud FastAPI tracer +- Enrichment modules rolled into one activity (no longer each their own) for optimization + - Also means a reduction in file download actions for enrichment modules - modules modified to support this +- LLM credential analysis and text summarization enrichment modules ported to `agents` + +### Fixed + +- Markdown escape for displayed extracted hashes +- Fixed PE parsing not throwing an exception +- Fixed runtime deprecation warning +- Maintain references to various asyncio tasks + + +## [2.1.1] - 2025-08-01 + +### Added + +- LiteLLM server (with "llm" profile in Docker) to serve future LLM integrations + - Includes cost limits +- Display/linking to originating container for files derived from containers +- Support for include/exclude filters for the `/containers` API + - Added filter support into `cli` container + submit.sh +- Processing for a number of disk image formats +- File monitoring for (large) containers copied to MOUNTED_CONTAINER_PATH + - Containers have files extracted + processed + - Used for workflows with very large containers/disk images +- Velociraptor connector (server event .yaml option) +- `noseyparker_scanner` now can scan zips and .git repos + - Includes relevant match info in results (can be set by ENV vars) + +### Changed + +- Expired containers now cleaned up +- Made "timestamp" and "expiration" submission fields optional (filled with defaults) +- Bumped Dapr version to 1.15.8 +- Filtering by URL for containers +- Bulk enrichment system now uses pub/sub +- `triage` connecter now uses LiteLLM for models via Rigging +- Pagination in FileList view for large number of files + +### Fixed + +- "source" field propagation for containers +- Container filtering in dashboard +- Arguments with value ordering error in submit.sh + + +## [2.1.0] - 2025-07-20 + +### Added + +- Retries for submit.py +- `3_workflow_performance.ipynb` Jupyter notebook to assess pipeline performance +- "source" field (to represent hostname, source site, etc.) integrated into schema + frontend +- Start of bulk-enrichment re-rerunning, including re-running Yara rules from the dashboard +- New system for large "container" triaging + - New `/api/containers` route + - Container process tracking system using pub/sub from `file_enrichment` -> `web_api` + - Live updating container tracking in the dashboard (new "Containers" tab) + - submit.sh/monitor.sh scripts now can submit "containers" +- Internal queues now cleaned on up system delete/reset +- PostgreSQL NOTIFY/LISTEN system for `file_enrichment` workers + +### Changed + +- Timeouts/improved submit logic for the web_api +- Bumped Dapr version to 1.15.6 +- Combined `dotnet_api` and `InspectAssembly` into single, streamlined pure .NET `dotnet_service` container +- Eliminated the internal file-enrichment queue + - Now relies on the Dapr pub/sub queue (RabbitMQ) to provide backpressure +- Stale workflows periodically cleaned up + +### Fixed + +- Limits/concurrency fixes for NoseyParker scanner to prevent OOM errors +- Implemented queue/workflow persistence + - RabbitMQ queues now restored properly even if containers are completely removed + - In-flight workflows re-submitted for processing + + ## [2.0.1] ### Changed diff --git a/compose.base.yaml b/compose.base.yaml index 4a47b64..749c0bf 100644 --- a/compose.base.yaml +++ b/compose.base.yaml @@ -13,13 +13,3 @@ services: build: context: ./infra/docker/python_base dockerfile: prod.Dockerfile - - # Long term, this shouldn't be with the base image. - # We should publish the build artifact and use that - # in the final image (instead of copying from this one). - # Until then. we'll keep it here so we don't have to - # rebuild it each time we rebuild just the services. - inspect-assembly: - build: - context: ./projects/InspectAssembly/ - dockerfile: Dockerfile \ No newline at end of file diff --git a/compose.override.yaml b/compose.override.yaml index 34c5c31..08dffdf 100644 --- a/compose.override.yaml +++ b/compose.override.yaml @@ -14,6 +14,8 @@ services: - ./libs/:/src/libs/ - ./projects/web_api/web_api:/src/projects/web_api/web_api - /src/projects/web_api/.venv + environment: + - LOG_LEVEL=${LOG_LEVEL:-DEBUG} noseyparker-scanner: image: !reset null @@ -24,16 +26,14 @@ services: environment: - RUST_LOG=debug - dotnet-api: + dotnet-service: image: !reset null build: context: . - dockerfile: ./projects/dotnet_api/Dockerfile + dockerfile: ./projects/dotnet_service/Dockerfile target: dev - volumes: - - ./libs/:/src/libs/ - - ./projects/dotnet_api/dotnet_api:/src/projects/dotnet_api/dotnet_api - - /src/projects/dotnet_api/.venv + environment: + - ASPNETCORE_ENVIRONMENT=Development file-enrichment: image: !reset null @@ -42,7 +42,7 @@ services: dockerfile: ./projects/file_enrichment/Dockerfile target: dev environment: - - LOG_LEVEL=DEBUG + - LOG_LEVEL=${LOG_LEVEL:-DEBUG} volumes: - ./libs/:/src/libs/ - ./projects/file_enrichment/file_enrichment:/src/projects/file_enrichment/file_enrichment @@ -57,19 +57,19 @@ services: target: dev command: > sh -c " - mkdir -p /app-runtime && - cp -r /app/* /app-runtime/ && + if [ -f /version.json ]; then cp /version.json /app/public/version.json; fi && SECRET=\"$$HASURA_ADMIN_SECRET\" && - sed -i \"s/\\$$HASURA_ADMIN_SECRET/$$SECRET/g\" /app-runtime/index.html && - cd /app-runtime && + echo \"window.ENV = { HASURA_ADMIN_SECRET: '$$SECRET' };\" > /app/public/env.js && + cd /app && npm run dev " volumes: - ./projects/frontend/index.html:/app/index.html:ro - ./projects/frontend/package.json:/app/package.json:ro - - ./projects/frontend/public:/app/public:ro + - ./projects/frontend/public:/app/public - ./projects/frontend/src:/app/src:ro - ./projects/frontend/vite.config.js:/app/vite.config.js:ro + - ./version.json:/version.json:ro labels: - "traefik.http.services.frontend.loadbalancer.server.port=3000" @@ -88,13 +88,21 @@ services: context: . dockerfile: ./projects/alerting/Dockerfile target: dev + environment: + - LOG_LEVEL=${LOG_LEVEL:-DEBUG} - triage: + agents: image: !reset null build: context: . - dockerfile: ./projects/triage/Dockerfile + dockerfile: ./projects/agents/Dockerfile target: dev + environment: + - LOG_LEVEL=${LOG_LEVEL:-DEBUG} + volumes: + - ./libs/:/src/libs/ + - ./projects/agents/agents:/src/projects/agents/agents + - /src/projects/agents/.venv housekeeping: image: !reset null @@ -108,6 +116,7 @@ services: - /src/projects/housekeeping/.venv environment: - CLEANUP_SCHEDULE=*/3 * * * * # Test every 3 minutes + - LOG_LEVEL=${LOG_LEVEL:-DEBUG} document-conversion: image: !reset null @@ -115,7 +124,16 @@ services: context: . dockerfile: ./projects/document_conversion/Dockerfile target: dev + args: + - TIKA_OCR_LANGUAGES=${TIKA_OCR_LANGUAGES:-eng} volumes: - ./libs/:/src/libs/ - ./projects/document_conversion/document_conversion:/src/projects/document_conversion/document_conversion - - /src/projects/document_conversion/.venv \ No newline at end of file + - /src/projects/document_conversion/.venv + environment: + - LOG_LEVEL=${LOG_LEVEL:-DEBUG} + + postgres: + # expose the port locally + ports: + - "5432:5432" \ No newline at end of file diff --git a/compose.prod.build.yaml b/compose.prod.build.yaml index 6223ac8..6a555b7 100644 --- a/compose.prod.build.yaml +++ b/compose.prod.build.yaml @@ -17,11 +17,11 @@ services: dockerfile: ./projects/noseyparker_scanner/Dockerfile target: prod - dotnet-api: + dotnet-service: image: !reset null build: context: . - dockerfile: ./projects/dotnet_api/Dockerfile + dockerfile: ./projects/dotnet_service/Dockerfile target: prod file-enrichment: @@ -59,11 +59,11 @@ services: dockerfile: ./projects/alerting/Dockerfile target: prod - triage: + agents: image: !reset null build: context: . - dockerfile: ./projects/triage/Dockerfile + dockerfile: ./projects/agents/Dockerfile target: prod housekeeping: diff --git a/compose.yaml b/compose.yaml index dd65508..936198d 100644 --- a/compose.yaml +++ b/compose.yaml @@ -9,10 +9,12 @@ volumes: jaeger_data: loki_data: minio_data: + phoenix_data: postgres_data: prometheus_data: rabbitmq_data: empty: + empty-mounted-containers: services: ######################################## @@ -30,6 +32,9 @@ services: - APP_ID=web-api - DAPR_GRPC_PORT=50001 - DAPR_HTTP_PORT=3500 + - DEFAULT_EXPIRATION_DAYS=${DEFAULT_EXPIRATION_DAYS:-100} + volumes: + - ${MOUNTED_CONTAINER_PATH:-empty-mounted-containers}:/mounted-containers logging: &logging-config driver: "json-file" options: { max-size: "10m", max-file: "3" } @@ -45,14 +50,14 @@ services: - "traefik.http.routers.web-api.tls=true" - "traefik.http.routers.web-api.middlewares=auth" web-api-dapr: - image: "daprio/daprd:1.15.5" + image: "daprio/daprd:1.16.1" command: [ "./daprd", "--app-id", "web-api", "--max-body-size", - "300Mi", + "1Gi", "--app-port", "8000", "--dapr-http-port", @@ -67,6 +72,9 @@ services: "/dapr/components", "--config", "/dapr/configuration/config.yaml", + "--enable-metrics", + "--dapr-graceful-shutdown-seconds", + "5", ] volumes: - ./infra/dapr/components/pubsub.yaml:/dapr/components/pubsub.yaml:ro @@ -76,6 +84,14 @@ services: - MINIO_ROOT_PASSWORD=${MINIO_ROOT_PASSWORD:?} - MINIO_ROOT_USER=${MINIO_ROOT_USER:?} - RABBITMQ_CONNECTION_STRING=amqp://${RABBITMQ_USER}:${RABBITMQ_PASSWORD}@rabbitmq:5672 + - POSTGRES_USER=${POSTGRES_USER:?} + - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:?} + - POSTGRES_HOST=${POSTGRES_HOST:-postgres} + - POSTGRES_PORT=${POSTGRES_PORT:-5432} + - POSTGRES_DB=${POSTGRES_DB:-enrichment} + - POSTGRES_PARAMETERS=${POSTGRES_PARAMETERS:-sslmode=disable} + - RABBITMQ_PASSWORD=${RABBITMQ_PASSWORD:?} + - RABBITMQ_USER=${RABBITMQ_USER:?} depends_on: web-api: { condition: service_started } placement: { condition: service_started } @@ -96,16 +112,20 @@ services: - MINIO_ENDPOINT=http://minio:9000 - MINIO_SECRET_KEY=${MINIO_ROOT_PASSWORD:?} - RUST_LOG=info - - SNIPPET_LENGTH=512 + - SNIPPET_LENGTH=512 # context length around any Nosey Parker matches + - MAX_CONCURRENT_FILES=2 # maximum number of concurrent files to scan + - MAX_FILE_SIZE_MB=200 # maximum file size to scan + - DECOMPRESS_ZIPS=true # whether to decompress+scan zips + - MAX_EXTRACT_SIZE_MB=1000 # maximum number of bytes to extract if decompressing volumes: - ./projects/noseyparker_scanner/custom_rules/:/opt/noseyparker:ro noseyparker-scanner-dapr: - image: "daprio/daprd:1.15.5" + image: "daprio/daprd:1.16.1" command: [ "./daprd", "--max-body-size", - "300Mi", + "1Gi", "--app-id", "noseyparker-scanner", "--app-port", @@ -124,6 +144,9 @@ services: "/dapr/configuration/config.yaml", "--app-max-concurrency", "1", + "--enable-metrics", + "--dapr-graceful-shutdown-seconds", + "5", ] environment: - RABBITMQ_CONNECTION_STRING=amqp://${RABBITMQ_USER}:${RABBITMQ_PASSWORD}@rabbitmq:5672 @@ -134,60 +157,64 @@ services: depends_on: [noseyparker-scanner, placement] network_mode: "service:noseyparker-scanner" - dotnet-api: - image: ghcr.io/specterops/nemesis/dotnet-api:latest + dotnet-service: + image: ghcr.io/specterops/nemesis/dotnet-service:latest healthcheck: - test: ["CMD", "wget", "--spider", "-q", "http://localhost:1337/healthz"] - interval: 10s - timeout: 5s - retries: 5 - environment: - - APP_ID=dotnet-api - - DAPR_GRPC_PORT=50010 - - DAPR_HTTP_PORT=3507 - logging: *logging-config + test: ["CMD", "curl", "-f", "http://localhost:5000/health"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 30s depends_on: - postgres: { condition: service_started } - placement: { condition: service_started } + minio: { condition: service_healthy } rabbitmq: { condition: service_healthy } - dotnet-api-dapr: - image: "daprio/daprd:1.15.5" + environment: + - DAPR_GRPC_PORT=50014 + - DAPR_PORT=3514 + - MINIO_ACCESS_KEY=${MINIO_ROOT_USER:?} + - MINIO_BUCKET=files + - MINIO_ENDPOINT=http://minio:9000 + - MINIO_SECRET_KEY=${MINIO_ROOT_PASSWORD:?} + - ASPNETCORE_URLS=http://0.0.0.0:5000 + expose: + - "5000" + dotnet-service-dapr: + image: "daprio/daprd:1.16.1" command: [ "./daprd", "--max-body-size", - "300Mi", + "1Gi", "--app-id", - "dotnet-api", + "dotnet-service", "--app-port", - "1337", + "5000", + "--app-protocol", + "http", "--dapr-http-port", - "3507", + "3514", "--dapr-grpc-port", - "50010", + "50014", "--placement-host-address", "placement:50006", - "--scheduler-host-address", - "scheduler:50007", "--resources-path", "/dapr/components", "--config", "/dapr/configuration/config.yaml", + "--app-max-concurrency", + "1", + "--enable-metrics", + "--dapr-graceful-shutdown-seconds", + "5", ] + environment: + - RABBITMQ_CONNECTION_STRING=amqp://${RABBITMQ_USER}:${RABBITMQ_PASSWORD}@rabbitmq:5672 volumes: + - ./infra/dapr/components/pubsub.yaml:/dapr/components/pubsub.yaml:ro - ./infra/dapr/components/secretstore.yaml:/dapr/components/secretstore.yaml:ro - ./infra/dapr/configuration/config_monitoring_${NEMESIS_MONITORING:-disabled}.yaml:/dapr/configuration/config.yaml:ro - environment: - - MINIO_ROOT_PASSWORD=${MINIO_ROOT_PASSWORD:?} - - MINIO_ROOT_USER=${MINIO_ROOT_USER:?} - - POSTGRES_CONNECTION_STRING=host=postgres user=${POSTGRES_USER} password=${POSTGRES_PASSWORD} dbname=enrichment port=5432 sslmode=disable - - RABBITMQ_CONNECTION_STRING=amqp://${RABBITMQ_USER}:${RABBITMQ_PASSWORD}@rabbitmq:5672 - depends_on: - dotnet-api: { condition: service_started } - placement: { condition: service_started } - scheduler: { condition: service_started } - rabbitmq: { condition: service_healthy } - network_mode: "service:dotnet-api" + depends_on: [dotnet-service, placement] + network_mode: "service:dotnet-service" file-enrichment: image: ghcr.io/specterops/nemesis/file-enrichment:latest @@ -203,30 +230,31 @@ services: - APP_ID=file-enrichment - DAPR_GRPC_PORT=50003 - DAPR_HTTP_PORT=3503 - - LOG_LEVEL=INFO - - MAX_PARALLEL_ENRICHMENT_MODULES=${MAX_PARALLEL_ENRICHMENT_MODULES:-5} - - MAX_PARALLEL_WORKFLOWS=${MAX_PARALLEL_WORKFLOWS:-5} + - LOG_LEVEL=${LOG_LEVEL:-INFO} - MAX_WORKFLOW_EXECUTION_TIME=${MAX_WORKFLOW_EXECUTION_TIME:-300} - NEMESIS_MONITORING=${NEMESIS_MONITORING:-disabled} - NEMESIS_URL=${NEMESIS_URL:?} - OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://otel-collector:4317 - OTEL_EXPORTER_OTLP_TRACES_ENDPOINT_INSECURE=true + - PYTHONASYNCIOTHREADPOOLSIZE=100 # Increase thread pool for asyncio.to_thread() - RIGGING_GENERATOR_CREDENTIALS=${RIGGING_GENERATOR_CREDENTIALS:-} - RIGGING_GENERATOR_SUMMARY=${RIGGING_GENERATOR_SUMMARY:-} - RIGGING_GENERATOR_TRIAGE=${RIGGING_GENERATOR_TRIAGE:-} - - UVICORN_WORKERS=${UVICORN_WORKERS:-2} + - UVICORN_WORKERS=1 # Reduce to 1 worker to avoid cross-process contention + - WORKFLOW_RUNTIME_LOG_LEVEL=${WORKFLOW_RUNTIME_LOG_LEVEL:-WARNING} + - WORKFLOW_CLIENT_LOG_LEVEL=${WORKFLOW_CLIENT_LOG_LEVEL:-WARNING} logging: *logging-config depends_on: postgres: { condition: service_started } placement: { condition: service_started } rabbitmq: { condition: service_healthy } file-enrichment-dapr: - image: "daprio/daprd:1.15.5" + image: "daprio/daprd:1.16.1" command: [ "./daprd", "--max-body-size", - "300Mi", + "1Gi", "--app-id", "file-enrichment", "--app-port", @@ -243,6 +271,9 @@ services: "/dapr/components", "--config", "/dapr/configuration/config.yaml", + "--enable-metrics", + "--dapr-graceful-shutdown-seconds", + "5", ] volumes: - ./infra/dapr/components/pubsub.yaml:/dapr/components/pubsub.yaml:ro @@ -250,7 +281,12 @@ services: - ./infra/dapr/components/workflowstate.yaml:/dapr/components/workflowstate.yaml:ro - ./infra/dapr/configuration/config_monitoring_${NEMESIS_MONITORING:-disabled}.yaml:/dapr/configuration/config.yaml:ro environment: - - POSTGRES_CONNECTION_STRING=host=postgres user=${POSTGRES_USER} password=${POSTGRES_PASSWORD} dbname=enrichment port=5432 sslmode=disable + - POSTGRES_USER=${POSTGRES_USER:?} + - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:?} + - POSTGRES_HOST=${POSTGRES_HOST:-postgres} + - POSTGRES_PORT=${POSTGRES_PORT:-5432} + - POSTGRES_DB=${POSTGRES_DB:-enrichment} + - POSTGRES_PARAMETERS=${POSTGRES_PARAMETERS:-sslmode=disable} - RABBITMQ_CONNECTION_STRING=amqp://${RABBITMQ_USER}:${RABBITMQ_PASSWORD}@rabbitmq:5672 - MINIO_ROOT_USER=${MINIO_ROOT_USER:?} - MINIO_ROOT_PASSWORD=${MINIO_ROOT_PASSWORD:?} @@ -296,7 +332,7 @@ services: volumes: - "./projects/jupyter/notebooks:/home/jovyan/work" healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:8888/jupyter/"] + test: ["CMD", "curl", "-f", "http://localhost:8888/jupyter/api"] interval: 30s timeout: 10s retries: 3 @@ -328,12 +364,12 @@ services: retries: 5 start_period: 20s alerting-dapr: - image: "daprio/daprd:1.15.5" + image: "daprio/daprd:1.16.1" command: [ "./daprd", "--max-body-size", - "300Mi", + "1Gi", "--app-id", "alerting", "--app-port", @@ -350,6 +386,9 @@ services: "/dapr/components", "--config", "/dapr/configuration/config.yaml", + "--enable-metrics", + "--dapr-graceful-shutdown-seconds", + "5", ] volumes: - ./infra/dapr/components/pubsub.yaml:/dapr/components/pubsub.yaml:ro @@ -365,13 +404,28 @@ services: rabbitmq: { condition: service_healthy } network_mode: "service:alerting" - triage: - image: ghcr.io/specterops/nemesis/triage:latest + agents: + image: ghcr.io/specterops/nemesis/agents:latest environment: - DAPR_HTTP_PORT=3509 - DAPR_GRPC_PORT=50009 - NEMESIS_URL=${NEMESIS_URL:?} - - RIGGING_GENERATOR_TRIAGE=${RIGGING_GENERATOR_TRIAGE:-} + - WORKFLOW_RUNTIME_LOG_LEVEL=${WORKFLOW_RUNTIME_LOG_LEVEL:-WARNING} + - WORKFLOW_CLIENT_LOG_LEVEL=${WORKFLOW_CLIENT_LOG_LEVEL:-WARNING} + - LITELLM_ADMIN_KEY=sk-${LITELLM_ADMIN_PASSWORD:-admin123} + - MAX_BUDGET=${LLM_MAX_BUDGET:-100} + - BUDGET_DURATION=${LLM_BUDGET_DURATION:-30d} + - DOTNET_ANALYSIS_RUN_REQUEST_LIMIT=${DOTNET_ANALYSIS_RUN_REQUEST_LIMIT:-25} # max LLM calls to use per .NET program analysis run + - DOTNET_ANALYSIS_RUN_TOKENS_LIMIT=${DOTNET_ANALYSIS_RUN_TOKENS_LIMIT:-1000000} # max tokens to use per .NET program analysis run + # if we hit this number of the same triage values for the same file, all future findings get that value + - TRIAGE_CONSENSUS_THRESHOLD=${TRIAGE_CONSENSUS_THRESHOLD:-3} + # Phoenix LLM tracing configuration + - PHOENIX_ENABLED=${PHOENIX_ENABLED:-false} + - PHOENIX_ENDPOINT=${PHOENIX_ENDPOINT:-http://phoenix:6006/v1/traces} + - PHOENIX_SQL_DATABASE_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST:-postgres}:${POSTGRES_PORT:-5432}/phoenix + - NEMESIS_MONITORING=${NEMESIS_MONITORING:-disabled} + - OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://otel-collector:4317 + - OTEL_EXPORTER_OTLP_TRACES_ENDPOINT_INSECURE=true logging: *logging-config depends_on: postgres: { condition: service_healthy } @@ -379,15 +433,15 @@ services: rabbitmq: { condition: service_healthy } hasura: { condition: service_healthy } healthcheck: *healthcheck-python-svc - triage-dapr: - image: "daprio/daprd:1.15.5" + agents-dapr: + image: "daprio/daprd:1.16.1" command: [ "./daprd", "--max-body-size", - "300Mi", + "1Gi", "--app-id", - "triage", + "agents", "--app-port", "8000", "--dapr-http-port", @@ -402,19 +456,31 @@ services: "/dapr/components", "--config", "/dapr/configuration/config.yaml", + "--enable-metrics", + "--dapr-graceful-shutdown-seconds", + "5", ] volumes: - ./infra/dapr/components/secretstore.yaml:/dapr/components/secretstore.yaml:ro + - ./infra/dapr/components/workflowstate.yaml:/dapr/components/workflowstate.yaml:ro - ./infra/dapr/configuration/config_monitoring_${NEMESIS_MONITORING:-disabled}.yaml:/dapr/configuration/config.yaml:ro environment: + - POSTGRES_USER=${POSTGRES_USER:?} + - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:?} + - POSTGRES_HOST=${POSTGRES_HOST:-postgres} + - POSTGRES_PORT=${POSTGRES_PORT:-5432} + - POSTGRES_DB=${POSTGRES_DB:-enrichment} + - POSTGRES_PARAMETERS=${POSTGRES_PARAMETERS:-sslmode=disable} - HASURA_ADMIN_SECRET=${HASURA_ADMIN_SECRET:-pass456} - RABBITMQ_CONNECTION_STRING=amqp://${RABBITMQ_USER}:${RABBITMQ_PASSWORD}@rabbitmq:5672 + - MINIO_ROOT_USER=${MINIO_ROOT_USER:?} + - MINIO_ROOT_PASSWORD=${MINIO_ROOT_PASSWORD:?} depends_on: - triage: { condition: service_started } + agents: { condition: service_started } placement: { condition: service_started } scheduler: { condition: service_started } rabbitmq: { condition: service_healthy } - network_mode: "service:triage" + network_mode: "service:agents" housekeeping: image: ghcr.io/specterops/nemesis/housekeeping:latest @@ -430,12 +496,12 @@ services: rabbitmq: { condition: service_healthy } healthcheck: *healthcheck-python-svc housekeeping-dapr: - image: "daprio/daprd:1.15.5" + image: "daprio/daprd:1.16.1" command: [ "./daprd", "--max-body-size", - "300Mi", + "1Gi", "--app-id", "housekeeping", "--app-port", @@ -452,6 +518,9 @@ services: "/dapr/components", "--config", "/dapr/configuration/config.yaml", + "--enable-metrics", + "--dapr-graceful-shutdown-seconds", + "5", ] volumes: - ./infra/dapr/components/secretstore.yaml:/dapr/components/secretstore.yaml:ro @@ -459,7 +528,12 @@ services: environment: - MINIO_ROOT_PASSWORD=${MINIO_ROOT_PASSWORD:?} - MINIO_ROOT_USER=${MINIO_ROOT_USER:?} - - POSTGRES_CONNECTION_STRING=host=postgres user=${POSTGRES_USER} password=${POSTGRES_PASSWORD} dbname=enrichment port=5432 sslmode=disable + - POSTGRES_USER=${POSTGRES_USER:?} + - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:?} + - POSTGRES_HOST=${POSTGRES_HOST:-postgres} + - POSTGRES_PORT=${POSTGRES_PORT:-5432} + - POSTGRES_DB=${POSTGRES_DB:-enrichment} + - POSTGRES_PARAMETERS=${POSTGRES_PARAMETERS:-sslmode=disable} depends_on: housekeeping: { condition: service_started } placement: { condition: service_started } @@ -476,6 +550,14 @@ services: - DAPR_GRPC_PORT=50002 - DAPR_HTTP_PORT=3501 - TIKA_CONFIG=/tika-config.xml + # If you want to have additional language packs supported (see https://github.com/tesseract-ocr/tessdata for a full list): + # $ export TIKA_OCR_LANGUAGES="eng chi_sim chi_tra jpn rus deu spa" + # To change the default: + # - TIKA_OCR_LANGUAGES=${TIKA_OCR_LANGUAGES:-eng chi_sim chi_tra jpn rus deu spa} + # Note: each package installed will increase the image size! + - TIKA_OCR_LANGUAGES=${TIKA_OCR_LANGUAGES:-eng} + - MAX_PARALLEL_WORKFLOWS=${MAX_PARALLEL_WORKFLOWS:-5} + - MAX_WORKFLOW_EXECUTION_TIME=${MAX_WORKFLOW_EXECUTION_TIME:-300} healthcheck: test: ["CMD", "wget", "--spider", "-q", "http://localhost:8000/healthz"] interval: 10s @@ -487,12 +569,12 @@ services: placement: { condition: service_started } rabbitmq: { condition: service_healthy } document-conversion-dapr: - image: "daprio/daprd:1.15.5" + image: "daprio/daprd:1.16.1" command: [ "./daprd", "--max-body-size", - "300Mi", + "1Gi", "--app-id", "document-conversion", "--app-port", @@ -509,6 +591,9 @@ services: "/dapr/components", "--config", "/dapr/configuration/config.yaml", + "--enable-metrics", + "--dapr-graceful-shutdown-seconds", + "5", ] volumes: - ./infra/dapr/components/pubsub.yaml:/dapr/components/pubsub.yaml:ro @@ -518,7 +603,12 @@ services: environment: - MINIO_ROOT_PASSWORD=${MINIO_ROOT_PASSWORD:?} - MINIO_ROOT_USER=${MINIO_ROOT_USER:?} - - POSTGRES_CONNECTION_STRING=host=postgres user=${POSTGRES_USER} password=${POSTGRES_PASSWORD} dbname=enrichment port=5432 sslmode=disable + - POSTGRES_USER=${POSTGRES_USER:?} + - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:?} + - POSTGRES_HOST=${POSTGRES_HOST:-postgres} + - POSTGRES_PORT=${POSTGRES_PORT:-5432} + - POSTGRES_DB=${POSTGRES_DB:-enrichment} + - POSTGRES_PARAMETERS=${POSTGRES_PARAMETERS:-sslmode=disable} - RABBITMQ_CONNECTION_STRING=amqp://${RABBITMQ_USER}:${RABBITMQ_PASSWORD}@rabbitmq:5672 depends_on: document-conversion: { condition: service_started } @@ -535,6 +625,7 @@ services: "--api-timeout=180s", "--libreoffice-restart-after=5", "--libreoffice-auto-start=true", + "--prometheus-collect-interval=10s", ] environment: { DISABLE_GOOGLE_CHROME: "1" } healthcheck: @@ -543,12 +634,12 @@ services: timeout: 10s retries: 3 gotenberg-dapr: - image: "daprio/daprd:1.15.5" + image: "daprio/daprd:1.16.1" command: [ "./daprd", "--max-body-size", - "300Mi", + "1Gi", "--app-id", "gotenberg", "--app-port", @@ -565,6 +656,9 @@ services: "/dapr/components", "--config", "/dapr/configuration/config.yaml", + "--enable-metrics", + "--dapr-graceful-shutdown-seconds", + "5", ] volumes: - ./infra/dapr/configuration/config_monitoring_${NEMESIS_MONITORING:-disabled}.yaml:/dapr/configuration/config.yaml:ro @@ -605,18 +699,21 @@ services: [ "/bin/sh", "-c", - "sleep 1; until mc alias set minio http://minio:9000 $${MINIO_ROOT_USER} $${MINIO_ROOT_PASSWORD}; do echo 'Minio is not online. Waiting for it to start...'; sleep 2; done && mc mb minio/loki-data --ignore-existing", + "sleep 1; until mc alias set minio http://minio:9000 $${MINIO_ROOT_USER} $${MINIO_ROOT_PASSWORD}; do echo 'Minio is not online. Waiting for it to start...'; sleep 2; done && mc mb minio/loki-data --ignore-existing && mc mb minio/files --ignore-existing", ] restart: "no" rabbitmq: - image: rabbitmq:4.1.1-management + image: rabbitmq:4.1.2-management + hostname: rabbitmq-node # have to do this for persistence reasons environment: - RABBITMQ_DEFAULT_PASS=${RABBITMQ_PASSWORD:?} - RABBITMQ_DEFAULT_USER=${RABBITMQ_USER:?} - RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS=-rabbitmq_management path_prefix "/rabbitmq" + - RABBITMQ_NODENAME=rabbit@rabbitmq-node # have to do this for persistence reasons volumes: - - rabbitmq_data:/var/lib/rabbitmq + - rabbitmq_data:/var/lib/rabbitmq + - ./infra/rabbitmq/enabled_plugins:/etc/rabbitmq/enabled_plugins:ro healthcheck: test: ["CMD", "rabbitmq-diagnostics", "check_port_connectivity"] interval: 10s @@ -630,7 +727,12 @@ services: - "traefik.http.routers.rabbitmq-ui.rule=PathPrefix(`/rabbitmq`)" postgres: - image: postgres:17.5-alpine + image: postgres:17.6-alpine + command: [ + "postgres", + "-c", "max_connections=200", + "-c", "shared_buffers=256MB" + ] environment: POSTGRES_DB: ${POSTGRES_DB:-enrichment} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?} @@ -644,20 +746,30 @@ services: timeout: 5s retries: 5 + postgres-exporter: + profiles: ["monitoring"] + image: prometheuscommunity/postgres-exporter:latest + environment: + DATA_SOURCE_NAME: "postgresql://${POSTGRES_USER:?}:${POSTGRES_PASSWORD:?}@${POSTGRES_HOST:-postgres}:${POSTGRES_PORT:-5432}/${POSTGRES_DB:-enrichment}?${POSTGRES_PARAMETERS:-sslmode=disable}" + volumes: + - ./infra/postgres-exporter/postgres_exporter.yml:/postgres_exporter.yml:ro + depends_on: + postgres: { condition: service_healthy } + hasura: - image: hasura/graphql-engine:v2.45.1.cli-migrations-v2 + image: hasura/graphql-engine:v2.48.6.cli-migrations-v2 depends_on: postgres: { condition: service_healthy } environment: HASURA_GRAPHQL_ADMIN_SECRET: "${HASURA_ADMIN_SECRET:-pass456}" HASURA_GRAPHQL_BASE_PATH: "/hasura" - HASURA_GRAPHQL_DATABASE_URL: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/enrichment + HASURA_GRAPHQL_DATABASE_URL: postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST:-postgres}:${POSTGRES_PORT:-5432}/${POSTGRES_DB:-enrichment} HASURA_GRAPHQL_ENABLE_CONSOLE: "true" HASURA_GRAPHQL_ENABLE_METADATA_SYNC: "true" HASURA_GRAPHQL_ENABLE_TELEMETRY: "false" HASURA_GRAPHQL_ENABLED_LOG_TYPES: startup, http-log, webhook-log, websocket-log, query-log HASURA_GRAPHQL_LOG_LEVEL: "warn" - HASURA_GRAPHQL_METADATA_DATABASE_URL: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/enrichment + HASURA_GRAPHQL_METADATA_DATABASE_URL: postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST:-postgres}:${POSTGRES_PORT:-5432}/${POSTGRES_DB:-enrichment} HASURA_GRAPHQL_METADATA_DIR: /hasura-metadata HASURA_GRAPHQL_UNAUTHORIZED_ROLE: anonymous volumes: @@ -672,7 +784,7 @@ services: - "traefik.http.routers.hasura.tls=true" traefik: - image: traefik:v3.3.2 + image: traefik:v3.4.4 command: - "--api.insecure=true" # - "--log.level=DEBUG" @@ -700,18 +812,20 @@ services: - ./auth:/auth:ro - ./infra/traefik/certs:/certs:ro - ./infra/traefik/config:/config:ro + placement: - image: "daprio/dapr:1.15.5" - command: ["./placement", "-port", "50006"] + image: "daprio/dapr:1.16.1" + command: ["./placement", "-port", "50006", "--enable-metrics"] scheduler: - image: "daprio/dapr:1.15.5" + image: "daprio/dapr:1.16.1" command: [ "./scheduler", "--port", "50007", "--etcd-data-dir=/var/lock/dapr/scheduler", + "--enable-metrics", ] ############################ @@ -750,6 +864,35 @@ services: retries: 3 start_period: 30s + phoenix: + profiles: ["monitoring"] + image: arizephoenix/phoenix:11.24.1 + environment: + - PHOENIX_WORKING_DIR=/data + - PHOENIX_HOST_ROOT_PATH=/phoenix + - PHOENIX_SQL_DATABASE_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST:-postgres}:${POSTGRES_PORT:-5432}/phoenix + - PHOENIX_DEFAULT_RETENTION_POLICY_DAYS=100 + volumes: + - phoenix_data:/data + # ports: + # - "6006:6006" # Phoenix UI port, for debugging + labels: + - "traefik.enable=true" + - "traefik.http.services.phoenix.loadbalancer.server.port=6006" + - "traefik.http.routers.phoenix.rule=PathPrefix(`/phoenix`)" + - "traefik.http.routers.phoenix.entrypoints=websecure" + - "traefik.http.routers.phoenix.tls=true" + - "traefik.http.routers.phoenix.middlewares=auth,phoenix-stripprefix" + - "traefik.http.middlewares.phoenix-stripprefix.stripprefix.prefixes=/phoenix" + depends_on: + postgres: { condition: service_healthy } + healthcheck: + test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:6006/healthz').read()"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 30s + loki: profiles: ["monitoring"] image: grafana/loki:3.3.2 @@ -793,6 +936,7 @@ services: - GF_SERVER_DOMAIN=${EXTERNAL_HOST:-https://localhost} - GF_SERVER_ROOT_URL=${EXTERNAL_HOST:-https://localhost}/grafana - GF_SERVER_SERVE_FROM_SUB_PATH=true + - POSTGRES_CONNECTION_STRING=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST:-postgres}:${POSTGRES_PORT:-5432}/${POSTGRES_DB:-enrichment}?${POSTGRES_PARAMETERS:-sslmode=disable} volumes: - ./infra/grafana/provisioning:/etc/grafana/provisioning:ro - grafana_data:/var/lib/grafana @@ -867,3 +1011,42 @@ services: - /var/lib/docker/:/var/lib/docker:ro - /var/run:/var/run:ro devices: ["/dev/kmsg:/dev/kmsg"] + + + ############################ + # LLM Services (optional profile) + ############################ + + litellm: + profiles: ["llm"] + image: ghcr.io/berriai/litellm:v1.74.0-stable + environment: + - DATABASE_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST:-postgres}:${POSTGRES_PORT:-5432}/litellm + - STORE_MODEL_IN_DB=True + - AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID:-} + - AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY:-} + - AWS_REGION_NAME=${AWS_REGION_NAME:-us-east-1} + - LITELLM_MASTER_KEY=sk-${LITELLM_ADMIN_PASSWORD:-admin123} + - LITELLM_SALT_KEY=sk-${LITELLM_ADMIN_PASSWORD:-admin123} + - SERVER_ROOT_PATH=/llm + - PROXY_BASE_URL=${NEMESIS_URL:?} + - UI_BASE_PATH=/llm/ui + volumes: + - ./infra/litellm/config.yml:/app/config.yml:ro + command: ["--config", "/app/config.yml"] + depends_on: + postgres: { condition: service_healthy } + healthcheck: + test: ["CMD", "wget", "--quiet", "--tries=1", "-O", "/dev/null", "--header=Authorization: Bearer sk-${LITELLM_ADMIN_PASSWORD:-admin123}", "http://localhost:4000/health"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 30s + disable: true + logging: *logging-config + labels: + - "traefik.enable=true" + - "traefik.http.routers.litellm.rule=PathPrefix(`/llm`)" + - "traefik.http.services.litellm.loadbalancer.server.port=4000" + - "traefik.http.routers.litellm.entrypoints=websecure" + - "traefik.http.routers.litellm.tls=true" diff --git a/docs/agents.md b/docs/agents.md new file mode 100644 index 0000000..96ffd3a --- /dev/null +++ b/docs/agents.md @@ -0,0 +1,95 @@ +# Nemesis Agents + +Nemesis integrates a number of LLM agents as well as some rule-based agents for different actions. These agents are run from the `agents` container and utilize Dapr workflows as well as [Pydantic AI](https://ai.pydantic.dev/) for the core agent execution. LLM powered agents are not enabled by default (see Setup and Configuration below for details). + +## Overview + +Nemesis currently has the following agents: + +| Agent Name | Type | Runs Automatically | Purpose | +| --------------------- | ---------- | ------------------ | -------------------------------------------------------------------------------------------------- | +| `validate` | LLM-based | true | Validates security findings by triaging them as true positives, false positives, or needing review | +| `credential_analyzer` | LLM-based | false | Extracts credentials and passwords from text content using LLM analysis | +| `dotnet_analyzer` | LLM-based | false | Adapted .NET vulnerability analyzer from @Dreadnode. | +| `summarizer` | LLM-based | false | Creates concise summaries of text content using LLM analysis | +| `jwt` | Rule-based | true | Rule-based JWT analysis that checks expiry status and identifies sample data | + +## Setup + +In order to enable any LLM-powered agents, you first need to configure one or more models in `./infra/litellm/config.yaml` . You can use any model provider that LiteLLM supports (more details [here](https://docs.litellm.ai/docs/proxy/configs)) - **ensure that at a minimum you have a model called "default"**. In the `agents` section of `./compose.yaml` you can adjust the MAX_BUDGET (default $100) and BUDGET_DURATION (default 30d) enrivonment variables as wanted. Then launch the "llm" profile for Nemesis by supplying `--llm` to the `./tools/nemesis-ctl.sh` script. + +***Note:*** service keys/tokens/etc. should be configured in your .env file and can be used in `./infra/litellm/config.yaml` as demonstrated in the current config file. + +## Agent Details + +### Finding Validator + +The `validate` agent uses the configured LLM to triage findings as true_positive, false_positive, or needs_review. This is based off of the finding details and the file path/name. It also will provide a 1-sentence reason for the decision along with a confidence score. Automated triage values are displayed in the main file-listing interface with a small robot icon for agent-powered triage. Mousing over the robot icon will give the explanation and confidence score: + +![Nemesis Agents Finding Triage](images/agents_finding_triage.png) + +If the finding is true_positive, a 1-sentence risk statement is also generated. Clicking the finding will give more details along with the risk statement if appliable + +![True Positive Finding Details](images/agents_true_positive_details.png) + +This agent will run/triage all findings that come in except for the following categories: "extracted_hash", "yara_match", "extracted_data". + +### Credential Analyzer + +The `credential_analyzer` agent is an LLM-powered agent that will examine a text file for any credentials that might be present. It does **not** run automatically, but is triggered manually from a file details interface: + +![Agent Credential Extraction](images/agent_credential_extraction.png) + +Once processing is complete, a markdown file will appear with any results in the transforms tab: + +![Agent Credential Extraction Results](images/agent_credential_extraction_results.png) + +### .NET Analyzer + +The `dotnet_analyzer` agent is an LLM-powered agent directly adapted from [@Dreadnode](https://x.com/dreadnode)'s [example-agents](https://github.com/dreadnode/example-agents) repo. It will analyze a .NET binary using a number of callable tools, searching for security issues. + +It does **not** run automatically, but is triggered manually from a file details interface. A confirmation dialog will confirm running the agent, as it may take a bit of time and tokens. + +![.NET Analysis](images/agent_dotnet_analysis.png) + +Once processing is complete, a markdown file will appear with any results in the transforms tab under ".NET Assembly Analysis": + +![.NET Analysis Results](images/agent_dotnet_analysis_results.png) + +### Text Summarizer + +The text `summarizer` agent is an LLM-powered agent that will summarize. Like the credential analizer, it does **not** run automatically, but is triggered manually from a file details interface. After processing, it will display the markdown-formatted text summary: + +![Agent Text Summarizer](images/agent_text_summarizer.png) + +### JWT Validator + +The JWT validator is a rule-based agent that will extract JWT tokens from Nosey Parker findings and mark the finding as "false_positive" if the token has expired. + +## The Nemesis Web Interface + +### Agents Web Interface + +If the llm profile is used, the Nemesis frontend will detect that LiteLLM was deployed and enable a new "Agents" tab on the left. Clicking on this tab will show you the currently enabled agents, as well as the current token and cost spend for the system: + +![Agents Web Interface](images/nemesis_dashboard_agents.png) + +For LLM-powered agents, you can modify the main system prompt used by clicking "Edit", making your changes to the prompt, and clicking "Save": + +![Agents Web Interface Prompts](images/nemesis_dashboard_agents_prompt.png) + +### Monitoring + +If the `--monitoring` flag is also passed to the `./tools/nemesis-ctl.sh` script, the [Arize Phoenix](https://github.com/Arize-ai/phoenix) will be deployed to allow tracking of the inputs/outputs sent to the LLM (at the /phoenix route): + +![Arize Phoenix Tracing](images/arize_phoenix_tracing.png) + +If the monitoring profile is used and Arize Phoenix is deployed, the Nemesis frontend will dynamically display links to the Phoenix interface in the Help menu as well. + +![Nemesis Dynamic Help Menu](images/nemesis_dynamic_help_menu.png) + +### LiteLLM Interface + +If the lm profile is used and LiteLLM is deployed, the Nemesis frontend will dynamically display links to the LiteLLM in the Help menu as well. Clicking on the link you can log in with `admin` and the value of the LITELLM_MASTER_KEY (sk-admin123 by default). This interface can give you additional breakdowns for tokens and costs: + +![LiteLLM Main Interface](images/litellm_main_interface.png) diff --git a/docs/api.md b/docs/api.md new file mode 100644 index 0000000..81c71d2 --- /dev/null +++ b/docs/api.md @@ -0,0 +1,329 @@ +# Enrichment API + +**Version:** 0.1.0 + +API for file enrichment services + +This documentation is automatically generated from the OpenAPI specification. + +--- + +## Enrichments + +### `GET /enrichments` + +List enrichment modules + +Get a list of all available enrichment modules + +**Returns:** 200 on success + +--- + +### `POST /enrichments/{enrichment_name}` + +Run enrichment module + +Run a specific enrichment module on a file + +**Parameters:** + +- `enrichment_name` (string, **required**): Name of the enrichment module to run + +**Request Body:** `EnrichmentRequest` (JSON) + +**Returns:** 200 on success + +--- + +### `POST /enrichments/{enrichment_name}/bulk` + +Start bulk enrichment + +Start bulk enrichment for a specific module against all files in the system using distributed processing + +**Parameters:** + +- `enrichment_name` (string, **required**): Name of the enrichment module to run + +**Returns:** 200 on success + +--- + +### `GET /enrichments/{enrichment_name}/bulk/status` + +Get bulk enrichment status + +Bulk enrichment status tracking has been simplified + +**Parameters:** + +- `enrichment_name` (string, **required**): Name of the enrichment module to check status for + +**Returns:** 200 on success + +--- + +### `POST /enrichments/{enrichment_name}/bulk/stop` + +Stop bulk enrichment + +Bulk enrichment cannot be stopped once tasks are published + +**Parameters:** + +- `enrichment_name` (string, **required**): Name of the enrichment module to stop + +**Returns:** 200 on success + +--- + + +## Files + +### `POST /containers` + +Submit large container file for processing with optional filtering + +... + +**Request Body:** See OpenAPI spec for details + +**Returns:** 200 on success + +--- + +### `GET /containers/{container_id}/status` + +Get container processing status + +Get the current processing status and progress of a submitted container + +**Parameters:** + +- `container_id` (string, **required**): Unique identifier of the container + +**Returns:** 200 on success + +--- + +### `POST /files` + +Upload file with metadata + +Upload a file using multipart/form-data with metadata. + Returns an object_id for the uploaded file and submission_id for the metadata submission. + + Example: + ``` + curl -k -u n:n -F "file=@example.txt" -F 'metadata={"agent_id":"agent123","project":"proj1","timestamp":"2024-01-29T12:00:00Z","expiration":"2024-02-29T12:00:00Z","path":"/tmp/example.txt"}' https://nemesis:7443/api/files + ``` + + Example: + ``` + curl -k -u n:n -F "file=@example.txt" -F 'metadata={"agent_id":"agent123","project":"proj1","path":"/tmp/example.txt"}' https://nemesis:7443/api/files + ``` + +**Request Body:** See OpenAPI spec for details + +**Returns:** 200 on success + +--- + +### `GET /files/{object_id}` + +Download a file + +Download a file by its object ID with optional raw format and custom filename + +**Parameters:** + +- `object_id` (string, **required**): Unique identifier of the file to download +- `raw` (boolean, optional): Whether to return the file in raw format +- `name` (string, optional): Custom filename for the downloaded file + +**Returns:** 200 on success + +--- + + +## Queues + +### `GET /queues` + +Get queue statistics + +Get comprehensive queue metrics for all workflow topics + +**Returns:** 200 on success + +--- + +### `GET /queues/{queue_name}` + +Get single queue statistics + +Get metrics for a specific queue topic + +**Parameters:** + +- `queue_name` (string, **required**): Name of the queue to get metrics for + +**Returns:** 200 on success + +--- + + +## System + +### `GET /agents` + +Get available agents + +Get a list of available AI agents with their metadata and capabilities + +**Returns:** 200 on success + +--- + +### `POST /agents/dotnet_analysis` + +Run .NET assembly analysis + +Forward .NET assembly analysis request to agents service + +**Request Body:** JSON object + +**Returns:** 200 on success + +--- + +### `POST /agents/llm_credential_analysis` + +Run credential analysis + +Forward credential analysis request to agents service + +**Request Body:** JSON object + +**Returns:** 200 on success + +--- + +### `GET /agents/spend-data` + +Get LLM spend and usage data + +Get total spend and token usage statistics from LiteLLM logs + +**Returns:** 200 on success + +--- + +### `POST /agents/text_summarizer` + +Run text summarization + +Forward text summarization request to agents service + +**Request Body:** JSON object + +**Returns:** 200 on success + +--- + +### `GET /system/apprise-info` + +Get Apprise alert information + +Get information about configured alert channels (currently Slack only) + +**Returns:** 200 on success + +--- + +### `GET /system/available-services` + +Get available services + +Query Traefik to determine which optional services are currently available + +**Returns:** 200 on success + +--- + +### `POST /system/cleanup` + +Trigger database and datalake cleanup + +Trigger the housekeeping service to clean up expired files and database entries, and reset the workflow manager state. Optionally specify an expiration date or 'all' to remove all files. + +**Request Body:** `CleanupRequest` (JSON) + +**Returns:** 200 on success + +--- + +### `GET /system/container-monitor/status` + +Container monitor status + +Get the status of the container file monitor + +**Returns:** 200 on success + +--- + +### `GET /system/health` + +Health check + +Health check endpoint for Docker healthcheck + +**Returns:** 200 on success + +--- + +### `GET /system/info` + +API information + +Root endpoint that shows API information + +**Returns:** 200 on success + +--- + +### `POST /system/yara/reload` + +Reload Yara rules + +Trigger a reload of all Yara rules in the backend across all workers/replicas + +**Returns:** 200 on success + +--- + + +## Workflows + +### `GET /workflows/failed` + +Get failed workflows + +Get the set of failed enrichment workflows + +**Returns:** 200 on success + +--- + +### `GET /workflows/status` + +Get workflow enrichment workflow status + +Get the current status of the enrichment workflow system + +**Returns:** 200 on success + +--- + diff --git a/docs/chromium.md b/docs/chromium.md new file mode 100644 index 0000000..4eea9b1 --- /dev/null +++ b/docs/chromium.md @@ -0,0 +1,119 @@ +# Nemesis Chromium Analysis + +Nemesis includes comprehensive support for analyzing Chromium-based browser data including Chrome, Edge, Brave, and other Chromium-based browsers. The system automatically detects and processes various Chromium database files to extract browsing history, saved credentials, cookies, downloads, and encryption state information. + +## Overview + +Nemesis currently analyzes the following Chromium data sources: + +| Data Type | File Source | Purpose | +| ---------- | ------------- | ------------------------------------------------------------------------------ | +| History | `History` | Extracts browsing history including URLs, titles, visit counts, and timestamps | +| Downloads | `History` | Extracts download history with file paths, URLs, and download metadata | +| Logins | `Login Data` | Extracts saved login credentials including usernames and passwords | +| Cookies | `Cookies` | Extracts browser cookies with domain, name, value, and expiration data | +| State Keys | `Local State` | Extracts OS encryption keys used to decrypt passwords and sensitive data | + + +## Chrome Web Tab + +### Chromium Data Viewer + +The Nemesis frontend provides a dedicated Chromium interface accessible through the main navigation. This interface organizes all extracted Chromium data into five main categories: + +### History Tab + +The History tab displays extracted browsing history with searchable and filterable tables. Each entry includes: + +- **Object ID**: The object_id from the originating file +- **Source**: the "source" label for the originating file (host, url, etc.) +- **Username**: Host-based username, extracted from file path context (i.e., `C:\Users\USER\*`) +- **Browser**: Browser name, extracted from file path context +- **Title**: Page title as recorded by the browser +- **Visits**: Number of times the URL was visited +- **Last Visit**: Timestamp of most recent visit +- **URL**: The visited website URL + +![Chromium History Tab](images/chromium-history-tab.png) + +### Downloads Tab + +The Downloads tab shows extracted download history with detailed information about each downloaded file: + +- **Object ID**: The object_id from the originating file +- **Source**: the "source" label for the originating file (host, url, etc.) +- **Username**: Host-based username, extracted from file path context (i.e., `C:\Users\USER\*`) +- **Browser**: Browser name, extracted from file path context +- **URL**: Original source URL of the downloaded file +- **End Time**: Time the download ended +- **Download Path**: Path the file was downloaded to on the host + +![Chromium Downloads Tab](images/chromium-downloads-tab.png) + +### Logins Tab + +The Logins tab displays extracted login credentials: + +- **Object ID**: The object_id from the originating file +- **Decrypted**: Yes/No indication if the login entry has been decrypted (by a state key) or not +- **Source**: the "source" label for the originating file (host, url, etc.) +- **Username**: Host-based username, extracted from file path context (i.e., `C:\Users\USER\*`) +- **Password**: Plaintext value of the password (if decrypted) +- **Browser**: Browser name, extracted from file path context +- **Login Name**: Extracted login name for the login data entry (NOT the host-based path username of the Login Data file) +- **Times Used**: Number of times the login entry has been used +- **Signon Realm**: Extracted signon realm for the login entry +- **Origin URL**: Website where credentials were saved + +![Chromium Logins Tab](images/chromium-logins-tab.png) + +### Cookies Tab + +The Cookies tab provides access to extracted browser cookies: + +- **Object ID**: The object_id from the originating file +- **Decrypted**: Yes/No indication if the coookie has been decrypted (by a state key) or not +- **Source**: the "source" label for the originating file (host, url, etc.) +- **Username**: Host-based username, extracted from file path context (i.e., `C:\Users\USER\*`) +- **Password**: Plaintext value of the password (if decrypted) +- **Browser**: Browser name, extracted from file path context +- **Host Key**: Domain or host the cookie belongs to +- **Expires UTC**: Time (in UTC) the cookie value expires +- **Last Access UTC**: Time (in UTC) the cookie value was last uased +- **Name**: Name identifier of the cookie + +**Note**: If the cookie value has been decrypted, click "Download CSV" to download the currently filtered/viewable +cookies on the page *including* decrypted values. + +![Chromium Cookies Tab](images/chromium-cookies-tab.png) + +### State Keys Tab + +The State Keys tab displays OS encryption keys used by Chromium to protect sensitive data: + +- **Object ID**: The object_id from the originating file +- **Source**: the "source" label for the originating file (host, url, etc.) +- **Username**: Host-based username, extracted from file path context (i.e., `C:\Users\USER\*`) +- **Browser**: Browser name, extracted from file path context +- **Key Decrypted**: If the pre-v127 Chromium local state encryption key has been decrypted +- **App Bound Key Decrypted**: If the post-v127 Chromium App-bound encryption key has been decrypted + +![Chromium State Keys Tab](images/chromium-state-keys-tab.png) + +## Data Export and Analysis + +### CSV Export Functionality + +All Chromium data tables support CSV export for external analysis: + +1. Use the table interface to filter and search desired records +2. Click the "Download CSV" button in the top-right of each tab +3. All currently visible/filtered records will be exported + +### Copy Operations + +Individual records or entire result sets can be copied to clipboard: + +- **Single Row**: Double-click any table row to copy all fields +- **Multiple Rows**: Select rows and use Ctrl+C (or Cmd+C on Mac) +- **Filtered Results**: Copy button will copy all currently visible records diff --git a/docs/cli.md b/docs/cli.md index e902625..006c708 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -72,6 +72,7 @@ The `./tools/submit.sh` script wraps the docker syntax automatically. --username your-username \ --password your-password \ --project my-project \ + --source host://HOST1 \ --agent-id my-agent \ --workers 5 \ --recursive \ @@ -103,6 +104,7 @@ Options: -u, --username TEXT Basic auth username [default: n] -p, --password TEXT Basic auth password [default: n] --project TEXT Project name for metadata [default: assess-test] + --source TEXT Source name for metadata (e.g., 'host://HOST1') --agent-id TEXT Agent ID for metadata [default: submitunknown_user@docker-desktop] -f, --file FILE Path to single file to submit (alternative to PATHS @@ -110,16 +112,17 @@ Options: --help Show this message and exit. ``` -| Option | Default | Description | -| ------------- | --------------------- | ------------------------- | -| `--host` | `0.0.0.0:7443` | Nemesis host and port | -| `--recursive` | `false` | Process subdirectories | -| `--workers` | `10` | Number of upload threads | -| `--username` | `n` | Basic auth username | -| `--password` | `n` | Basic auth password | -| `--project` | `assess-test` | Project name for metadata | -| `--agent-id` | `submit@` | Agent ID for metadata | -| `--debug` | `false` | Enable debug logging | +| Option | Default | Description | +| ------------- | --------------------- | ----------------------------------------------------------------------- | +| `--host` | `0.0.0.0:7443` | Nemesis host and port | +| `--recursive` | `false` | Process subdirectories | +| `--workers` | `10` | Number of upload threads | +| `--username` | `n` | Basic auth username | +| `--password` | `n` | Basic auth password | +| `--project` | `assess-test` | Project name for metadata | +| `--source` | | Source name (e.g., 'host://HOST1' or 'https://domain.com') for metadata | +| `--agent-id` | `submit@` | Agent ID for metadata | +| `--debug` | `false` | Enable debug logging | ## Folder Monitoring @@ -173,6 +176,7 @@ docker run \ --username your-username \ --password your-password \ --project my-project \ + --source host://HOST1 \ --agent-id my-agent \ --workers 5 \ --debug @@ -188,16 +192,17 @@ poetry run python -m cli monitor /path/to/directory ### Options Reference -| Option | Default | Description | -| ---------------- | ---------------------- | ----------------------------------------------- | -| `--host` | `0.0.0.0:7443` | Nemesis host and port | -| `--username` | `n` | Basic auth username | -| `--password` | `n` | Basic auth password | -| `--project` | `assess-test` | Project name for metadata | -| `--agent-id` | `monitor@` | Agent ID for metadata | -| `--workers` | `10` | Number of upload threads for initial submission | -| `--only-monitor` | `false` | Skip existing files, only monitor for new ones | -| `--debug` | `false` | Enable debug logging | +| Option | Default | Description | +| ---------------- | ---------------------- | ----------------------------------------------------------------------- | +| `--host` | `0.0.0.0:7443` | Nemesis host and port | +| `--username` | `n` | Basic auth username | +| `--password` | `n` | Basic auth password | +| `--project` | `assess-test` | Project name for metadata | +| `--source` | | Source name (e.g., 'host://HOST1' or 'https://domain.com') for metadata | +| `--agent-id` | `monitor@` | Agent ID for metadata | +| `--workers` | `10` | Number of upload threads for initial submission | +| `--only-monitor` | `false` | Skip existing files, only monitor for new ones | +| `--debug` | `false` | Enable debug logging | ## Mythic Connector diff --git a/docs/containers.md b/docs/containers.md new file mode 100644 index 0000000..f169a93 --- /dev/null +++ b/docs/containers.md @@ -0,0 +1,88 @@ +# Containers + +Nemesis has the ability to process the files extracted/carved from various container formats _without_ saving the container itself to the internal datalake. This is useful the the following situations: + +- When you want to process a large number of files without doubling the storage (storing .zip + extracted files) +- When you want to process large "containers" like forensic disk images + +## Submitting Containers + +### "Regular" Containers + +To process a regular container, you have two options. First, you can submit the .zip/etc. as normal, and then click "Extract/Process Container Contents" on the file viewer page: + +![Extract/Process Container Contents](images/extract_process_container_contents.png) + +Alternatively, you can configure and drop containers into the mounted folder as described in the **"Large" Containers** section below. + +You can also submit the container with the `nemesis-cli` (and `./tools/submit.sh` script) with something like (note the --container flag): + +```bash +% ./tools/submit.sh --project PROJECT-123 --source DEV --container zip_test.zip +Uploading (✓:1 ✗:0 | 1.59 KB): 100%|██████████| 1/1 [00:00<00:00] +INFO +Upload Summary: +INFO ──────────────────────────────────────── +INFO Total Files: 1 +INFO Successful: 1 +INFO Failed: 0 +INFO Success Rate: 100.0% +INFO Total Uploaded: 1.59 KB +``` + +### "Large" Containers + +For large things like disk images, a straight REST API doesn't cut it - lots of things mess up. Getting a multi-gigabyte file into Nemesis can be a challenge, but the current process uses a mountained container and large container monitoring abilities in the `web-api` service. + +In order to process really large containers, first create a folder on your host and set the MOUNTED_CONTAINER_PATH ENV variable to that path. This folder is mounted into the `web-api` and will process containers that appear there (after they're done copying in). Then just start Nemesis and copy containers into that folder, it's that easy! + +Wait, but what about metadata? + +#### Large Container Configs/Metadata + +Since we're changing the normal way we submit files, we need a new way to pass metadata into Nemesis for a disk image/large container. In order to accomplish this, we support a YAML-based metadata file format that can be placed in the parent folder, or any sub folders. This file can be named config.[yaml|yml] or settings.[yaml|yml] and takes the form: + +```yaml +metadata: + project: project123 + agent: collector + source: BLAH +file_filters: + pattern_type: regex + include: + - "^(?:[A-Za-z]://|/)?[Ww]indows/[Ss]ystem32/config/" + exclude: + - "^(?:[A-Za-z]://|/)?[Ww]indows/" +``` + +The metadata will be passed through for every file carved from the container. The optional `file_filters` allow you specify which extracted/carved files to include, or not include, for processing. + +The logic for inclusion varies based on which patterns are provided: + +1. No filters: Include everything +2. Only include patterns: Only include files matching include patterns (allowlist mode) +3. Only exclude patterns: Include everything except files matching exclude patterns (blocklist mode) +4. Both include and exclude: Include everything, apply excludes, then re-include matches from include patterns (exception mode) + +This creates a natural hierarchy where include patterns act as exceptions to exclusions when both are present. + +You can also create sub-directories in the submission folder, for example: + +``` +MOUNTED_CONTAINER_PATH / settings.yaml + / disk1.dd + / windows / + / settings.yaml + / windows_disk.dd +``` + +In this case, the `settings.yaml` closest to the hierarchy of the file takes precedence - the "windows_disk.dd" will use the settings.yaml in its current folder, but would use the MOUNTED_CONTAINER_PATH/settings.yaml file if one wasn't present lower down. This lets you create a nested structure with multiple config options depending on where you drop your disk image. + + +## Tracking Containers + +Whether a container is submitted via the cli or the mounted folder option, it will appear in the "Containers" page accessible from the left navigation page: + +![Containers Dashboard](images/containers_dashboard.png) + +This page will show the status of the container file extraction, and lets you filter by various fields. \ No newline at end of file diff --git a/docs/dapr.md b/docs/dapr.md index b40a40f..c15105e 100644 --- a/docs/dapr.md +++ b/docs/dapr.md @@ -20,13 +20,13 @@ The [document_conversion](https://github.com/SpecterOps/Nemesis/tree/main/projec ## Secrets -Nemesis uses the [Dapr Secrets management](https://docs.dapr.io/developing-applications/building-blocks/secrets/secrets-overview/) building block to protect secrets internally (like Postgres connection strings). Currently the [Local environment variables](https://docs.dapr.io/reference/components-reference/supported-secret-stores/envvar-secret-store/) component is used. These secrets are also refereced within some Dapr files such as [pubsub.yaml](https://github.com/SpecterOps/Nemesis/tree/main/infra/dapr/components/pubsub.yaml). +Nemesis uses the [Dapr Secrets management](https://docs.dapr.io/developing-applications/building-blocks/secrets/secrets-overview/) building block to protect secrets internally (like PostgreSQL connection parameters). Currently the [Local environment variables](https://docs.dapr.io/reference/components-reference/supported-secret-stores/envvar-secret-store/) component is used. These secrets are also referenced within some Dapr files such as [pubsub.yaml](https://github.com/SpecterOps/Nemesis/tree/main/infra/dapr/components/pubsub.yaml). This reason for using this abstraction is so alternative secret management systems like [Vault or Kubernetes secrets](https://docs.dapr.io/reference/components-reference/supported-secret-stores/) can be used in the future: ![Dapr Secrets](images/dapr-secrets-overview-cloud-stores.png) -An example of retrieving a secret is at the top of the the [housekeeping code](https://github.com/SpecterOps/Nemesis/blob/main/projects/housekeeping/housekeeping/main.py) to retrieve the `POSTGRES_CONNECTION_STRING` string. +An example of retrieving secrets is in [libs/common/common/db.py](https://github.com/SpecterOps/Nemesis/blob/main/libs/common/common/db.py) which retrieves individual PostgreSQL connection parameters (`POSTGRES_USER`, `POSTGRES_PASSWORD`, `POSTGRES_HOST`, `POSTGRES_PORT`, `POSTGRES_DB`, `POSTGRES_PARAMETERS`) and constructs the connection string. ## Service Invocation diff --git a/docs/docker_compose.md b/docs/docker_compose.md index a97c0e5..c8e39ea 100644 --- a/docs/docker_compose.md +++ b/docs/docker_compose.md @@ -73,7 +73,7 @@ Development images are not published and must be built locally. If you make any The easiest method to build + run dev images is to just use the `dev` target instead of `prod` with `./tools/nemesis-ctl.sh` : ```bash -./tools/nemesis-ctl.sh start dev [--monitoring] [--jupyter] +./tools/nemesis-ctl.sh start dev [--monitoring] [--jupyter] [--llm] ``` ### Step 1 - Configure environment variables diff --git a/docs/dpapi.md b/docs/dpapi.md new file mode 100644 index 0000000..4a64879 --- /dev/null +++ b/docs/dpapi.md @@ -0,0 +1,116 @@ +# Nemesis DPAPI Analysis + +Nemesis includes comprehensive support for analyzing Microsoft's Data Protection API (DPAPI) artifacts, which are used to encrypt sensitive data on Windows systems. + +The system provides functionality to view extracted DPAPI master keys, domain backup keys, and submit credential material to decrypt protected data. + +Additionally, "retroactive" decryption will occur when plaintext DPAPI masterkeys are submitted (or scraped from LSASS dumps), or DPAPI masterkeys are successfully decrypted (via domain backup keys, NTLM hashes, passwords, etc.). Specifically, new plaintext masterkeys are used to decrypt Google Chromekey1's and Chromium Local State files. + +## Overview + +Nemesis currently analyzes and manages the following DPAPI data sources: + +| Data Type | Purpose | +| ------------------------------ | ---------------------------------------------------------------------------------- | +| Master Keys | User-specific DPAPI master keys used to encrypt/decrypt user data | +| Domain Backup Keys | Domain controller backup keys for enterprise DPAPI key recovery | +| Credential Material Submission | Interface to submit various credential types to decrypt existing DPAPI master keys | + +## DPAPI Web Tab + +### DPAPI Data Viewer + +The Nemesis frontend provides a dedicated DPAPI interface accessible through the main navigation. This interface organizes all DPAPI-related functionality into three main categories: + +### Master Keys Tab + +The Master Keys tab displays extracted DPAPI master keys with searchable and filterable tables. Each entry includes: + +- **Object ID**: The object_id from the originating file +- **Source**: The "source" label for the originating file (host, url, etc.) +- **Username**: Host-based username, extracted from file path context (i.e., `C:\Users\USER\*`) +- **Key GUID**: Unique identifier for the master key +- **Key Status**: Decryption status of the master key +- **Created Date**: When the master key was created +- **Master Key Data**: The actual key material (if decrypted) + +![DPAPI masterkeys](images/dpapi_masterkeys.png) + +### Domain Backup Keys Tab + +The Domain Backup Keys tab shows extracted domain backup keys used for enterprise DPAPI recovery: + +- **Object ID**: The object_id from the originating file +- **Source**: The "source" label for the originating file (host, url, etc.) +- **Domain**: Domain name associated with the backup key +- **Key GUID**: Unique identifier for the backup key +- **Key Status**: Whether the backup key has been successfully extracted +- **Domain Controller**: Source domain controller (if available) +- **Backup Key Data**: The actual backup key material + +![DPAPI domain backup key](images/dpapi_domain_backupkey.png) + +### Submit Credential Material Tab + +The Submit Credential Material tab provides an interface for submitting various types of credential material to decrypt existing DPAPI master keys stored in the system. This tab includes: + +#### Credential Types Supported + +The interface supports multiple credential types for DPAPI decryption: + +- **Password**: Plain text passwords for user accounts +- **NTLM Hash**: NT hash values (16 bytes) for password-equivalent authentication +- **SHA1**: SHA1 credential keys (20 bytes) derived from NTLM hashes +- **Secure Credential Key (PBKDF2)**: 16-byte keys derived using PBKDF2 +- **Domain Backup Key**: Domain controller DPAPI backup keys in base64 PVK format +- **Master Keys {GUID}:SHA1 Pairs**: Plaintext DPAPI master keys as {GUID}:SHA1 pairs +- **DPAPI_SYSTEM Secret**: System-wide DPAPI credentials for machine encryption + +![DPAPI credential submission types](images/dpapi_submit_credential_types.png) + +#### Form Fields + +Based on the selected credential type, the form dynamically displays relevant fields: + +- **User SID**: Required for user-specific credential types (password, NTLM hash, SHA1, PBKDF2) +- **Backup Key GUID**: Required for domain backup keys +- **Domain Controller**: Optional field for domain backup key submissions +- **Credential Value**: Main input field for the credential material +- **Master Key Data**: Structured input for {GUID}:SHA1 pairs (multi-line format) + +Additionally, each credential type includes detailed descriptions explaining: + +- What the credential type is used for +- How to obtain or generate the credential material +- Common sources where these credentials can be collected +- Technical details about the key format and length requirements + +![DPAPI domain backup key submission](images/dpapi_domain_backupkey_submission.png) + +## Data Export and Analysis + +### CSV Export Functionality + +DPAPI data tables support CSV export for external analysis: + +1. Use the table interface to filter and search desired records +2. Click the "Download CSV" button in the top-right of each tab +3. All currently visible/filtered records will be exported + +### Copy Operations + +Individual records or entire result sets can be copied to clipboard: + +- **Single Row**: Double-click any table row to copy all fields +- **Multiple Rows**: Select rows and use Ctrl+C (or Cmd+C on Mac) +- **Filtered Results**: Copy button will copy all currently visible records + +## Integration with Other Nemesis Components + +### Automatic Processing + +DPAPI artifacts are automatically processed when: + +- Windows registry hives are uploaded (SYSTEM, SECURITY, SAM) +- LSASS dumps are analized +- User/system profile directories containing DPAPI folders/masterkeys are processed diff --git a/docs/file_enrichment_modules.md b/docs/file_enrichment_modules.md index 2d95404..4b04a7f 100644 --- a/docs/file_enrichment_modules.md +++ b/docs/file_enrichment_modules.md @@ -33,8 +33,13 @@ The `should_process()` function determines if the module should run on a file. Y ```python ... -def should_process(self, state_key: str) -> bool: - """Determine if this module should run based on file type.""" +def should_process(self, object_id: str, file_path: str | None = None) -> bool: + """Determine if this module should run based on file type + + Args: + object_id: The object ID of the file + file_path: Optional path to already downloaded file + """ file_enriched = get_file_enriched(state_key) # Check if file appears to be a VNC config file should_run = ( @@ -65,18 +70,31 @@ rule has_dpapi_blob } """) -def should_process(self, object_id: str) -> bool: +def should_process(self, object_id: str, file_path: str | None = None) -> bool: + """Check if this file should be processed by scanning for DPAPI blobs. + + Args: + object_id: The object ID of the file + file_path: Optional path to already downloaded file + """ file_enriched = get_file_enriched(object_id) + logger.debug(f"File {object_id} should be processed by DPAPI blob analyzer") if file_enriched.size > self.size_limit: - logger.warning( + logger.debug( f"[dpapi_analyzer] file {file_enriched.path} ({file_enriched.object_id} / {file_enriched.size} bytes) exceeds the size limit of {self.size_limit} bytes, only analyzing the first {self.size_limit} bytes" ) - num_bytes = file_enriched.size if file_enriched.size < self.size_limit else self.size_limit - file_bytes = self.storage.download_bytes(file_enriched.object_id, length=num_bytes) + if file_path: + # Use provided file path - read only the needed bytes + with open(file_path, "rb") as f: + num_bytes = min(file_enriched.size, self.size_limit) + file_bytes = f.read(num_bytes) + else: + # Fallback to downloading the file itself + num_bytes = file_enriched.size if file_enriched.size < self.size_limit else self.size_limit + file_bytes = self.storage.download_bytes(file_enriched.object_id, length=num_bytes) should_run = len(self.yara_rule.scan(file_bytes).matching_rules) > 0 - logger.debug(f"[dpapi_analyzer] should_run: {should_run}") return should_run ... ``` diff --git a/docs/images/agent_credential_extraction.png b/docs/images/agent_credential_extraction.png new file mode 100644 index 0000000..0ad3559 Binary files /dev/null and b/docs/images/agent_credential_extraction.png differ diff --git a/docs/images/agent_credential_extraction_results.png b/docs/images/agent_credential_extraction_results.png new file mode 100644 index 0000000..23cc271 Binary files /dev/null and b/docs/images/agent_credential_extraction_results.png differ diff --git a/docs/images/agent_dotnet_analysis.png b/docs/images/agent_dotnet_analysis.png new file mode 100644 index 0000000..27772b5 Binary files /dev/null and b/docs/images/agent_dotnet_analysis.png differ diff --git a/docs/images/agent_dotnet_analysis_results.png b/docs/images/agent_dotnet_analysis_results.png new file mode 100644 index 0000000..e658277 Binary files /dev/null and b/docs/images/agent_dotnet_analysis_results.png differ diff --git a/docs/images/agent_text_summarizer.png b/docs/images/agent_text_summarizer.png new file mode 100644 index 0000000..778e036 Binary files /dev/null and b/docs/images/agent_text_summarizer.png differ diff --git a/docs/images/agents_finding_triage.png b/docs/images/agents_finding_triage.png new file mode 100644 index 0000000..d3ef548 Binary files /dev/null and b/docs/images/agents_finding_triage.png differ diff --git a/docs/images/agents_true_positive_details.png b/docs/images/agents_true_positive_details.png new file mode 100644 index 0000000..9364b63 Binary files /dev/null and b/docs/images/agents_true_positive_details.png differ diff --git a/docs/images/arize_phoenix_tracing.png b/docs/images/arize_phoenix_tracing.png new file mode 100644 index 0000000..f5285c2 Binary files /dev/null and b/docs/images/arize_phoenix_tracing.png differ diff --git a/docs/images/chromium-cookies-tab.png b/docs/images/chromium-cookies-tab.png new file mode 100644 index 0000000..b0d95e3 Binary files /dev/null and b/docs/images/chromium-cookies-tab.png differ diff --git a/docs/images/chromium-downloads-tab.png b/docs/images/chromium-downloads-tab.png new file mode 100644 index 0000000..d3ad4e3 Binary files /dev/null and b/docs/images/chromium-downloads-tab.png differ diff --git a/docs/images/chromium-history-tab.png b/docs/images/chromium-history-tab.png new file mode 100644 index 0000000..56faece Binary files /dev/null and b/docs/images/chromium-history-tab.png differ diff --git a/docs/images/chromium-logins-tab.png b/docs/images/chromium-logins-tab.png new file mode 100644 index 0000000..ddb97ab Binary files /dev/null and b/docs/images/chromium-logins-tab.png differ diff --git a/docs/images/chromium-state-keys-tab.png b/docs/images/chromium-state-keys-tab.png new file mode 100644 index 0000000..f9ab7f2 Binary files /dev/null and b/docs/images/chromium-state-keys-tab.png differ diff --git a/docs/images/containers_dashboard.png b/docs/images/containers_dashboard.png new file mode 100644 index 0000000..650d823 Binary files /dev/null and b/docs/images/containers_dashboard.png differ diff --git a/docs/images/dpapi_domain_backupkey.png b/docs/images/dpapi_domain_backupkey.png new file mode 100644 index 0000000..532285d Binary files /dev/null and b/docs/images/dpapi_domain_backupkey.png differ diff --git a/docs/images/dpapi_domain_backupkey_submission.png b/docs/images/dpapi_domain_backupkey_submission.png new file mode 100644 index 0000000..26b440a Binary files /dev/null and b/docs/images/dpapi_domain_backupkey_submission.png differ diff --git a/docs/images/dpapi_masterkeys.png b/docs/images/dpapi_masterkeys.png new file mode 100644 index 0000000..cbd8d76 Binary files /dev/null and b/docs/images/dpapi_masterkeys.png differ diff --git a/docs/images/dpapi_submit_credential_types.png b/docs/images/dpapi_submit_credential_types.png new file mode 100644 index 0000000..56a0b4e Binary files /dev/null and b/docs/images/dpapi_submit_credential_types.png differ diff --git a/docs/images/extract_process_container_contents.png b/docs/images/extract_process_container_contents.png new file mode 100644 index 0000000..4b5a3d2 Binary files /dev/null and b/docs/images/extract_process_container_contents.png differ diff --git a/docs/images/litellm_main_interface.png b/docs/images/litellm_main_interface.png new file mode 100644 index 0000000..94098e8 Binary files /dev/null and b/docs/images/litellm_main_interface.png differ diff --git a/docs/images/nemesis_dashboard_agents.png b/docs/images/nemesis_dashboard_agents.png new file mode 100644 index 0000000..1877d12 Binary files /dev/null and b/docs/images/nemesis_dashboard_agents.png differ diff --git a/docs/images/nemesis_dashboard_agents_prompt.png b/docs/images/nemesis_dashboard_agents_prompt.png new file mode 100644 index 0000000..e328b02 Binary files /dev/null and b/docs/images/nemesis_dashboard_agents_prompt.png differ diff --git a/docs/images/nemesis_dynamic_help_menu.png b/docs/images/nemesis_dynamic_help_menu.png new file mode 100644 index 0000000..5bf2e1d Binary files /dev/null and b/docs/images/nemesis_dynamic_help_menu.png differ diff --git a/docs/index.md b/docs/index.md index 9849678..2ca2805 100644 --- a/docs/index.md +++ b/docs/index.md @@ -28,23 +28,27 @@ Nemesis is an offensive file enrichment pipeline. Nemesis 2.0 is built on [Docker](https://www.docker.com/) with heavy [Dapr integration](https://dapr.io/), our goal with Nemesis was to create a centralized file processing platform that functions as an "offensive VirusTotal". -## Additional Information -Blog Posts: +***Note that Nemesis v1.0 is incompatible with Nemesis 2.0!*** -| Title | Date | -|------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------| -| [*Nemesis 1.0.0*](https://posts.specterops.io/nemesis-1-0-0-8c6b745dc7c5) | Apr 25, 2024 | -| [*Summoning RAGnarok With Your Nemesis*](https://posts.specterops.io/summoning-ragnarok-with-your-nemesis-7c4f0577c93b) | Mar 13, 2024 | -| [*Shadow Wizard Registry Gang: Structured Registry Querying*](https://posts.specterops.io/shadow-wizard-registry-gang-structured-registry-querying-9a2fab62a26f) | Sep 5, 2023 | -| [*Hacking With Your Nemesis*](https://posts.specterops.io/hacking-with-your-nemesis-7861f75fcab4) | Aug 9, 2023 | -| [*Challenges In Post-Exploitation Workflows*](https://posts.specterops.io/challenges-in-post-exploitation-workflows-2b3469810fe9) | Aug 2, 2023 | -| [*On (Structured) Data*](https://posts.specterops.io/on-structured-data-707b7d9876c6) | Jul 26, 2023 | +## Additional Information + +Blog Posts + +| Title | Nemesis Version | Date | +| ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------- | ------------ | +| [*Nemesis 2.0*](https://specterops.io/blog/2025/08/05/nemesis-2-0/) | v2.0 | Aug 5, 2025 | +| [*Nemesis 1.0.0*](https://posts.specterops.io/nemesis-1-0-0-8c6b745dc7c5) | v1.0 | Apr 25, 2024 | +| [*Summoning RAGnarok With Your Nemesis*](https://posts.specterops.io/summoning-ragnarok-with-your-nemesis-7c4f0577c93b) | v1.0 | Mar 13, 2024 | +| [*Shadow Wizard Registry Gang: Structured Registry Querying*](https://posts.specterops.io/shadow-wizard-registry-gang-structured-registry-querying-9a2fab62a26f) | v1.0 | Sep 5, 2023 | +| [*Hacking With Your Nemesis*](https://posts.specterops.io/hacking-with-your-nemesis-7861f75fcab4) | v1.0 | Aug 9, 2023 | +| [*Challenges In Post-Exploitation Workflows*](https://posts.specterops.io/challenges-in-post-exploitation-workflows-2b3469810fe9) | v1.0 | Aug 2, 2023 | +| [*On (Structured) Data*](https://posts.specterops.io/on-structured-data-707b7d9876c6) | v1.0 | Jul 26, 2023 | Presentations: | Title | Date | -|----------------------------------------------------------------------------|--------------| +| -------------------------------------------------------------------------- | ------------ | | [*SAINTCON 2023*](https://www.youtube.com/watch?v=0q9u2hDcpIo) | Oct 24, 2023 | | [*BSidesAugusta 2023*](https://www.youtube.com/watch?v=Ug9r7lCF_FA) | Oct 7, 2023 | | [*44CON 2023*](https://www.youtube.com/watch?v=tjPTLBGI7K8) | Sep 15, 2023 | diff --git a/docs/odr.md b/docs/odr.md index 2991090..7db8505 100644 --- a/docs/odr.md +++ b/docs/odr.md @@ -22,6 +22,7 @@ Schema definition for the public `file` message POSTed to the API frontend. | Field | Type | Description | | ----------------------- | -------- | ---------------------------------------------------------------------------------------------------------------- | | `path` | string | File system path to the relevant resource. Can use either forward (/) or backward (\\) slashes | +| `source` | string | String representing a `host://IP`, `host://HOSTNAME`, or traditional URI (i.e., `https://domain.com) | | `originating_object_id` | string | UUID v4 format identifier referencing a parent or source object | | `nesting_level` | number | The level of nesting for the file within an originating container. Used to prevent indefinite container nesting. | | `creation_time` | datetime | ISO 8601 formatted timestamp for when the file was created | diff --git a/docs/openapi.json b/docs/openapi.json new file mode 100644 index 0000000..0674172 --- /dev/null +++ b/docs/openapi.json @@ -0,0 +1,1924 @@ +{ + "openapi": "3.1.0", + "info": { + "title": "Enrichment API", + "description": "API for file enrichment services", + "version": "0.1.0" + }, + "paths": { + "/files": { + "post": { + "tags": [ + "files" + ], + "summary": "Upload file with metadata", + "description": "Upload a file using multipart/form-data with metadata.\n Returns an object_id for the uploaded file and submission_id for the metadata submission.\n\n Example:\n ```\n curl -k -u n:n -F \"file=@example.txt\" -F 'metadata={\"agent_id\":\"agent123\",\"project\":\"proj1\",\"timestamp\":\"2024-01-29T12:00:00Z\",\"expiration\":\"2024-02-29T12:00:00Z\",\"path\":\"/tmp/example.txt\"}' https://nemesis:7443/api/files\n ```\n\n Example:\n ```\n curl -k -u n:n -F \"file=@example.txt\" -F 'metadata={\"agent_id\":\"agent123\",\"project\":\"proj1\",\"path\":\"/tmp/example.txt\"}' https://nemesis:7443/api/files\n ```", + "operationId": "upload_file_files_post", + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/Body_upload_file_files_post" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FileWithMetadataResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/files/{object_id}": { + "get": { + "tags": [ + "files" + ], + "summary": "Download a file", + "description": "Download a file by its object ID with optional raw format and custom filename", + "operationId": "download_file_files__object_id__get", + "parameters": [ + { + "name": "object_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "description": "Unique identifier of the file to download", + "title": "Object Id" + }, + "description": "Unique identifier of the file to download" + }, + { + "name": "raw", + "in": "query", + "required": false, + "schema": { + "type": "boolean", + "description": "Whether to return the file in raw format", + "default": false, + "title": "Raw" + }, + "description": "Whether to return the file in raw format" + }, + { + "name": "name", + "in": "query", + "required": false, + "schema": { + "type": "string", + "description": "Custom filename for the downloaded file", + "default": "", + "title": "Name" + }, + "description": "Custom filename for the downloaded file" + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + }, + "description": "Not Found" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + }, + "description": "Bad Request" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + }, + "description": "Internal Server Error" + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/containers": { + "post": { + "tags": [ + "files" + ], + "summary": "Submit large container file for processing with optional filtering", + "description": "...", + "operationId": "submit_container_containers_post", + "requestBody": { + "content": { + "multipart/form-data": { + "schema": { + "$ref": "#/components/schemas/Body_submit_container_containers_post" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ContainerSubmissionResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/containers/{container_id}/status": { + "get": { + "tags": [ + "files" + ], + "summary": "Get container processing status", + "description": "Get the current processing status and progress of a submitted container", + "operationId": "get_container_status_containers__container_id__status_get", + "parameters": [ + { + "name": "container_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "description": "Unique identifier of the container", + "title": "Container Id" + }, + "description": "Unique identifier of the container" + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ContainerStatusResponse" + } + } + } + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + }, + "description": "Not Found" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + }, + "description": "Internal Server Error" + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/workflows/status": { + "get": { + "tags": [ + "workflows" + ], + "summary": "Get workflow enrichment workflow status", + "description": "Get the current status of the enrichment workflow system", + "operationId": "get_status_workflows_status_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/WorkflowStatusResponse" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/workflows/failed": { + "get": { + "tags": [ + "workflows" + ], + "summary": "Get failed workflows", + "description": "Get the set of failed enrichment workflows", + "operationId": "get_failed_workflows_failed_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FailedWorkflowsResponse" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/enrichments": { + "get": { + "tags": [ + "enrichments" + ], + "summary": "List enrichment modules", + "description": "Get a list of all available enrichment modules", + "operationId": "list_enrichments_enrichments_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EnrichmentsListResponse" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/enrichments/{enrichment_name}": { + "post": { + "tags": [ + "enrichments" + ], + "summary": "Run enrichment module", + "description": "Run a specific enrichment module on a file", + "operationId": "run_enrichment_enrichments__enrichment_name__post", + "parameters": [ + { + "name": "enrichment_name", + "in": "path", + "required": true, + "schema": { + "type": "string", + "description": "Name of the enrichment module to run", + "title": "Enrichment Name" + }, + "description": "Name of the enrichment module to run" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EnrichmentRequest", + "description": "The enrichment request containing the object ID" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EnrichmentResponse" + } + } + } + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + }, + "description": "Not Found" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + }, + "description": "Internal Server Error" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + }, + "description": "Service Unavailable" + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/enrichments/{enrichment_name}/bulk": { + "post": { + "tags": [ + "enrichments" + ], + "summary": "Start bulk enrichment", + "description": "Start bulk enrichment for a specific module against all files in the system using distributed processing", + "operationId": "run_bulk_enrichment_enrichments__enrichment_name__bulk_post", + "parameters": [ + { + "name": "enrichment_name", + "in": "path", + "required": true, + "schema": { + "type": "string", + "description": "Name of the enrichment module to run", + "title": "Enrichment Name" + }, + "description": "Name of the enrichment module to run" + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + }, + "description": "Not Found" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + }, + "description": "Internal Server Error" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + }, + "description": "Service Unavailable" + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/enrichments/{enrichment_name}/bulk/status": { + "get": { + "tags": [ + "enrichments" + ], + "summary": "Get bulk enrichment status", + "description": "Bulk enrichment status tracking has been simplified", + "operationId": "get_bulk_enrichment_status_enrichments__enrichment_name__bulk_status_get", + "parameters": [ + { + "name": "enrichment_name", + "in": "path", + "required": true, + "schema": { + "type": "string", + "description": "Name of the enrichment module to check status for", + "title": "Enrichment Name" + }, + "description": "Name of the enrichment module to check status for" + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/enrichments/{enrichment_name}/bulk/stop": { + "post": { + "tags": [ + "enrichments" + ], + "summary": "Stop bulk enrichment", + "description": "Bulk enrichment cannot be stopped once tasks are published", + "operationId": "stop_bulk_enrichment_enrichments__enrichment_name__bulk_stop_post", + "parameters": [ + { + "name": "enrichment_name", + "in": "path", + "required": true, + "schema": { + "type": "string", + "description": "Name of the enrichment module to stop", + "title": "Enrichment Name" + }, + "description": "Name of the enrichment module to stop" + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/queues": { + "get": { + "tags": [ + "queues" + ], + "summary": "Get queue statistics", + "description": "Get comprehensive queue metrics for all workflow topics", + "operationId": "get_queue_metrics_queues_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/QueuesResponse" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/queues/{queue_name}": { + "get": { + "tags": [ + "queues" + ], + "summary": "Get single queue statistics", + "description": "Get metrics for a specific queue topic", + "operationId": "get_single_queue_metrics_queues__queue_name__get", + "parameters": [ + { + "name": "queue_name", + "in": "path", + "required": true, + "schema": { + "type": "string", + "description": "Name of the queue to get metrics for", + "title": "Queue Name" + }, + "description": "Name of the queue to get metrics for" + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SingleQueueResponse" + } + } + } + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + }, + "description": "Internal Server Error" + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/system/yara/reload": { + "post": { + "tags": [ + "system" + ], + "summary": "Reload Yara rules", + "description": "Trigger a reload of all Yara rules in the backend across all workers/replicas", + "operationId": "reload_yara_rules_system_yara_reload_post", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/YaraReloadResponse" + } + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/system/cleanup": { + "post": { + "tags": [ + "system" + ], + "summary": "Trigger database and datalake cleanup", + "description": "Trigger the housekeeping service to clean up expired files and database entries, and reset the workflow manager state. Optionally specify an expiration date or 'all' to remove all files.", + "operationId": "trigger_cleanup_system_cleanup_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CleanupRequest", + "description": "Optional cleanup parameters" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "500": { + "description": "Internal Server Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "503": { + "description": "Service Unavailable", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/system/health": { + "get": { + "tags": [ + "system" + ], + "summary": "Health check", + "description": "Health check endpoint for Docker healthcheck", + "operationId": "healthcheck_system_health_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HealthResponse" + } + } + } + } + } + } + }, + "/system/info": { + "get": { + "tags": [ + "system" + ], + "summary": "API information", + "description": "Root endpoint that shows API information", + "operationId": "root_system_info_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/APIInfo" + } + } + } + } + } + } + }, + "/system/apprise-info": { + "get": { + "tags": [ + "system" + ], + "summary": "Get Apprise alert information", + "description": "Get information about configured alert channels (currently Slack only)", + "operationId": "get_apprise_info_system_apprise_info_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/agents": { + "get": { + "tags": [ + "system" + ], + "summary": "Get available agents", + "description": "Get a list of available AI agents with their metadata and capabilities", + "operationId": "get_available_agents_agents_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/agents/spend-data": { + "get": { + "tags": [ + "system" + ], + "summary": "Get LLM spend and usage data", + "description": "Get total spend and token usage statistics from LiteLLM logs", + "operationId": "get_agents_spend_data_agents_spend_data_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/agents/text_summarizer": { + "post": { + "tags": [ + "system" + ], + "summary": "Run text summarization", + "description": "Forward text summarization request to agents service", + "operationId": "run_text_summarizer_agents_text_summarizer_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "additionalProperties": true, + "type": "object", + "title": "Request", + "description": "Request containing object_id" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/agents/llm_credential_analysis": { + "post": { + "tags": [ + "system" + ], + "summary": "Run credential analysis", + "description": "Forward credential analysis request to agents service", + "operationId": "run_llm_credential_analysis_agents_llm_credential_analysis_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "additionalProperties": true, + "type": "object", + "title": "Request", + "description": "Request containing object_id" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/agents/dotnet_analysis": { + "post": { + "tags": [ + "system" + ], + "summary": "Run .NET assembly analysis", + "description": "Forward .NET assembly analysis request to agents service", + "operationId": "run_dotnet_analysis_agents_dotnet_analysis_post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "additionalProperties": true, + "type": "object", + "title": "Request", + "description": "Request containing object_id" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + } + } + } + } + }, + "/system/available-services": { + "get": { + "tags": [ + "system" + ], + "summary": "Get available services", + "description": "Query Traefik to determine which optional services are currently available", + "operationId": "get_available_services_system_available_services_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + }, + "/system/container-monitor/status": { + "get": { + "tags": [ + "system" + ], + "summary": "Container monitor status", + "description": "Get the status of the container file monitor", + "operationId": "get_container_monitor_status_system_container_monitor_status_get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {} + } + } + } + } + } + } + }, + "components": { + "schemas": { + "APIInfo": { + "properties": { + "name": { + "type": "string", + "title": "Name", + "description": "API name" + }, + "version": { + "type": "string", + "title": "Version", + "description": "API version" + } + }, + "type": "object", + "required": [ + "name", + "version" + ], + "title": "APIInfo", + "description": "Model representing API information" + }, + "ActiveWorkflowDetail": { + "properties": { + "id": { + "type": "string", + "title": "Id" + }, + "status": { + "type": "string", + "title": "Status" + }, + "filename": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Filename" + }, + "object_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Object Id" + }, + "timestamp": { + "anyOf": [ + { + "type": "string", + "format": "date-time" + }, + { + "type": "null" + } + ], + "title": "Timestamp" + }, + "runtime_seconds": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "title": "Runtime Seconds" + }, + "error": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Error" + } + }, + "type": "object", + "required": [ + "id", + "status" + ], + "title": "ActiveWorkflowDetail" + }, + "Body_submit_container_containers_post": { + "properties": { + "file": { + "type": "string", + "format": "binary", + "title": "File", + "description": "The container file to process" + }, + "metadata": { + "type": "string", + "title": "Metadata", + "description": "JSON string containing file metadata with optional file_filters" + } + }, + "type": "object", + "required": [ + "file", + "metadata" + ], + "title": "Body_submit_container_containers_post" + }, + "Body_upload_file_files_post": { + "properties": { + "file": { + "type": "string", + "format": "binary", + "title": "File", + "description": "The file to upload" + }, + "metadata": { + "type": "string", + "title": "Metadata", + "description": "JSON string containing file metadata" + } + }, + "type": "object", + "required": [ + "file", + "metadata" + ], + "title": "Body_upload_file_files_post" + }, + "CleanupRequest": { + "properties": { + "expiration": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Expiration" + } + }, + "type": "object", + "title": "CleanupRequest" + }, + "ContainerStatusResponse": { + "properties": { + "container_id": { + "type": "string", + "title": "Container Id" + }, + "status": { + "type": "string", + "title": "Status" + }, + "progress_percent_files": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "title": "Progress Percent Files" + }, + "progress_percent_bytes": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "title": "Progress Percent Bytes" + }, + "processed_files": { + "type": "integer", + "title": "Processed Files" + }, + "total_files": { + "type": "integer", + "title": "Total Files" + }, + "processed_bytes": { + "type": "integer", + "title": "Processed Bytes" + }, + "total_bytes": { + "type": "integer", + "title": "Total Bytes" + }, + "current_file": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Current File" + }, + "started_at": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Started At" + }, + "error": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Error" + }, + "filter_stats": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Filter Stats" + } + }, + "type": "object", + "required": [ + "container_id", + "status", + "processed_files", + "total_files", + "processed_bytes", + "total_bytes" + ], + "title": "ContainerStatusResponse" + }, + "ContainerSubmissionResponse": { + "properties": { + "container_id": { + "type": "string", + "title": "Container Id" + }, + "message": { + "type": "string", + "title": "Message" + }, + "estimated_files": { + "type": "integer", + "title": "Estimated Files" + }, + "estimated_size": { + "type": "integer", + "title": "Estimated Size" + }, + "filter_config": { + "anyOf": [ + { + "additionalProperties": true, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Filter Config" + } + }, + "type": "object", + "required": [ + "container_id", + "message", + "estimated_files", + "estimated_size" + ], + "title": "ContainerSubmissionResponse" + }, + "EnrichmentRequest": { + "properties": { + "object_id": { + "type": "string", + "title": "Object Id" + } + }, + "type": "object", + "required": [ + "object_id" + ], + "title": "EnrichmentRequest" + }, + "EnrichmentResponse": { + "properties": { + "status": { + "type": "string", + "title": "Status" + }, + "message": { + "type": "string", + "title": "Message" + }, + "instance_id": { + "type": "string", + "title": "Instance Id" + }, + "object_id": { + "type": "string", + "title": "Object Id" + } + }, + "type": "object", + "required": [ + "status", + "message", + "instance_id", + "object_id" + ], + "title": "EnrichmentResponse" + }, + "EnrichmentsListResponse": { + "properties": { + "modules": { + "items": { + "type": "string" + }, + "type": "array", + "title": "Modules" + } + }, + "type": "object", + "required": [ + "modules" + ], + "title": "EnrichmentsListResponse" + }, + "ErrorResponse": { + "properties": { + "detail": { + "type": "string", + "title": "Detail", + "description": "Error message details" + } + }, + "type": "object", + "required": [ + "detail" + ], + "title": "ErrorResponse" + }, + "FailedWorkflowsResponse": { + "properties": { + "failed_count": { + "type": "integer", + "title": "Failed Count" + }, + "workflows": { + "items": { + "$ref": "#/components/schemas/ActiveWorkflowDetail" + }, + "type": "array", + "title": "Workflows", + "default": [] + }, + "timestamp": { + "type": "string", + "title": "Timestamp" + } + }, + "type": "object", + "required": [ + "failed_count", + "timestamp" + ], + "title": "FailedWorkflowsResponse" + }, + "FileWithMetadataResponse": { + "properties": { + "object_id": { + "type": "string", + "format": "uuid", + "title": "Object Id", + "description": "Unique identifier for the uploaded file" + }, + "submission_id": { + "type": "string", + "format": "uuid", + "title": "Submission Id", + "description": "Unique identifier for the metadata submission" + } + }, + "type": "object", + "required": [ + "object_id", + "submission_id" + ], + "title": "FileWithMetadataResponse", + "description": "Response for combined file and metadata uploads" + }, + "HTTPValidationError": { + "properties": { + "detail": { + "items": { + "$ref": "#/components/schemas/ValidationError" + }, + "type": "array", + "title": "Detail" + } + }, + "type": "object", + "title": "HTTPValidationError" + }, + "HealthResponse": { + "properties": { + "status": { + "type": "string", + "title": "Status", + "description": "Health status of the service" + } + }, + "type": "object", + "required": [ + "status" + ], + "title": "HealthResponse", + "description": "Model representing health check response" + }, + "QueueMetrics": { + "properties": { + "total_messages": { + "type": "integer", + "title": "Total Messages" + }, + "ready_messages": { + "type": "integer", + "title": "Ready Messages" + }, + "processing_messages": { + "type": "integer", + "title": "Processing Messages" + }, + "consumers": { + "type": "integer", + "title": "Consumers" + }, + "queue_exists": { + "type": "boolean", + "title": "Queue Exists" + }, + "memory_bytes": { + "type": "integer", + "title": "Memory Bytes" + }, + "state": { + "type": "string", + "title": "State" + }, + "message_stats": { + "additionalProperties": true, + "type": "object", + "title": "Message Stats" + }, + "error": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Error" + } + }, + "type": "object", + "required": [ + "total_messages", + "ready_messages", + "processing_messages", + "consumers", + "queue_exists", + "memory_bytes", + "state", + "message_stats" + ], + "title": "QueueMetrics" + }, + "QueueSummary": { + "properties": { + "total_queued_messages": { + "type": "integer", + "title": "Total Queued Messages" + }, + "total_processing_messages": { + "type": "integer", + "title": "Total Processing Messages" + }, + "total_consumers": { + "type": "integer", + "title": "Total Consumers" + }, + "healthy_queues": { + "type": "integer", + "title": "Healthy Queues" + }, + "total_queues_checked": { + "type": "integer", + "title": "Total Queues Checked" + }, + "bottleneck_queues": { + "items": { + "type": "string" + }, + "type": "array", + "title": "Bottleneck Queues" + }, + "queues_without_consumers": { + "items": { + "type": "string" + }, + "type": "array", + "title": "Queues Without Consumers" + }, + "total_memory_bytes": { + "type": "integer", + "title": "Total Memory Bytes" + } + }, + "type": "object", + "required": [ + "total_queued_messages", + "total_processing_messages", + "total_consumers", + "healthy_queues", + "total_queues_checked", + "bottleneck_queues", + "queues_without_consumers", + "total_memory_bytes" + ], + "title": "QueueSummary" + }, + "QueuesResponse": { + "properties": { + "queue_details": { + "additionalProperties": { + "$ref": "#/components/schemas/QueueMetrics" + }, + "type": "object", + "title": "Queue Details" + }, + "summary": { + "$ref": "#/components/schemas/QueueSummary" + }, + "timestamp": { + "type": "string", + "title": "Timestamp" + } + }, + "type": "object", + "required": [ + "queue_details", + "summary", + "timestamp" + ], + "title": "QueuesResponse" + }, + "SingleQueueResponse": { + "properties": { + "topic": { + "type": "string", + "title": "Topic" + }, + "metrics": { + "$ref": "#/components/schemas/QueueMetrics" + }, + "timestamp": { + "type": "string", + "title": "Timestamp" + } + }, + "type": "object", + "required": [ + "topic", + "metrics", + "timestamp" + ], + "title": "SingleQueueResponse" + }, + "ValidationError": { + "properties": { + "loc": { + "items": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "integer" + } + ] + }, + "type": "array", + "title": "Location" + }, + "msg": { + "type": "string", + "title": "Message" + }, + "type": { + "type": "string", + "title": "Error Type" + } + }, + "type": "object", + "required": [ + "loc", + "msg", + "type" + ], + "title": "ValidationError" + }, + "WorkflowMetrics": { + "properties": { + "completed_count": { + "type": "integer", + "title": "Completed Count" + }, + "failed_count": { + "type": "integer", + "title": "Failed Count" + }, + "total_processed": { + "type": "integer", + "title": "Total Processed" + }, + "success_rate": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "title": "Success Rate" + }, + "processing_times": { + "anyOf": [ + { + "$ref": "#/components/schemas/WorkflowProcessingStats" + }, + { + "type": "null" + } + ] + } + }, + "type": "object", + "required": [ + "completed_count", + "failed_count", + "total_processed" + ], + "title": "WorkflowMetrics" + }, + "WorkflowProcessingStats": { + "properties": { + "avg_seconds": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "title": "Avg Seconds" + }, + "min_seconds": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "title": "Min Seconds" + }, + "max_seconds": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "title": "Max Seconds" + }, + "p50_seconds": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "title": "P50 Seconds" + }, + "p90_seconds": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "title": "P90 Seconds" + }, + "p95_seconds": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "title": "P95 Seconds" + }, + "p99_seconds": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "title": "P99 Seconds" + }, + "samples_count": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Samples Count" + } + }, + "type": "object", + "title": "WorkflowProcessingStats" + }, + "WorkflowStatusResponse": { + "properties": { + "active_workflows": { + "type": "integer", + "title": "Active Workflows" + }, + "status_counts": { + "anyOf": [ + { + "additionalProperties": { + "type": "integer" + }, + "type": "object" + }, + { + "type": "null" + } + ], + "title": "Status Counts" + }, + "active_details": { + "items": { + "$ref": "#/components/schemas/ActiveWorkflowDetail" + }, + "type": "array", + "title": "Active Details", + "default": [] + }, + "metrics": { + "$ref": "#/components/schemas/WorkflowMetrics" + }, + "timestamp": { + "type": "string", + "title": "Timestamp" + }, + "error": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Error" + } + }, + "type": "object", + "required": [ + "active_workflows", + "metrics", + "timestamp" + ], + "title": "WorkflowStatusResponse" + }, + "YaraReloadResponse": { + "properties": { + "message": { + "type": "string", + "title": "Message", + "description": "Status message for Yara rules reload" + } + }, + "type": "object", + "required": [ + "message" + ], + "title": "YaraReloadResponse", + "description": "Model representing Yara rules reload response" + } + } + }, + "tags": [ + { + "name": "files", + "description": "File management operations" + }, + { + "name": "workflows", + "description": "Workflow management operations" + }, + { + "name": "enrichments", + "description": "Enrichment management operations" + }, + { + "name": "queues", + "description": "Internal pub/sub queue operations" + }, + { + "name": "system", + "description": "System and health check endpoints" + } + ] +} \ No newline at end of file diff --git a/docs/performance.md b/docs/performance.md index 0275eb4..69ed20f 100644 --- a/docs/performance.md +++ b/docs/performance.md @@ -10,11 +10,8 @@ If workflows begin to fail, or you are experiencing major performance issues (as For production (non-dev) deployments, multiple UVICORN_WORKERS are used for the `file-enrichment` service. The default value is 2 and is defined in the `file-enrichment` section in [compose.yaml](https://github.com/SpecterOps/Nemesis/blob/71406afc12f855140ea68aae337076f9b8dc292f/compose.yaml#L217). This value can be set to 1 for troubleshooting, or increased to 4+ for potential performance gains. You can modify this value by defining the `export UVICORN_WORKERS=4` environment variable before launching Nemesis. -### MAX_PARALLEL_WORKFLOWS - -The `file-enrichment` container runs a number of file-enrichment workflows in parallel, defaulting to 5. You can modify this value by defining the `export MAX_PARALLEL_WORKFLOWS=3` environment variable before launching Nemesis. - - -### MAX_PARALLEL_ENRICHMENT_MODULES - -For each file enrichment workflow, the `file-enrichment` container runs multiple file enrichment modules in parallel, defaulting to 5. You can modify this value by defining the `export MAX_PARALLEL_ENRICHMENT_MODULES=3` environment variable before launching Nemesis. +# Useful Prometheus Metrics +Minio +``` +minio_cluster_usage_objects_count{} +``` \ No newline at end of file diff --git a/docs/quickstart.md b/docs/quickstart.md index 8c3fa61..34794eb 100644 --- a/docs/quickstart.md +++ b/docs/quickstart.md @@ -17,6 +17,12 @@ Ensure your machine meets the following requirements: - Docker version 28.0.0 or higher is recommended. See [Docker's installation instructions](https://docs.docker.com/engine/install/) for instructions on installing Docker. Running the Docker Engine on Linux or on OS X via Docker Desktop is recommended. If using Docker Desktop, ensure that the VM is configured with sufficient RAM/Disk/swap. +**NOTE:** for multi-language support for OCR/document processing, set the `TIKA_OCR_LANGUAGES` ENV var before launching with the [Tesseract language code](https://github.com/tesseract-ocr/tessdata): +```bash +$ export TIKA_OCR_LANGUAGES="eng chi_sim chi_tra jpn rus deu spa" +``` + + ### Step 1: Clone the Nemesis Repository ```bash git clone https://github.com/SpecterOps/Nemesis @@ -46,10 +52,10 @@ To start Nemesis's core services, run the `./tools/nemesis-ctl.sh` script: ./tools/nemesis-ctl.sh start prod ``` -If you'd like to install the monitoring services and/or jupyter notebooks, use the associated optional command line arguments: +If you'd like to install the monitoring services, jupyter notebooks, and/or LLM agents use the associated optional command line arguments: ```bash -./tools/nemesis-ctl.sh start prod --monitoring --jupyter +./tools/nemesis-ctl.sh start prod --monitoring --jupyter [--llm] ``` `nemesis-ctl.sh` effectively is a wrapper for `docker compose` commands and is in charge of pulling and starting the appropriate published Nemesis docker images. In general, we recommend people use `nemesis-ctl.sh` instead of manually invoking `docker compose`. For more complex deployment scenarios, see Nemesis's [Docker Compose documentation](docker_compose.md) to understand what `nemesis-ctl.sh` does underneath. @@ -90,17 +96,19 @@ Click on the "Help" button on the bottom left to view the additionally exposed N **NOTE:** The /jupyter/ route will only be available if you started with it enabled (`--jupyter`). +**NOTE:** The /jupyter/ route will only be available if you started with it enabled (`--jupyter`). + ![Nemesis services](images/nemesis-dashboard-services.png) ### Step 7: Shutting Nemesis Down -To shutdown Nemesis, use the `nemesis-ctl.sh` script's `stop` or `clean` commands ***with the same arguments you used to start it***. For example, if you started it with monitoring and jupyter enabled, then run the following: +To shutdown Nemesis, use the `nemesis-ctl.sh` script's `stop` or `clean` commands ***with the same arguments you used to start it***. For example, if you started it with monitoring, jupyter, or LLM support enabled, then run the following: - To stop Nemesis containers: ```bash -./tools/nemesis-ctl.sh stop prod --monitoring --jupyter +./tools/nemesis-ctl.sh stop prod --monitoring --jupyter --llm ``` - To stop Nemesis containers and delete their associated volumes: ```bash -./tools/nemesis-ctl.sh clean prod --monitoring --jupyter +./tools/nemesis-ctl.sh clean prod --monitoring --jupyter --llm ``` diff --git a/env.example b/env.example index 2013719..1928b65 100644 --- a/env.example +++ b/env.example @@ -1,20 +1,27 @@ -MINIO_ROOT_PASSWORD=Qwerty12345 -MINIO_ROOT_USER=nemesis -POSTGRES_PASSWORD=Qwerty12345 -POSTGRES_USER=nemesis -RABBITMQ_PASSWORD=Qwerty12345 -RABBITMQ_USER=nemesis +MINIO_ROOT_PASSWORD="Qwerty12345" +MINIO_ROOT_USER="nemesis" + +RABBITMQ_PASSWORD="Qwerty12345" +RABBITMQ_USER="nemesis" + +POSTGRES_PASSWORD="Qwerty12345" +POSTGRES_USER="nemesis" +POSTGRES_HOST="postgres" +POSTGRES_PORT=5432 +POSTGRES_DB="enrichment" +POSTGRES_PARAMETERS="sslmode=disable" -# You can change the password used by HASURA. If not defined a default password will be used. + +# You can change the password used by HASURA. If not defined a default password will be used (pass456) # Example: -# HASURA_ADMIN_SECRET=Qwerty12345 +# HASURA_ADMIN_SECRET="Qwerty12345" # NEMESIS_URL is used when building hyperlinks for findings and Apprise alerts. # If you change the port Nemesis listens on using NEMESIS_PORT (below), ensure this URL's port matches. # If using a host/domain name, a FQDN (with a top level domain) is recommended. -NEMESIS_URL=https://localhost:7443/ +NEMESIS_URL="https://localhost:7443/" #################### @@ -50,4 +57,11 @@ NEMESIS_URL=https://localhost:7443/ # (Optional) Set Jupyter credentials using JUPYTER_PASSWORD. # If not defined, a random password will be generated and printed in the jupyter container's logs. # Example: -# JUPYTER_PASSWORD=Qwerty12345 +# JUPYTER_PASSWORD="Qwerty12345" + + +# (Optional) Enable Phoenix LLM tracing for Pydantic AI agents. +# Requires starting Nemesis with monitoring profile: ./tools/nemesis-ctl.sh start dev --monitoring +# Phoenix UI will be available at http://localhost:6006 +# Example: +# PHOENIX_ENABLED=true diff --git a/infra/dapr/components/pubsub.yaml b/infra/dapr/components/pubsub.yaml index 1e8299a..4d101e8 100644 --- a/infra/dapr/components/pubsub.yaml +++ b/infra/dapr/components/pubsub.yaml @@ -16,15 +16,54 @@ spec: value: "false" - name: autoAck value: "false" + - name: requeueInFailure + value: "true" - name: reconnectWait - value: "5" + value: "3" - name: concurrencyMode value: parallel - # - name: backOffMaxRetries # doesn't appear to be a used value - # value: "5" - # - name: backOffMaxInterval # doesn't appear to be a used value - # value: "5s" - - name: prefetchCount - value: "10" # Set to 1 for processing one message at a time + - name: deliveryMode # persistence setting + value: "2" + - name: prefetchCount # should be equal or less than the max concurrent workflows + value: "5" + - name: maxPriority # set so we can prioritize some messages if wanted + value: "3" auth: secretStore: nemesis-secret-store +--- +apiVersion: dapr.io/v1alpha1 +kind: Component +metadata: + name: broadcast +spec: + type: pubsub.rabbitmq + version: v1 + metadata: + - name: connectionString + secretKeyRef: + name: RABBITMQ_CONNECTION_STRING + key: RABBITMQ_CONNECTION_STRING + - name: consumerID + value: mytest-{appID}-{uuid} # Unique consumerID causes the message to be sent to each subscriber. Each subscriber has a RabbitMQ queue named accoridingly. + - name: durable + value: true + - name: deleteWhenUnused + value: true + - name: autoAck + value: true + - name: deliveryMode + value: 2 # 1=transient messages(messages stay in-memory), 2=Persistent messages(use in conjunction with durable queues) + - name: publisherConfirm + value: true + - name: requeueInFailure + value: "true" + - name: reconnectWait + value: "3" + - name: concurrencyMode + value: parallel + - name: prefetchCount # should be equal or less than the max concurrent workflows + value: "5" + - name: maxPriority # set so we can prioritize some messages if wanted + value: "3" +auth: + secretStore: nemesis-secret-store \ No newline at end of file diff --git a/infra/dapr/components/workflowstate.yaml b/infra/dapr/components/workflowstate.yaml index 8698389..24273d2 100644 --- a/infra/dapr/components/workflowstate.yaml +++ b/infra/dapr/components/workflowstate.yaml @@ -7,9 +7,27 @@ spec: version: v1 metadata: - name: connectionString + value: "postgresql://username:password@hostname:5432/databasename?sslmode=disable" + - name: host secretKeyRef: - name: POSTGRES_CONNECTION_STRING - key: POSTGRES_CONNECTION_STRING + name: POSTGRES_HOST + key: POSTGRES_HOST + - name: port + secretKeyRef: + name: POSTGRES_PORT + key: POSTGRES_PORT + - name: database + secretKeyRef: + name: POSTGRES_DB + key: POSTGRES_DB + - name: user + secretKeyRef: + name: POSTGRES_USER + key: POSTGRES_USER + - name: password + secretKeyRef: + name: POSTGRES_PASSWORD + key: POSTGRES_PASSWORD - name: actorStateStore value: "true" - name: table diff --git a/infra/dapr/configuration/config_monitoring_disabled.yaml b/infra/dapr/configuration/config_monitoring_disabled.yaml index c83f886..b4b6225 100644 --- a/infra/dapr/configuration/config_monitoring_disabled.yaml +++ b/infra/dapr/configuration/config_monitoring_disabled.yaml @@ -3,6 +3,9 @@ kind: Configuration metadata: name: schedulerconfig spec: + # workflow: + # maxConcurrentWorkflowInvocations: 1 + # maxConcurrentActivityInvocations: 1 httpPipeline: handlers: - name: maximum-request-size diff --git a/infra/dapr/configuration/config_monitoring_enabled.yaml b/infra/dapr/configuration/config_monitoring_enabled.yaml index cf5c8cb..275ea73 100644 --- a/infra/dapr/configuration/config_monitoring_enabled.yaml +++ b/infra/dapr/configuration/config_monitoring_enabled.yaml @@ -3,6 +3,9 @@ kind: Configuration metadata: name: schedulerconfig spec: + # workflow: + # maxConcurrentWorkflowInvocations: 1 + # maxConcurrentActivityInvocations: 1 tracing: expandParams: true samplingRate: "1" diff --git a/infra/grafana/provisioning/datasources/postgres.yaml b/infra/grafana/provisioning/datasources/postgres.yaml index 10bde90..f7d328c 100644 --- a/infra/grafana/provisioning/datasources/postgres.yaml +++ b/infra/grafana/provisioning/datasources/postgres.yaml @@ -7,13 +7,8 @@ deleteDatasources: datasources: - name: Postgres type: postgres - url: postgres:5432 - user: ${POSTGRES_USER} - secureJsonData: - password: ${POSTGRES_PASSWORD} + url: ${POSTGRES_CONNECTION_STRING} jsonData: - database: enrichment - sslmode: disable maxOpenConns: 100 maxIdleConns: 100 maxIdleConnsAuto: true diff --git a/infra/hasura/metadata/tables.yaml b/infra/hasura/metadata/tables.yaml index 2a7f97e..4f2ebc1 100644 --- a/infra/hasura/metadata/tables.yaml +++ b/infra/hasura/metadata/tables.yaml @@ -2,6 +2,9 @@ - table: name: document_search_results schema: public +- table: + name: container_processing + schema: public - table: name: workflows schema: public @@ -139,3 +142,59 @@ - table: name: yara_rules schema: public +- table: + name: agent_prompts + schema: public +- table: + name: file_linkings + schema: public +- table: + name: file_listings + schema: public + +# `chromium` schema +- table: + name: history + schema: chromium + object_relationships: + - name: files_enriched + using: + foreign_key_constraint_on: originating_object_id +- table: + name: downloads + schema: chromium + object_relationships: + - name: files_enriched + using: + foreign_key_constraint_on: originating_object_id +- table: + name: logins + schema: chromium + object_relationships: + - name: files_enriched + using: + foreign_key_constraint_on: originating_object_id +- table: + name: cookies + schema: chromium + object_relationships: + - name: files_enriched + using: + foreign_key_constraint_on: originating_object_id +- table: + name: state_keys + schema: chromium +- table: + name: chrome_keys + schema: chromium + +# `dpapi` schema +- table: + name: masterkeys + schema: dpapi +- table: + name: system_credentials + schema: dpapi +- table: + name: domain_backup_keys + schema: dpapi diff --git a/infra/litellm/config.yml b/infra/litellm/config.yml new file mode 100644 index 0000000..a8e6438 --- /dev/null +++ b/infra/litellm/config.yml @@ -0,0 +1,19 @@ +model_list: + # used if other task models are not defined + - model_name: default + litellm_params: + model: bedrock/us.anthropic.claude-sonnet-4-20250514-v1:0 + aws_access_key_id: os.environ/AWS_ACCESS_KEY_ID + aws_secret_access_key: os.environ/AWS_SECRET_ACCESS_KEY + aws_region_name: "us-east-1" + # # used to triage findings and determine their validity + # - model_name: triage + # litellm_params: + # model: bedrock/us.anthropic.claude-sonnet-4-20250514-v1:0 + # aws_access_key_id: os.environ/AWS_ACCESS_KEY_ID + # aws_secret_access_key: os.environ/AWS_SECRET_ACCESS_KEY + # aws_region_name: "us-east-1" + +general_settings: + database_connection_pool_limit: 30 + database_connection_timeout: 60 diff --git a/infra/postgres-exporter/postgres_exporter.yml b/infra/postgres-exporter/postgres_exporter.yml new file mode 100644 index 0000000..d6bcdd3 --- /dev/null +++ b/infra/postgres-exporter/postgres_exporter.yml @@ -0,0 +1,2 @@ +# Postgres Exporter Configuration +auth_modules: {} diff --git a/infra/postgres/01-schema.sql b/infra/postgres/01-schema.sql index b85cfe3..f42478e 100644 --- a/infra/postgres/01-schema.sql +++ b/infra/postgres/01-schema.sql @@ -6,11 +6,13 @@ CREATE EXTENSION IF NOT EXISTS pg_trgm; CREATE TABLE IF NOT EXISTS files ( object_id UUID PRIMARY KEY, agent_id VARCHAR(255), + source VARCHAR(1000), project VARCHAR(255), timestamp TIMESTAMP WITH TIME ZONE, expiration TIMESTAMP WITH TIME ZONE, path TEXT, originating_object_id UUID, + originating_container_id UUID, -- for large container processing nesting_level INTEGER, file_creation_time TIMESTAMP WITH TIME ZONE, file_access_time TIMESTAMP WITH TIME ZONE, @@ -27,6 +29,7 @@ CREATE TABLE IF NOT EXISTS files ( CREATE TABLE IF NOT EXISTS files_enriched ( object_id UUID PRIMARY KEY, agent_id VARCHAR(255), + source VARCHAR(1000), project VARCHAR(255), timestamp TIMESTAMP WITH TIME ZONE, expiration TIMESTAMP WITH TIME ZONE, @@ -39,6 +42,7 @@ CREATE TABLE IF NOT EXISTS files_enriched ( is_plaintext BOOLEAN, is_container BOOLEAN, originating_object_id UUID, + originating_container_id UUID, -- for large container processing nesting_level INTEGER, file_creation_time TIMESTAMP WITH TIME ZONE, file_access_time TIMESTAMP WITH TIME ZONE, @@ -105,6 +109,7 @@ CREATE TABLE IF NOT EXISTS files_feedback ( CREATE TABLE IF NOT EXISTS files_enriched_dataset ( object_id UUID PRIMARY KEY, agent_id VARCHAR(255), + source VARCHAR(1000), project VARCHAR(255), timestamp TIMESTAMP WITH TIME ZONE, expiration TIMESTAMP WITH TIME ZONE, @@ -169,6 +174,9 @@ CREATE TABLE IF NOT EXISTS findings_triage_history ( username VARCHAR(255) NOT NULL, automated BOOLEAN, value VARCHAR(255) NOT NULL, + explanation VARCHAR(5000), + confidence REAL, + true_positive_context VARCHAR(5000), timestamp TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP ); @@ -256,6 +264,7 @@ SELECT ef.extension, ef.project, ef.agent_id, + ef.source, ef."timestamp"::timestamp with time zone FROM plaintext_content pc JOIN files_enriched ef ON pc.object_id = ef.object_id; @@ -268,7 +277,8 @@ CREATE OR REPLACE FUNCTION public.search_documents( project_name text DEFAULT NULL, start_date timestamp with time zone DEFAULT NULL, end_date timestamp with time zone DEFAULT NULL, - max_results integer DEFAULT 100 + max_results integer DEFAULT 100, + source_pattern text DEFAULT NULL ) RETURNS SETOF document_search_results STABLE LANGUAGE sql @@ -281,15 +291,13 @@ AS $$ ) as rn FROM document_search_results WHERE - ( - content_vector @@ plainto_tsquery('simple', search_query) - OR content ILIKE '%' || search_query || '%' - OR file_name ILIKE '%' || search_query || '%' - ) AND (path_pattern IS NULL OR path LIKE path_pattern) + content_vector @@ plainto_tsquery('simple', search_query) + AND (path_pattern IS NULL OR path LIKE path_pattern) AND (agent_pattern IS NULL OR agent_id LIKE agent_pattern) AND (project_name IS NULL OR project = project_name) AND (start_date IS NULL OR "timestamp" >= start_date) AND (end_date IS NULL OR "timestamp" <= end_date) + AND (source_pattern IS NULL OR source IS NULL OR source LIKE source_pattern) ) SELECT object_id, @@ -301,6 +309,7 @@ AS $$ extension, project, agent_id, + source, "timestamp" FROM ranked_chunks WHERE rn = 1 @@ -331,6 +340,160 @@ CREATE TABLE IF NOT EXISTS yara_rules ( ); +----------------------- +-- Agent Prompts +----------------------- +CREATE TABLE IF NOT EXISTS agent_prompts ( + name VARCHAR(255) NOT NULL PRIMARY KEY, + description TEXT, + prompt TEXT NOT NULL, + enabled BOOLEAN NOT NULL DEFAULT true, + created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP +); + + +----------------------- +-- FILE LINKINGS +----------------------- +CREATE TABLE IF NOT EXISTS file_linkings ( + linking_id BIGSERIAL PRIMARY KEY, + source VARCHAR(1000) NOT NULL, + file_path_1 TEXT NOT NULL, + file_path_2 TEXT NOT NULL, + link_type VARCHAR(255), -- Optional: to specify the type of relationship + created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, + UNIQUE(source, file_path_1, file_path_2) +); + +-- Create indexes for efficient lookups in both directions +CREATE INDEX IF NOT EXISTS idx_file_linkings_file_1 ON file_linkings(file_path_1); +CREATE INDEX IF NOT EXISTS idx_file_linkings_file_2 ON file_linkings(file_path_2); +CREATE INDEX IF NOT EXISTS idx_file_linkings_source ON file_linkings(source); + + +----------------------- +-- FILE LISTINGS +----------------------- +CREATE TABLE IF NOT EXISTS file_listings ( + listing_id BIGSERIAL PRIMARY KEY, + source VARCHAR(1000) NOT NULL, + path TEXT NOT NULL, + object_id UUID, + status VARCHAR(50) NOT NULL CHECK (status IN ('needs_to_be_collected', 'not_exists', 'collected', 'not_wanted')), + created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, + path_lower TEXT GENERATED ALWAYS AS (LOWER(path)) STORED, + UNIQUE(source, path_lower), + FOREIGN KEY (object_id) REFERENCES files_enriched(object_id) ON DELETE CASCADE +); + +-- Create indexes for efficient queries +CREATE INDEX IF NOT EXISTS idx_file_listings_source ON file_listings(source); +CREATE INDEX IF NOT EXISTS idx_file_listings_status ON file_listings(status); +CREATE INDEX IF NOT EXISTS idx_file_listings_object_id ON file_listings(object_id); +-- Create composite index for efficient path prefix queries +CREATE INDEX IF NOT EXISTS idx_file_listings_source_path ON file_listings(source, path); +-- Create trigram index for path pattern matching +CREATE INDEX IF NOT EXISTS idx_file_listings_path_trgm ON file_listings USING gist (path gist_trgm_ops); + +-- Helper functions for file browser hierarchical navigation +CREATE OR REPLACE FUNCTION get_path_depth(file_path text) +RETURNS integer AS $$ +BEGIN + -- Count forward slashes to determine depth + -- Root files (no slash) are depth 0, /folder/file is depth 1, etc. + IF file_path = '' OR file_path IS NULL THEN + RETURN 0; + END IF; + RETURN array_length(string_to_array(file_path, '/'), 1) - 1; +END; +$$ LANGUAGE plpgsql IMMUTABLE; + +CREATE OR REPLACE FUNCTION get_path_parent(file_path text) +RETURNS text AS $$ +BEGIN + -- Return parent path, handling edge cases + IF file_path IS NULL OR file_path = '' OR position('/' in file_path) = 0 THEN + RETURN ''; + END IF; + RETURN regexp_replace(file_path, '/[^/]*$', ''); +END; +$$ LANGUAGE plpgsql IMMUTABLE; + +CREATE OR REPLACE FUNCTION get_path_filename(file_path text) +RETURNS text AS $$ +BEGIN + -- Extract just the filename/folder name from full path + IF file_path IS NULL OR file_path = '' THEN + RETURN ''; + END IF; + RETURN regexp_replace(file_path, '^.*/', ''); +END; +$$ LANGUAGE plpgsql IMMUTABLE; + +CREATE OR REPLACE FUNCTION is_file_path(file_path text) +RETURNS boolean AS $$ +BEGIN + -- Simple heuristic: if path has an extension, it's likely a file + -- This isn't perfect but works for most cases + IF file_path IS NULL OR file_path = '' THEN + RETURN false; + END IF; + -- Check if the last part after the final slash contains a dot + RETURN get_path_filename(file_path) LIKE '%.%'; +END; +$$ LANGUAGE plpgsql IMMUTABLE; + +-- View for hierarchical file browser queries +-- This creates virtual folder entries for efficient navigation +CREATE OR REPLACE VIEW file_listings_hierarchy AS +WITH RECURSIVE folder_paths AS ( + -- Get all unique folder paths from file paths + SELECT DISTINCT + source, + get_path_parent(path) as folder_path, + get_path_depth(get_path_parent(path)) as depth + FROM file_listings + WHERE get_path_parent(path) != '' + + UNION + + -- Add parent folders recursively + SELECT + source, + get_path_parent(folder_path) as folder_path, + get_path_depth(get_path_parent(folder_path)) as depth + FROM folder_paths + WHERE get_path_parent(folder_path) != '' AND get_path_parent(folder_path) != folder_path +) +SELECT + source, + folder_path as path, + 'folder' as item_type, + null::uuid as object_id, + 'folder' as status, + depth, + get_path_parent(folder_path) as parent_path, + get_path_filename(folder_path) as name +FROM folder_paths +WHERE folder_path != '' + +UNION ALL + +SELECT + source, + path, + 'file' as item_type, + object_id, + status, + get_path_depth(path) as depth, + get_path_parent(path) as parent_path, + get_path_filename(path) as name +FROM file_listings; + + ----------------------- -- CREATE UPDATE FIELD TRIGGER ----------------------- @@ -362,6 +525,11 @@ CREATE OR REPLACE TRIGGER update_yara_rules_updated_at FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); +CREATE OR REPLACE TRIGGER update_agent_prompts_updated_at + BEFORE UPDATE ON agent_prompts + FOR EACH ROW + EXECUTE FUNCTION update_updated_at_column(); + CREATE OR REPLACE TRIGGER update_enrichments_updated_at BEFORE UPDATE ON enrichments FOR EACH ROW @@ -382,6 +550,15 @@ CREATE OR REPLACE TRIGGER update_files_enriched_dataset_updated_at FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); +CREATE OR REPLACE TRIGGER update_file_linkings_updated_at + BEFORE UPDATE ON file_linkings + FOR EACH ROW + EXECUTE FUNCTION update_updated_at_column(); + +CREATE OR REPLACE TRIGGER update_file_listings_updated_at + BEFORE UPDATE ON file_listings + FOR EACH ROW + EXECUTE FUNCTION update_updated_at_column(); ----------------------- @@ -397,3 +574,244 @@ CREATE TABLE IF NOT EXISTS workflows ( runtime_seconds REAL, start_time TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP ); + + +----------------------- +-- Container Processing Tracking +----------------------- +CREATE TABLE IF NOT EXISTS container_processing ( + container_id UUID NOT NULL PRIMARY KEY DEFAULT gen_random_uuid(), + container_type VARCHAR(50) NOT NULL, + original_filename VARCHAR(255), + original_size BIGINT, + agent_id VARCHAR(255), + source VARCHAR(1000), + project VARCHAR(255), + status VARCHAR(50) NOT NULL DEFAULT 'submitted', + total_files_extracted INTEGER DEFAULT 0, + total_bytes_extracted INTEGER DEFAULT 0, + submitted_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, + expiration TIMESTAMP WITH TIME ZONE, + processing_started_at TIMESTAMP WITH TIME ZONE, + processing_completed_at TIMESTAMP WITH TIME ZONE, + error_message TEXT, + workflows_completed INTEGER DEFAULT 0, + workflows_failed INTEGER DEFAULT 0, + workflows_total INTEGER DEFAULT 0, + total_bytes_processed INTEGER DEFAULT 0 +); + + +-- Create phoenix database +CREATE DATABASE phoenix; + + +----------------------- +-- Chromium schema/tables +----------------------- + +CREATE SCHEMA chromium; + +-- "urls" table in "History" file +CREATE TABLE IF NOT EXISTS chromium.history ( + id SERIAL PRIMARY KEY, + originating_object_id UUID, + agent_id VARCHAR(255), + source VARCHAR(1000), + project VARCHAR(255), + username TEXT, -- username extracted from user data directory, if applicable + browser TEXT, -- browser name extracted from user data directory, if applicable + + url TEXT, -- extracted from the Chromium DB + title TEXT, -- extracted from the Chromium DB + visit_count INTEGER, -- extracted from the Chromium DB + last_visit_time TIMESTAMP WITH TIME ZONE, -- extracted from the Chromium DB + + FOREIGN KEY (originating_object_id) REFERENCES files_enriched(object_id) ON DELETE CASCADE, + UNIQUE (source, username, browser, url, title, last_visit_time) +); + +-- "downloads" table in "History" file +CREATE TABLE IF NOT EXISTS chromium.downloads ( + id SERIAL PRIMARY KEY, + originating_object_id UUID, + agent_id VARCHAR(255), + source VARCHAR(1000), + project VARCHAR(255), + username TEXT, -- username extracted from user data directory, if applicable + browser TEXT, -- browser name extracted from user data directory, if applicable + + url TEXT, -- extracted from the Chromium DB + download_path TEXT, -- extracted from the Chromium DB `target_path` field + start_time TIMESTAMP WITH TIME ZONE, -- extracted from the Chromium DB + end_time TIMESTAMP WITH TIME ZONE, -- extracted from the Chromium DB + total_bytes INTEGER, -- extracted from the Chromium DB + + FOREIGN KEY (originating_object_id) REFERENCES files_enriched(object_id) ON DELETE CASCADE, + UNIQUE (source, username, browser, url, download_path, start_time) +); + +-- extracted from a Chromium "Local State" file +CREATE TABLE chromium.state_keys ( + id SERIAL PRIMARY KEY, + originating_object_id UUID, + agent_id VARCHAR(255), + source VARCHAR(1000), + project VARCHAR(255), + username TEXT, -- username extracted from user data directory, if applicable + browser TEXT, -- browser name extracted from user data directory, if applicable + + key_masterkey_guid UUID, -- associated masterkey GUID for key_bytes_enc + key_bytes_enc BYTEA, -- os_crypt.encrypted_key in Chromium `Local State` file (pre v127) + key_bytes_dec BYTEA, + key_is_decrypted BOOLEAN, + + app_bound_key_enc BYTEA, -- os_crypt.app_bound_encrypted_key in Chromium `Local State` file (post v127) + app_bound_key_system_masterkey_guid UUID, -- associated _system_ masterkey GUID for key_bytes_enc + app_bound_key_system_dec BYTEA, -- intermediate dec value after the SYSTEM key has been used + app_bound_key_user_masterkey_guid UUID, -- associated _user_ masterkey GUID for app_bound_key_system_dec + app_bound_key_user_dec BYTEA, -- intermediate dec value after the USER key has been used (before chromekey for v3) + app_bound_key_dec BYTEA, -- completely dec value + app_bound_key_is_decrypted BOOLEAN, + + FOREIGN KEY (originating_object_id) REFERENCES files_enriched(object_id) ON DELETE CASCADE, + UNIQUE (source, username, browser) +); + +-- AES keys extracted from a CNG "Google Chromekey1" CNG file from C:\ProgramData\Microsoft\Crypto\SystemKeys\ +-- Used in v3 of the Chromium ABE decryption +CREATE TABLE chromium.chrome_keys ( + id SERIAL PRIMARY KEY, + originating_object_id UUID, + agent_id VARCHAR(255), + source VARCHAR(1000), -- should only be one key per host/source + project VARCHAR(255), + + key_masterkey_guid UUID, -- associated _system_ masterkey GUID for key_bytes_enc + key_bytes_enc BYTEA, -- the raw DPAPI blob bytes from the CNG file + key_bytes_dec BYTEA, -- completely dec AES key value + key_is_decrypted BOOLEAN, + + FOREIGN KEY (originating_object_id) REFERENCES files_enriched(object_id) ON DELETE CASCADE, + UNIQUE (key_masterkey_guid) +); + +-- Create indexes for masterkey GUID lookups on state_keys +CREATE INDEX IF NOT EXISTS idx_state_keys_key_masterkey_guid ON chromium.state_keys(key_masterkey_guid) WHERE key_is_decrypted = FALSE; +CREATE INDEX IF NOT EXISTS idx_state_keys_app_bound_system_mk_guid ON chromium.state_keys(app_bound_key_system_masterkey_guid) WHERE length(app_bound_key_system_dec) = 0; +CREATE INDEX IF NOT EXISTS idx_state_keys_app_bound_user_mk_guid ON chromium.state_keys(app_bound_key_user_masterkey_guid) WHERE length(app_bound_key_user_dec) = 0; + +-- "logins" table in "Login Data" file +CREATE TABLE IF NOT EXISTS chromium.logins ( + id SERIAL PRIMARY KEY, + originating_object_id UUID, + agent_id VARCHAR(255), + source VARCHAR(1000), + project VARCHAR(255), + username TEXT, -- username extracted from user data directory, if applicable + browser TEXT, -- browser name extracted from user data directory, if applicable + + origin_url TEXT, -- extracted from the Chromium DB + username_value TEXT, -- extracted from the Chromium DB + signon_realm TEXT, -- extracted from the Chromium DB + date_created TIMESTAMP WITH TIME ZONE, -- extracted from the Chromium DB + date_last_used TIMESTAMP WITH TIME ZONE, -- extracted from the Chromium DB + date_password_modified TIMESTAMP WITH TIME ZONE, -- extracted from the Chromium DB + times_used INTEGER, -- extracted from the Chromium DB + + encryption_type TEXT, -- carved from the `password_value_enc` bytes - dpapi, key, or abe (app-bound-encryption) + masterkey_guid UUID, -- if encryption_type == dpapi, associated masterkey GUID + state_key_id INTEGER, -- if encryption_type != dpapi, linked to "id" in `chromium.state_keys` + is_decrypted BOOLEAN, + password_value_enc BYTEA, -- extracted from the Chromium DB `password_value` field + password_value_dec TEXT, + + FOREIGN KEY (originating_object_id) REFERENCES files_enriched(object_id) ON DELETE CASCADE, + FOREIGN KEY (state_key_id) REFERENCES chromium.state_keys(id) ON DELETE SET NULL, + UNIQUE (source, username, browser, origin_url, username_value) +); + +-- "cookies" table in "Cookies" file +CREATE TABLE IF NOT EXISTS chromium.cookies ( + id SERIAL PRIMARY KEY, + originating_object_id UUID, + agent_id VARCHAR(255), + source VARCHAR(1000), + project VARCHAR(255), + username TEXT, -- username extracted from user data directory, if applicable + browser TEXT, -- browser name extracted from user data directory, if applicable + + host_key TEXT, -- extracted from the Chromium DB + name TEXT, -- extracted from the Chromium DB + path TEXT, -- extracted from the Chromium DB + creation_utc TIMESTAMP WITH TIME ZONE, -- extracted from the Chromium DB + expires_utc TIMESTAMP WITH TIME ZONE, -- extracted from the Chromium DB + last_access_utc TIMESTAMP WITH TIME ZONE, -- extracted from the Chromium DB + last_update_utc TIMESTAMP WITH TIME ZONE, -- extracted from the Chromium DB + is_secure BOOLEAN, -- extracted from the Chromium DB + is_httponly BOOLEAN, -- extracted from the Chromium DB + is_persistent BOOLEAN, -- extracted from the Chromium DB + samesite TEXT, -- extracted from the Chromium DB, translated from int + source_port INTEGER, -- extracted from the Chromium DB + + encryption_type TEXT, -- carved from the `encrypted_value` field - dpapi, key, or abe (app-bound-encryption) + masterkey_guid UUID, -- if encryption_type == dpapi, associated masterkey GUID + state_key_id INTEGER, -- if encryption_type != dpapi, linked to "id" in `chromium.state_keys` + is_decrypted BOOLEAN, + value_enc BYTEA, -- extracted from the Chromium DB `encrypted_value` field + value_dec TEXT, + + FOREIGN KEY (originating_object_id) REFERENCES files_enriched(object_id) ON DELETE CASCADE, + FOREIGN KEY (state_key_id) REFERENCES chromium.state_keys(id) ON DELETE SET NULL, + UNIQUE (source, username, browser, host_key, name, path) +); + +-- DPAPI tables +CREATE SCHEMA dpapi; + +CREATE TABLE IF NOT EXISTS dpapi.masterkeys ( + id SERIAL PRIMARY KEY, + guid TEXT UNIQUE NOT NULL, + encrypted_key_usercred BYTEA, + encrypted_key_backup BYTEA, + plaintext_key BYTEA, + plaintext_key_sha1 BYTEA, + backup_key_guid TEXT, + masterkey_type TEXT DEFAULT 'unknown', + created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP +); + +CREATE TABLE IF NOT EXISTS dpapi.domain_backup_keys ( + id SERIAL PRIMARY KEY, + guid TEXT UNIQUE NOT NULL, + key_data BYTEA NOT NULL, + domain_controller TEXT, + created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP +); + +CREATE TABLE IF NOT EXISTS dpapi.system_credentials ( + id SERIAL PRIMARY KEY, + user_key BYTEA NOT NULL, + machine_key BYTEA NOT NULL, + created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, + UNIQUE (user_key, machine_key) +); + +-- Create triggers for DPAPI tables +CREATE OR REPLACE TRIGGER update_dpapi_masterkeys_updated_at + BEFORE UPDATE ON dpapi.masterkeys + FOR EACH ROW + EXECUTE FUNCTION update_updated_at_column(); + +CREATE OR REPLACE TRIGGER update_dpapi_domain_backup_keys_updated_at + BEFORE UPDATE ON dpapi.domain_backup_keys + FOR EACH ROW + EXECUTE FUNCTION update_updated_at_column(); + +CREATE OR REPLACE TRIGGER update_dpapi_system_credentials_updated_at + BEFORE UPDATE ON dpapi.system_credentials + FOR EACH ROW + EXECUTE FUNCTION update_updated_at_column(); \ No newline at end of file diff --git a/infra/prometheus/prometheus.yml b/infra/prometheus/prometheus.yml index f84c2bf..ba59e75 100644 --- a/infra/prometheus/prometheus.yml +++ b/infra/prometheus/prometheus.yml @@ -9,20 +9,128 @@ scrape_configs: - targets: ['traefik:8080'] - job_name: 'minio' - metrics_path: /minio/v2/metrics/cluster + metrics_path: /minio/metrics/v3/cluster/health scheme: http static_configs: - targets: ['minio:9000'] + - job_name: 'minio-api' + metrics_path: /minio/metrics/v3/api/requests + scheme: http + static_configs: + - targets: ['minio:9000'] + + - job_name: 'minio-bucket-api' + metrics_path: /minio/metrics/v3/bucket/api + scheme: http + static_configs: + - targets: ['minio:9000'] + + - job_name: 'minio-cluster-usage-buckets' + metrics_path: /minio/metrics/v3/cluster/usage/buckets + scheme: http + static_configs: + - targets: ['minio:9000'] + + - job_name: 'minio-cluster-usage-objects' + metrics_path: /minio/metrics/v3/cluster/usage/objects + scheme: http + static_configs: + - targets: ['minio:9000'] + + - job_name: 'minio-drives' + metrics_path: /minio/metrics/v3/system/drive + scheme: http + static_configs: + - targets: ['minio:9000'] + + - job_name: 'loki' + metrics_path: '/metrics' + static_configs: + - targets: ['loki:3100'] + - job_name: 'prometheus' metrics_path: '/prometheus/metrics' static_configs: - targets: ['localhost:9090'] - job_name: 'node-exporter' + metrics_path: '/metrics' static_configs: - targets: ['node-exporter:9100'] - job_name: 'cadvisor' + metrics_path: '/metrics' static_configs: - - targets: ['cadvisor:8080'] \ No newline at end of file + - targets: ['cadvisor:8080'] + + - job_name: 'postgres' + metrics_path: '/metrics' + static_configs: + - targets: ['postgres-exporter:9187'] + + - job_name: 'rabbitmq' + metrics_path: '/metrics' + static_configs: + - targets: ['rabbitmq:15692'] + + # Reserved for enterprise edition only? + # - job_name: 'hasura' + # metrics_path: '/v1/metrics' + # static_configs: + # - targets: ['hasura:8080'] + + - job_name: 'nemesis-web-api' + metrics_path: '/metrics' + static_configs: + - targets: ['web-api:9090'] + + - job_name: 'nemesis-noseyparker-scanner' + metrics_path: '/metrics' + static_configs: + - targets: ['noseyparker-scanner:9090'] + + - job_name: 'nemesis-dotnet-service' + metrics_path: '/metrics' + static_configs: + - targets: ['dotnet-service:9090'] + + - job_name: 'nemesis-file-enrichment' + metrics_path: '/metrics' + static_configs: + - targets: ['file-enrichment:9090'] + + - job_name: 'nemesis-alerting' + metrics_path: '/metrics' + static_configs: + - targets: ['alerting:9090'] + + - job_name: 'nemesis-agents' + metrics_path: '/metrics' + static_configs: + - targets: ['agents:9090'] + + - job_name: 'nemesis-housekeeping' + metrics_path: '/metrics' + static_configs: + - targets: ['housekeeping:9090'] + + - job_name: 'nemesis-document-conversion' + metrics_path: '/metrics' + static_configs: + - targets: ['document-conversion:9090'] + + - job_name: 'nemesis-gotenberg' + metrics_path: '/prometheus/metrics' + static_configs: + - targets: ['gotenberg:3000'] + + - job_name: 'nemesis-placement' + metrics_path: '/metrics' + static_configs: + - targets: ['placement:9090'] + + - job_name: 'nemesis-scheduler' + metrics_path: '/metrics' + static_configs: + - targets: ['scheduler:9090'] \ No newline at end of file diff --git a/infra/rabbitmq/enabled_plugins b/infra/rabbitmq/enabled_plugins new file mode 100644 index 0000000..cc972a4 --- /dev/null +++ b/infra/rabbitmq/enabled_plugins @@ -0,0 +1 @@ +[rabbitmq_management,rabbitmq_prometheus]. diff --git a/infra/tika/tika-config.xml b/infra/tika/tika-config.xml index c7016f6..47aed24 100644 --- a/infra/tika/tika-config.xml +++ b/infra/tika/tika-config.xml @@ -6,6 +6,64 @@ 10 5 300000 + + 1073741824 + + + + + + + + + eng + + + 300 + + + true + + + 3 + + + 300 + + + 4 + + + gray + + + triangle + + + 900 + + + true + + + + + + + + auto + true + true + + 536870912 + + + + + + + + \ No newline at end of file diff --git a/projects/triage/.vscode/settings.json b/libs/chromium/.vscode/settings.json similarity index 69% rename from projects/triage/.vscode/settings.json rename to libs/chromium/.vscode/settings.json index 34bd581..372f1cf 100644 --- a/projects/triage/.vscode/settings.json +++ b/libs/chromium/.vscode/settings.json @@ -6,15 +6,13 @@ "editor.formatOnSave": false }, "[python]": { - "editor.formatOnSave": true, "editor.codeActionsOnSave": { - "source.fixAll": "explicit", - "source.organizeImports": "explicit" + "source.fixAll.ruff": "explicit", + "source.organizeImports.ruff": "explicit" }, "editor.defaultFormatter": "charliermarsh.ruff" }, "autoDocstring.docstringFormat": "google", - "editor.formatOnSave": true, "files.exclude": { "**/.DS_Store": true, "**/.git": true, @@ -39,27 +37,18 @@ "**/*.egg-info/**": true, "**/build/**": true, "**/dist/**": true, - "**/node_modules/*/**": true, + "**/node_modules/*/**": true }, "python.analysis.diagnosticSeverityOverrides": { "reportMissingImports": "none", - "reportMissingModuleSource": "none", + "reportMissingModuleSource": "none" }, "python.analysis.useLibraryCodeForTypes": true, // Pyright - // "python.formatting.provider": "none", "python.languageServer": "Pylance", - "python.linting.enabled": true, - "python.linting.flake8Args": [ - "--max-line-length=240", - "--ignore=E203,E722,W503", - ], - "python.linting.flake8Enabled": true, - "python.linting.lintOnSave": true, - "python.linting.pylintEnabled": false, "python.testing.pytestArgs": [ "tests" ], "python.testing.unittestEnabled": false, "python.testing.pytestEnabled": true, - "python.analysis.typeCheckingMode": "basic", + "python.analysis.typeCheckingMode": "basic" } \ No newline at end of file diff --git a/libs/chromium/README.md b/libs/chromium/README.md new file mode 100644 index 0000000..f91b3ab --- /dev/null +++ b/libs/chromium/README.md @@ -0,0 +1,2 @@ +# Chromium +A Chromium helper library. \ No newline at end of file diff --git a/libs/chromium/chromium/__init__.py b/libs/chromium/chromium/__init__.py new file mode 100644 index 0000000..7458445 --- /dev/null +++ b/libs/chromium/chromium/__init__.py @@ -0,0 +1,25 @@ +"""Chromium utility library for Chromium focused operations.""" + +from .chromekey import retry_decrypt_chrome_keys_for_masterkey +from .cookies import process_chromium_cookies +from .helpers import convert_chromium_timestamp +from .history import process_chromium_history +from .local_state import ( + process_chromium_local_state, + retry_decrypt_state_keys_for_chromekey, + retry_decrypt_state_keys_for_masterkey, +) +from .logins import process_chromium_logins +from .retry import retry_decrypt_chromium_data + +__all__ = [ + "convert_chromium_timestamp", + "process_chromium_history", + "process_chromium_cookies", + "process_chromium_logins", + "process_chromium_local_state", + "retry_decrypt_chrome_keys_for_masterkey", + "retry_decrypt_chromium_data", + "retry_decrypt_state_keys_for_chromekey", + "retry_decrypt_state_keys_for_masterkey", +] diff --git a/libs/chromium/chromium/chromekey.py b/libs/chromium/chromium/chromekey.py new file mode 100644 index 0000000..b33754f --- /dev/null +++ b/libs/chromium/chromium/chromekey.py @@ -0,0 +1,219 @@ +"""Chrome Key and database operations.""" + +from uuid import UUID + +import psycopg +from common.logger import get_logger +from file_enrichment_modules.cng_file.cng_parser import check_bcrypt_key_blob, extract_final_key_material +from nemesis_dpapi import Blob, DpapiManager, MasterKeyNotDecryptedError, MasterKeyNotFoundError + +from .helpers import get_postgres_connection_str +from .local_state import retry_decrypt_state_keys_for_chromekey + +logger = get_logger(__name__) + + +async def _retry_state_keys_for_chromekey_after_decrypt(source: str, chromekey: bytes): + """Helper to retry state key decryption after a chromekey is decrypted. + + Import is done here to avoid circular dependency issues. + """ + return await retry_decrypt_state_keys_for_chromekey(source, chromekey) + + +async def retry_decrypt_chrome_key(chrome_key_id: int, dpapi_manager: DpapiManager, pg_conn) -> dict: + """Attempt to decrypt a single chrome_key record using currently available masterkeys. + + Args: + chrome_key_id: The ID of the chrome_key record to decrypt + dpapi_manager: DpapiManager instance for decryption + pg_conn: PostgreSQL connection + + Returns: + Dict with decryption results: { + "decrypted": bool, + "state_keys_result": dict (optional, if decryption succeeded) + } + """ + result = {"decrypted": False} + cng_key_blob_entropy = b'xT5rZW5qVVbrvpuA\x00' + + # Fetch the chrome_key record + with pg_conn.cursor() as cur: + cur.execute( + """ + SELECT id, source, key_masterkey_guid, key_bytes_enc, + key_bytes_dec, key_is_decrypted + FROM chromium.chrome_keys + WHERE id = %s + """, + (chrome_key_id,), + ) + row = cur.fetchone() + + if not row: + logger.warning("Chrome key not found", chrome_key_id=chrome_key_id) + return result + + (record_id, source, key_masterkey_guid, key_bytes_enc, key_bytes_dec, key_is_decrypted) = row + + # Skip if already decrypted + if key_is_decrypted: + return result + + # Try to decrypt the chrome key + if key_bytes_enc and len(key_bytes_enc) > 0: + try: + dpapi_blob = Blob.from_bytes(key_bytes_enc) + try: + decrypted_blob = await dpapi_manager.decrypt_blob(dpapi_blob, entropy=cng_key_blob_entropy) + if decrypted_blob: + # Check for BCRYPT_KEY_DATA_BLOB and log details + if check_bcrypt_key_blob(decrypted_blob): + + # Extract the final 32-byte key material + key_bytes_dec = extract_final_key_material(decrypted_blob) + + if key_bytes_dec: + key_is_decrypted = True + result["decrypted"] = True + logger.info( + "Successfully decrypted and extracted chrome_key material", + chrome_key_id=chrome_key_id, + masterkey_guid=dpapi_blob.masterkey_guid, + ) + + # Update database + with pg_conn.cursor() as cur: + cur.execute( + """ + UPDATE chromium.chrome_keys + SET key_bytes_dec = %s, key_is_decrypted = %s, + key_masterkey_guid = %s + WHERE id = %s + """, + (key_bytes_dec, key_is_decrypted, dpapi_blob.masterkey_guid, chrome_key_id), + ) + + # Commit the chrome_key update before trying state_keys + pg_conn.commit() + + # Now try to decrypt any state_keys waiting for this chromekey + try: + state_keys_result = await _retry_state_keys_for_chromekey_after_decrypt(source, key_bytes_dec) + result["state_keys_result"] = state_keys_result + logger.info( + "Completed retroactive state_key decryption for newly decrypted chromekey", + chrome_key_id=chrome_key_id, + source=source, + state_keys_result=state_keys_result, + ) + except Exception as e: + logger.warning( + "Error retrying state_keys after chromekey decryption", + chrome_key_id=chrome_key_id, + source=source, + error=str(e), + ) + else: + logger.warning( + "Failed to extract final key material from decrypted chrome_key", + chrome_key_id=chrome_key_id, + ) + + except (MasterKeyNotFoundError, MasterKeyNotDecryptedError): + # Masterkey still not available, skip silently + pass + except Exception as e: + logger.warning("Error decrypting chrome_key", chrome_key_id=chrome_key_id, error=str(e)) + + except Exception as e: + logger.warning("Error processing chrome_key", chrome_key_id=chrome_key_id, error=str(e)) + else: + # Commit even if no decryption happened (for consistency) + pg_conn.commit() + + return result + + +async def retry_decrypt_chrome_keys_for_masterkey( + masterkey_guid: UUID, dpapi_manager: DpapiManager, masterkey_type: str | None = None +) -> dict: + """Find all chrome_keys waiting for this masterkey and try to decrypt them. + + Args: + masterkey_guid: The GUID of the newly available masterkey + dpapi_manager: DpapiManager instance for decryption + masterkey_type: Optional masterkey type ('system', 'user', 'unknown') for optimization + + Returns: + Dict with statistics: { + "chrome_keys_attempted": int, + "chrome_keys_decrypted": int, + "errors": list + } + """ + result = {"chrome_keys_attempted": 0, "chrome_keys_decrypted": 0, "errors": []} + + # Chrome keys only use SYSTEM masterkeys, so skip if this is a USER key + if masterkey_type == "user": + logger.debug( + "Skipping chrome_key decryption for USER masterkey", + masterkey_guid=masterkey_guid, + masterkey_type=masterkey_type, + ) + return result + + conn_str = get_postgres_connection_str() + + try: + with psycopg.connect(conn_str) as pg_conn: + # Find all chrome_keys that might need this masterkey + with pg_conn.cursor() as cur: + # SYSTEM keys are used for chrome_keys, but if type is unknown, still try + query = """ + SELECT DISTINCT id FROM chromium.chrome_keys + WHERE key_masterkey_guid = %s AND key_is_decrypted = FALSE + """ + cur.execute(query, (masterkey_guid,)) + + chrome_key_ids = [row[0] for row in cur.fetchall()] + + logger.debug( + "Found chrome_keys potentially waiting for masterkey", + masterkey_guid=masterkey_guid, + masterkey_type=masterkey_type, + count=len(chrome_key_ids), + ) + + # Try to decrypt each chrome_key + for chrome_key_id in chrome_key_ids: + result["chrome_keys_attempted"] += 1 + try: + decrypt_result = await retry_decrypt_chrome_key(chrome_key_id, dpapi_manager, pg_conn) + + # Check if decryption succeeded + if decrypt_result["decrypted"]: + result["chrome_keys_decrypted"] += 1 + + except Exception as e: + error_msg = f"Error processing chrome_key {chrome_key_id}: {str(e)}" + logger.warning("Failed to retry decrypt chrome_key", chrome_key_id=chrome_key_id, error=str(e)) + result["errors"].append(error_msg) + + logger.info( + "Completed retroactive chrome_key decryption for masterkey", + masterkey_guid=masterkey_guid, + attempted=result["chrome_keys_attempted"], + decrypted=result["chrome_keys_decrypted"], + errors=len(result["errors"]), + ) + + except Exception as e: + error_msg = f"Database error during retroactive chrome_key decryption: {str(e)}" + logger.exception( + "Error in retry_decrypt_chrome_keys_for_masterkey", masterkey_guid=masterkey_guid, error=str(e) + ) + result["errors"].append(error_msg) + + return result diff --git a/libs/chromium/chromium/cookies.py b/libs/chromium/chromium/cookies.py new file mode 100644 index 0000000..1326de4 --- /dev/null +++ b/libs/chromium/chromium/cookies.py @@ -0,0 +1,282 @@ +"""Chromium Cookies file parsing and database operations.""" + +import asyncio +import sqlite3 + +import psycopg +from common.logger import get_logger +from common.state_helpers import get_file_enriched +from common.storage import StorageMinio +from nemesis_dpapi import Blob, DpapiManager + +from .helpers import ( + convert_chromium_timestamp, + decrypt_chrome_string, + detect_encryption_type, + get_postgres_connection_str, + get_state_key_bytes, + get_state_key_id, + is_sqlite3, + parse_chromium_file_path, + try_decrypt_with_all_keys, +) + +logger = get_logger(__name__) + + +def process_chromium_cookies( + object_id: str, file_path: str | None = None, dpapi_manager: DpapiManager | None = None +) -> None: + """Process Chromium Cookies file and insert cookies into database. + + Args: + object_id: The object ID of the Cookies file + file_path: Optional path to already downloaded file + """ + logger.info("Processing Chromium Cookies file", object_id=object_id) + + file_enriched = get_file_enriched(object_id) + + # Extract username and browser from file path + username, browser = parse_chromium_file_path(file_enriched.path or "") + logger.debug("[process_chromium_cookies]", username=username, browser=browser) + + # Get database file + if file_path: + db_path = file_path + else: + storage = StorageMinio() + with storage.download(file_enriched.object_id) as temp_file: + db_path = temp_file.name + + if is_sqlite3(db_path) is False: + logger.warning("File is not a valid SQLite3 database", object_id=object_id, file_path=file_enriched.path) + return + + conn_str = get_postgres_connection_str() + with psycopg.connect(conn_str) as pg_conn: + _insert_cookies(file_enriched, username, browser, db_path, pg_conn, dpapi_manager) + + logger.debug("Completed processing Chromium Cookies", object_id=object_id) + + +def _translate_samesite(samesite_int: int) -> str: + """Translate samesite integer value to string. + + Args: + samesite_int: Integer value from database + + Returns: + String representation of samesite value + """ + samesite_map = {-1: "Unspecified", 0: "None", 1: "Lax", 2: "Strict"} + return samesite_map.get(samesite_int, "Unknown") + + +def _insert_cookies( + file_enriched, username: str | None, browser: str, db_path: str, pg_conn, dpapi_manager: DpapiManager | None = None +) -> None: + """Extract cookies from Cookies database and insert into chromium.cookies table.""" + try: + # Read from SQLite + with sqlite3.connect(db_path) as conn: + conn.text_factory = bytes # Get raw bytes, we'll handle text decoding manually + cursor = conn.cursor() + + # Query cookies table + cursor.execute(""" + SELECT host_key, name, path, creation_utc, expires_utc, last_access_utc, + last_update_utc, is_secure, is_httponly, is_persistent, samesite, + source_port, encrypted_value + FROM cookies + """) + rows = cursor.fetchall() + + if not rows: + return + + # Prepare data for PostgreSQL + cookies_data = [] + for row in rows: + ( + host_key, + name, + path, + creation_utc, + expires_utc, + last_access_utc, + last_update_utc, + is_secure, + is_httponly, + is_persistent, + samesite, + source_port, + encrypted_value, + ) = row + + # Decode text fields from bytes (since we set text_factory = bytes) + host_key = host_key.decode("utf-8", errors="replace") if host_key else None + name = name.decode("utf-8", errors="replace") if name else None + path = path.decode("utf-8", errors="replace") if path else None + + # encrypted_value is already binary (what we want) + + if encrypted_value is None: + encrypted_value = b"" + + # Detect encryption type and get masterkey GUID (if applicable) + encryption_type, masterkey_guid = detect_encryption_type(encrypted_value) + is_decrypted = False + value_dec = None + if masterkey_guid and dpapi_manager: + try: + value_dec_bytes = asyncio.run(dpapi_manager.decrypt_blob(Blob.from_bytes(encrypted_value))) + if value_dec_bytes: + value_dec = value_dec_bytes.decode("utf-8", errors="replace") + is_decrypted = True + except: + pass + + # Get state key ID for key/abe encryption + state_key_id = None + if encryption_type in ["key", "abe"]: + # Try primary approach: get state key based on username/browser + if username: # Only try primary approach if username was extracted + state_key_id = get_state_key_id(file_enriched.source, username, browser, pg_conn) + if state_key_id: + # Retrieve the pre-processed state key for decryption + state_key_bytes = get_state_key_bytes(state_key_id, encryption_type, pg_conn) + if state_key_bytes: + try: + value_dec_bytes = decrypt_chrome_string( + encrypted_value, state_key_bytes, encryption_type + ) + if value_dec_bytes: + # For cookies, may need to strip offset bytes depending on version + if encryption_type == "abe" and len(value_dec_bytes) > 32: + # v20 cookies typically have 32-byte offset + value_dec_bytes = value_dec_bytes[32:] + elif encryption_type == "key" and len(value_dec_bytes) > 48: + # v10/v11 cookies may have 32-byte prefix + 16-byte suffix + value_dec_bytes = value_dec_bytes[32:-16] + elif encryption_type == "key" and len(value_dec_bytes) > 16: + # Or just 16-byte suffix + value_dec_bytes = value_dec_bytes[:-16] + + value_dec = value_dec_bytes.decode("utf-8", errors="replace") + is_decrypted = True + except Exception as e: + logger.warning( + "Failed to decrypt cookie with state key", + state_key_id=state_key_id, + encryption_type=encryption_type, + error=str(e), + ) + + # Backup approach: try all state keys from the same source if primary failed + if not is_decrypted: + logger.debug( + "Primary decryption failed, trying backup approach with all keys from source", + source=file_enriched.source, + encryption_type=encryption_type, + ) + backup_decrypted_bytes, backup_state_key_id = try_decrypt_with_all_keys( + encrypted_value, file_enriched.source, encryption_type, pg_conn + ) + if backup_decrypted_bytes and backup_state_key_id: + # Apply the same offset handling as above + if encryption_type == "abe" and len(backup_decrypted_bytes) > 32: + # v20 cookies typically have 32-byte offset + backup_decrypted_bytes = backup_decrypted_bytes[32:] + elif encryption_type == "key" and len(backup_decrypted_bytes) > 48: + # v10/v11 cookies may have 32-byte prefix + 16-byte suffix + backup_decrypted_bytes = backup_decrypted_bytes[32:-16] + elif encryption_type == "key" and len(backup_decrypted_bytes) > 16: + # Or just 16-byte suffix + backup_decrypted_bytes = backup_decrypted_bytes[:-16] + + value_dec = backup_decrypted_bytes.decode("utf-8", errors="replace") + is_decrypted = True + state_key_id = backup_state_key_id + logger.debug( + "Successfully decrypted cookie using backup approach", state_key_id=backup_state_key_id + ) + + cookie_data = { + "originating_object_id": file_enriched.object_id, + "agent_id": file_enriched.agent_id, + "source": file_enriched.source, + "project": file_enriched.project, + "username": username, + "browser": browser, + "host_key": host_key, + "name": name, + "path": path, + "creation_utc": convert_chromium_timestamp(creation_utc), + "expires_utc": convert_chromium_timestamp(expires_utc), + "last_access_utc": convert_chromium_timestamp(last_access_utc), + "last_update_utc": convert_chromium_timestamp(last_update_utc), + "is_secure": bool(is_secure), + "is_httponly": bool(is_httponly), + "is_persistent": bool(is_persistent), + "samesite": _translate_samesite(samesite) if samesite is not None else "Unknown", + "source_port": source_port, + "encryption_type": encryption_type, + "masterkey_guid": masterkey_guid, + "state_key_id": state_key_id, + "is_decrypted": is_decrypted, + "value_enc": encrypted_value, + "value_dec": value_dec, + } + cookies_data.append(cookie_data) + + # Insert into PostgreSQL + with pg_conn.cursor() as cur: + insert_sql = """ + INSERT INTO chromium.cookies + (originating_object_id, agent_id, source, project, username, browser, + host_key, name, path, creation_utc, expires_utc, last_access_utc, + last_update_utc, is_secure, is_httponly, is_persistent, samesite, + source_port, encryption_type, masterkey_guid, state_key_id, + is_decrypted, value_enc, value_dec) + VALUES (%(originating_object_id)s, %(agent_id)s, %(source)s, %(project)s, + %(username)s, %(browser)s, %(host_key)s, %(name)s, %(path)s, + %(creation_utc)s, %(expires_utc)s, %(last_access_utc)s, + %(last_update_utc)s, %(is_secure)s, %(is_httponly)s, %(is_persistent)s, + %(samesite)s, %(source_port)s, %(encryption_type)s, %(masterkey_guid)s, + %(state_key_id)s, %(is_decrypted)s, %(value_enc)s, %(value_dec)s) + ON CONFLICT (source, username, browser, host_key, name, path) + DO UPDATE SET + host_key = EXCLUDED.host_key, + name = EXCLUDED.name, + path = EXCLUDED.path, + creation_utc = EXCLUDED.creation_utc, + expires_utc = EXCLUDED.expires_utc, + last_access_utc = EXCLUDED.last_access_utc, + last_update_utc = EXCLUDED.last_update_utc, + is_secure = EXCLUDED.is_secure, + is_httponly = EXCLUDED.is_httponly, + is_persistent = EXCLUDED.is_persistent, + samesite = EXCLUDED.samesite, + source_port = EXCLUDED.source_port, + encryption_type = EXCLUDED.encryption_type, + masterkey_guid = EXCLUDED.masterkey_guid, + state_key_id = EXCLUDED.state_key_id, + is_decrypted = EXCLUDED.is_decrypted, + value_enc = EXCLUDED.value_enc, + value_dec = EXCLUDED.value_dec + """ + + cur.executemany(insert_sql, cookies_data) + pg_conn.commit() + + logger.info("Inserted cookies into database", count=len(cookies_data)) + + except Exception as e: + logger.exception( + e, + "Error processing Cookies", + object_id=file_enriched.object_id, + file_path=file_enriched.path, + ) + raise diff --git a/libs/chromium/chromium/helpers.py b/libs/chromium/chromium/helpers.py new file mode 100644 index 0000000..2de8d98 --- /dev/null +++ b/libs/chromium/chromium/helpers.py @@ -0,0 +1,482 @@ +"""Helper functions for Chromium data processing.""" + +import re +import struct +from datetime import UTC, datetime, timedelta + +import psycopg +from common.db import get_postgres_connection_str +from common.logger import get_logger +from Crypto.Cipher import AES, ChaCha20_Poly1305 +from nemesis_dpapi import Blob + +logger = get_logger(__name__) + +def is_sqlite3(filename): + try: + with open(filename, "rb") as f: + header = f.read(16) + return header.startswith(b"SQLite format 3\0") + except OSError: + return False + + + +def convert_chromium_timestamp(timestamp: int, str_format: bool = False) -> datetime | str | None: + """Convert Chromium timestamp to datetime. + + Args: + timestamp: Chromium timestamp (microseconds since 1601-01-01) + str_format: Whether to return the timestamp in a string iso format (default is a datetime) + + Returns: + datetime object or None if invalid + """ + if not timestamp or timestamp == 0: + return None + + try: + epoch = datetime(1601, 1, 1, tzinfo=UTC) + dt = epoch + timedelta(microseconds=timestamp) + if str_format: + return dt.isoformat() + else: + return dt + except (ValueError, OverflowError): + return None + + +def parse_chromium_file_path(file_path: str) -> tuple[str | None, str]: + """Extract username and browser from Chromium file path. + + Args: + file_path: Path to the Chromium file + + Returns: + Tuple of (username, browser_name) + """ + + # Chrome/Edge/Brave pattern + match = re.search( + r".*/(?P[^/]+)/AppData/Local/(?:Google|Microsoft|BraveSoftware)/(?PChrome|Edge|Brave-Browser)/", + file_path, + re.IGNORECASE, + ) + + if match: + username = match.group("username").lower() + browser_str = match.group("browser").lower() + + if "chrome" in browser_str: + return username, "chrome" + elif "edge" in browser_str: + return username, "edge" + elif "brave" in browser_str: + return username, "brave" + + # Opera pattern + match = re.search(r".*/(?P[^/]+)/AppData/Roaming/Opera Software/Opera Stable/", file_path, re.IGNORECASE) + + if match: + username = match.group("username").lower() + return username, "opera" + + return None, "unknown" + + +def detect_encryption_type(encrypted_value: bytes) -> tuple[str, str | None]: + """Detect encryption type and extract masterkey GUID if applicable. + + Args: + encrypted_value: Raw encrypted value bytes + + Returns: + Tuple of (encryption_type, masterkey_guid) + """ + if not encrypted_value or len(encrypted_value) < 4: + return "unknown", None + + # Check for DPAPI (first 4 bytes are \x01\x00\x00\x00) + if encrypted_value[:4] == b"\x01\x00\x00\x00": + try: + blob = Blob.from_bytes(encrypted_value) + return "dpapi", str(blob.masterkey_guid) + except Exception as e: + raise Exception(f"Found DPAPI app bound key, but couldn't parse blob: {str(e)}") from e + + # Check for key-based encryption (v10, v11) + if len(encrypted_value) >= 3: + prefix = encrypted_value[:3] + try: + prefix_str = prefix.decode("ascii") + if prefix_str in ["v10", "v11"]: + return "key", None + elif prefix_str == "v20": + return "abe", None + except UnicodeDecodeError: + pass + + return "unknown", None + + +def get_state_key_id(source: str, username: str | None, browser: str, pg_conn=None) -> int | None: + """Get state key ID for key/abe encryption types. + + Args: + source: Source value + username: Username value + browser: Browser value + pg_conn: existing Postgres connection + + Returns: + State key ID if found, None otherwise + """ + if pg_conn: + try: + with pg_conn.cursor() as cur: + cur.execute( + "SELECT id FROM chromium.state_keys WHERE source = %s AND username = %s AND browser = %s", + (source, username, browser), + ) + result = cur.fetchone() + return result[0] if result else None + except Exception as e: + logger.warning("Failed to lookup state key ID", error=str(e)) + return None + else: + try: + conn_str = get_postgres_connection_str() + with psycopg.connect(conn_str) as pg_conn: + with pg_conn.cursor() as cur: + cur.execute( + "SELECT id FROM chromium.state_keys WHERE source = %s AND username = %s AND browser = %s", + (source, username, browser), + ) + result = cur.fetchone() + return result[0] if result else None + except Exception as e: + logger.warning("Failed to lookup state key ID", error=str(e)) + return None + + +def get_state_key_bytes(state_key_id: int, encryption_type: str, pg_conn=None) -> bytes | None: + """Get decrypted state key bytes for key/abe encryption types. + + Args: + state_key_id: State key ID + encryption_type: Either 'key' or 'abe' + pg_conn: existing Postgres connection + + Returns: + Decrypted key bytes if found and decrypted, None otherwise + """ + if encryption_type not in ["key", "abe"]: + return None + + column_name = "key_bytes_dec" if encryption_type == "key" else "app_bound_key_dec" + is_decrypted_col = "key_is_decrypted" if encryption_type == "key" else "app_bound_key_is_decrypted" + + if pg_conn: + try: + with pg_conn.cursor() as cur: + cur.execute( + f"SELECT {column_name}, {is_decrypted_col} FROM chromium.state_keys WHERE id = %s", + (state_key_id,), + ) + result = cur.fetchone() + if result and result[1]: # Check if is_decrypted is True + return result[0] + return None + except Exception as e: + logger.warning("Failed to lookup state key bytes", error=str(e)) + return None + else: + try: + conn_str = get_postgres_connection_str() + with psycopg.connect(conn_str) as pg_conn: + with pg_conn.cursor() as cur: + cur.execute( + f"SELECT {column_name}, {is_decrypted_col} FROM chromium.state_keys WHERE id = %s", + (state_key_id,), + ) + result = cur.fetchone() + if result and result[1]: # Check if is_decrypted is True + return result[0] + return None + except Exception as e: + logger.warning("Failed to lookup state key bytes", error=str(e)) + return None + + +def get_all_state_keys_from_source(source: str, pg_conn=None) -> list[dict]: + """Get all decrypted state keys from the same source. + + Args: + source: Source value + pg_conn: existing Postgres connection + + Returns: + List of dictionaries containing state key information + """ + state_keys = [] + + if pg_conn: + try: + with pg_conn.cursor() as cur: + cur.execute( + """SELECT id, username, browser, key_bytes_dec, key_is_decrypted, + app_bound_key_dec, app_bound_key_is_decrypted + FROM chromium.state_keys WHERE source = %s""", + (source,), + ) + results = cur.fetchall() + for result in results: + state_key_info = { + "id": result[0], + "username": result[1], + "browser": result[2], + "key_bytes_dec": result[3] if result[4] else None, # Only if decrypted + "app_bound_key_dec": result[5] if result[6] else None, # Only if decrypted + } + state_keys.append(state_key_info) + return state_keys + except Exception as e: + logger.warning("Failed to lookup all state keys from source", error=str(e)) + return [] + else: + try: + conn_str = get_postgres_connection_str() + with psycopg.connect(conn_str) as pg_conn: + with pg_conn.cursor() as cur: + cur.execute( + """SELECT id, username, browser, key_bytes_dec, key_is_decrypted, + app_bound_key_dec, app_bound_key_is_decrypted + FROM chromium.state_keys WHERE source = %s""", + (source,), + ) + results = cur.fetchall() + for result in results: + state_key_info = { + "id": result[0], + "username": result[1], + "browser": result[2], + "key_bytes_dec": result[3] if result[4] else None, # Only if decrypted + "app_bound_key_dec": result[5] if result[6] else None, # Only if decrypted + } + state_keys.append(state_key_info) + return state_keys + except Exception as e: + logger.warning("Failed to lookup all state keys from source", error=str(e)) + return [] + + +def is_valid_text(data: bytes) -> bool: + """Check if decrypted bytes represent valid ASCII or UTF-8 text. + + Args: + data: Bytes to validate + + Returns: + True if data is valid text, False otherwise + """ + if not data: + return False + + try: + # Try to decode as UTF-8 + text = data.decode("utf-8") + # Check if it contains mostly printable characters + printable_chars = sum(1 for c in text if c.isprintable() or c.isspace()) + return printable_chars / len(text) > 0.8 # At least 80% printable + except UnicodeDecodeError: + try: + # Try ASCII as fallback + text = data.decode("ascii") + printable_chars = sum(1 for c in text if c in "\x20-\x7e\t\n\r") + return printable_chars / len(text) > 0.8 + except UnicodeDecodeError: + return False + + +def try_decrypt_with_all_keys( + encrypted_value: bytes, source: str, encryption_type: str, pg_conn=None +) -> tuple[bytes | None, int | None]: + """Try to decrypt with all available state keys from the same source. + + Args: + encrypted_value: Raw encrypted value bytes + source: Source value to look up keys from + encryption_type: Either 'key' or 'abe' + pg_conn: existing Postgres connection + + Returns: + Tuple of (decrypted_bytes, state_key_id) if successful, (None, None) otherwise + """ + if encryption_type not in ["key", "abe"]: + return None, None + + state_keys = get_all_state_keys_from_source(source, pg_conn) + + for state_key in state_keys: + key_bytes = state_key.get("key_bytes_dec") if encryption_type == "key" else state_key.get("app_bound_key_dec") + + if not key_bytes: + continue + + try: + decrypted_bytes = decrypt_chrome_string(encrypted_value, key_bytes, encryption_type) + if decrypted_bytes: + # Apply offset handling for cookies + if encryption_type == "abe" and len(decrypted_bytes) > 32: + # v20 cookies typically have 32-byte offset + test_bytes = decrypted_bytes[32:] + elif encryption_type == "key" and len(decrypted_bytes) > 48: + # v10/v11 cookies may have 32-byte prefix + 16-byte suffix + test_bytes = decrypted_bytes[32:-16] + elif encryption_type == "key" and len(decrypted_bytes) > 16: + # Or just 16-byte suffix + test_bytes = decrypted_bytes[:-16] + else: + test_bytes = decrypted_bytes + + # Check if the result is valid text + if is_valid_text(test_bytes): + logger.debug( + "Successfully decrypted with backup key", + state_key_id=state_key["id"], + username=state_key["username"], + browser=state_key["browser"], + ) + return decrypted_bytes, state_key["id"] + except Exception: + # Continue trying other keys + continue + + return None, None + + +def byte_xor(ba1, ba2): + return bytes([_a ^ _b for _a, _b in zip(ba1, ba2)]) + +def parse_abe_blob(abe_data: bytes, chromekey: bytes | None = None) -> dict | None: + """Parse ABE (App-Bound Encryption) blob data. + + Args: + abe_data: Raw ABE blob bytes + + Returns: + Dictionary containing parsed ABE data, None if parsing fails + """ + try: + abe_parsed = {} + header_len = struct.unpack(" bytes | None: + """Derive ABE key from parsed ABE data. + + Args: + abe_data: Parsed ABE data dictionary + + Returns: + Derived ABE key bytes, None if derivation fails + """ + try: + if abe_data["version"] == 1: + cipher = AES.new( + bytes.fromhex("B31C6E241AC846728DA9C1FAC4936651CFFB944D143AB816276BCC6DA0284787"), + AES.MODE_GCM, + nonce=abe_data["iv"], + ) + elif abe_data["version"] == 2: + cipher = ChaCha20_Poly1305.new( + key=bytes.fromhex("E98F37D7F4E1FA433D19304DC2258042090E2D1D7EEA7670D41F738D08729660"), + nonce=abe_data["iv"], + ) + elif abe_data["version"] == 3: + if abe_data["xored_aes_key"]: + cipher = AES.new(abe_data["xored_aes_key"], AES.MODE_GCM, nonce=abe_data["iv"]) + else: + # Version 3 requires CNG decryption of encrypted AES key + logger.warning("xored_aes_key not present, ABE version 3 requires CNG decrypted 'Google Chromekey1'") + return None + else: + logger.warning("Unknown ABE version", version=abe_data["version"]) + return None + + return cipher.decrypt_and_verify(abe_data["cipherdata"], abe_data["tag"]) + except Exception as e: + logger.warning("Failed to derive ABE key", error=str(e)) + return None + + +def decrypt_chrome_string(encrypted_data: bytes, key_bytes: bytes, encryption_type: str) -> bytes | None: + """Decrypt Chrome encrypted string using key or ABE encryption. + + Args: + encrypted_data: Raw encrypted data bytes + key_bytes: Decrypted key bytes (BME key for 'key', ABE key for 'abe') + encryption_type: Either 'key' or 'abe' + + Returns: + Decrypted bytes, None if decryption fails + """ + if not encrypted_data or len(encrypted_data) < 3: + return None + + try: + if encryption_type == "key" and encrypted_data[:3] in [b"v10", b"v11"]: + # Version|IV|ciphertext, 4|12| + iv = encrypted_data[3 : 3 + 12] + ciphertext = encrypted_data[15:] + cipher = AES.new(key_bytes, AES.MODE_GCM, iv) + return cipher.decrypt(ciphertext) + + elif encryption_type == "abe" and encrypted_data[:3] == b"v20": + # Version|IV|ciphertext|tag, 3|12||16 bytes + iv = encrypted_data[3 : 3 + 12] + ciphertext = encrypted_data[15:-16] + tag = encrypted_data[-16:] + cipher = AES.new(key_bytes, AES.MODE_GCM, iv) + decrypted = cipher.decrypt_and_verify(ciphertext, tag) + # v20 cookies have 32-byte offset, but this varies by data type + return decrypted + else: + logger.warning("Unsupported encryption format", prefix=encrypted_data[:3], encryption_type=encryption_type) + return None + + except Exception as e: + logger.warning("Failed to decrypt Chrome string", error=str(e)) + return None diff --git a/libs/chromium/chromium/history.py b/libs/chromium/chromium/history.py new file mode 100644 index 0000000..6e7184b --- /dev/null +++ b/libs/chromium/chromium/history.py @@ -0,0 +1,165 @@ +"""Chromium History file parsing and database operations.""" + +import sqlite3 + +import psycopg +from common.logger import get_logger +from common.state_helpers import get_file_enriched +from common.storage import StorageMinio + +from .helpers import convert_chromium_timestamp, get_postgres_connection_str, parse_chromium_file_path + +logger = get_logger(__name__) + + +def process_chromium_history(object_id: str, file_path: str | None = None) -> None: + """Process Chromium History file and insert URLs and downloads into database. + + Args: + object_id: The object ID of the History file + file_path: Optional path to already downloaded file + """ + logger.info("Processing Chromium History file", object_id=object_id) + + file_enriched = get_file_enriched(object_id) + + # Extract username and browser from file path + username, browser = parse_chromium_file_path(file_enriched.path or "") + logger.debug("[process_chromium_history]", username=username, browser=browser) + + # Get database file + if file_path: + db_path = file_path + else: + storage = StorageMinio() + with storage.download(file_enriched.object_id) as temp_file: + db_path = temp_file.name + + # Process both tables + _insert_history_urls(object_id, file_enriched, username, browser, db_path) + _insert_history_downloads(object_id, file_enriched, username, browser, db_path) + + logger.debug("Completed processing Chromium History", object_id=object_id) + + +def _insert_history_urls(object_id: str, file_enriched, username: str | None, browser: str, db_path: str) -> None: + """Extract URLs from History and insert into chromium.history table.""" + try: + # Read from SQLite + with sqlite3.connect(db_path) as conn: + conn.text_factory = lambda x: x.decode("utf-8", errors="replace") + cursor = conn.cursor() + + cursor.execute("SELECT url, title, visit_count, last_visit_time FROM urls") + rows = cursor.fetchall() + + if not rows: + return + + # Prepare data for PostgreSQL + urls_data = [] + for url, title, visit_count, last_visit_time in rows: + urls_data.append( + { + "originating_object_id": file_enriched.object_id, + "agent_id": file_enriched.agent_id, + "source": file_enriched.source, + "project": file_enriched.project, + "username": username, + "browser": browser, + "url": url, + "title": title, + "visit_count": visit_count, + "last_visit_time": convert_chromium_timestamp(last_visit_time), + } + ) + + # Insert into PostgreSQL + conn_str = get_postgres_connection_str() + with psycopg.connect(conn_str) as pg_conn: + with pg_conn.cursor() as cur: + insert_sql = """ + INSERT INTO chromium.history + (originating_object_id, agent_id, source, project, username, browser, + url, title, visit_count, last_visit_time) + VALUES (%(originating_object_id)s, %(agent_id)s, %(source)s, %(project)s, + %(username)s, %(browser)s, %(url)s, %(title)s, %(visit_count)s, %(last_visit_time)s) + ON CONFLICT (source, username, browser, url, title, last_visit_time) + DO UPDATE SET + url = EXCLUDED.url, + title = EXCLUDED.title, + visit_count = EXCLUDED.visit_count, + last_visit_time = EXCLUDED.last_visit_time + """ + + cur.executemany(insert_sql, urls_data) + pg_conn.commit() + + logger.info("Inserted URLs into database", count=len(urls_data)) + + except Exception as e: + logger.exception("Error processing History URLs", error=str(e)) + raise + + +def _insert_history_downloads(object_id: str, file_enriched, username: str | None, browser: str, db_path: str) -> None: + """Extract downloads from History and insert into chromium_downloads table.""" + try: + # Read from SQLite + with sqlite3.connect(db_path) as conn: + conn.text_factory = lambda x: x.decode("utf-8", errors="replace") + cursor = conn.cursor() + + cursor.execute("SELECT tab_url, target_path, start_time, end_time, total_bytes FROM downloads") + rows = cursor.fetchall() + + if not rows: + return + + # Prepare data for PostgreSQL + downloads_data = [] + for tab_url, target_path, start_time, end_time, total_bytes in rows: + downloads_data.append( + { + "originating_object_id": file_enriched.object_id, + "agent_id": file_enriched.agent_id, + "source": file_enriched.source, + "project": file_enriched.project, + "username": username, + "browser": browser, + "url": tab_url, + "download_path": target_path, + "start_time": convert_chromium_timestamp(start_time), + "end_time": convert_chromium_timestamp(end_time), + "total_bytes": total_bytes, + } + ) + + # Insert into PostgreSQL + conn_str = get_postgres_connection_str() + with psycopg.connect(conn_str) as pg_conn: + with pg_conn.cursor() as cur: + insert_sql = """ + INSERT INTO chromium.downloads + (originating_object_id, agent_id, source, project, username, browser, + url, download_path, start_time, end_time, total_bytes) + VALUES (%(originating_object_id)s, %(agent_id)s, %(source)s, %(project)s, + %(username)s, %(browser)s, %(url)s, %(download_path)s, + %(start_time)s, %(end_time)s, %(total_bytes)s) + ON CONFLICT (source, username, browser, url, download_path, start_time) + DO UPDATE SET + url = EXCLUDED.url, + download_path = EXCLUDED.download_path, + start_time = EXCLUDED.start_time, + end_time = EXCLUDED.end_time, + total_bytes = EXCLUDED.total_bytes + """ + + cur.executemany(insert_sql, downloads_data) + pg_conn.commit() + + logger.info("Inserted downloads into database", count=len(downloads_data)) + + except Exception as e: + logger.exception("Error processing History downloads", error=str(e)) + raise diff --git a/libs/chromium/chromium/local_state.py b/libs/chromium/chromium/local_state.py new file mode 100644 index 0000000..1975a8e --- /dev/null +++ b/libs/chromium/chromium/local_state.py @@ -0,0 +1,968 @@ +"""Chromium Local State file parsing and database operations.""" + +import base64 +import json +import posixpath +from uuid import UUID + +import psycopg +from common.helpers import get_drive_from_path +from common.logger import get_logger +from common.state_helpers import get_file_enriched +from common.storage import StorageMinio +from file_linking import add_file_linking +from impacket.dpapi import DPAPI_BLOB +from impacket.uuid import bin_to_string +from nemesis_dpapi import Blob, DpapiManager, MasterKeyNotDecryptedError, MasterKeyNotFoundError + +from .helpers import ( + derive_abe_key, + detect_encryption_type, + get_postgres_connection_str, + parse_abe_blob, + parse_chromium_file_path, +) + +logger = get_logger(__name__) + + +async def process_chromium_local_state( + dpapi_manager: DpapiManager, + object_id: str, + file_path: str | None = None, +) -> dict | None: + """Process Chromium Local State file and insert state keys into database. + + Args: + object_id: The object ID of the Local State file + file_path: Optional path to already downloaded file + """ + logger.info("Processing Chromium Local State file", object_id=object_id) + + file_enriched = get_file_enriched(object_id) + + # Extract username and browser from file path + username, browser = parse_chromium_file_path(file_enriched.path or "") + logger.debug("[process_chromium_local_state()]", username=username, browser=browser) + + # Get file content + if file_path: + with open(file_path, encoding="utf-8") as f: + content = f.read() + else: + storage = StorageMinio() + with storage.download(file_enriched.object_id) as temp_file: + with open(temp_file.name, encoding="utf-8") as f: + content = f.read() + + conn_str = get_postgres_connection_str() + with psycopg.connect(conn_str) as pg_conn: + state_key_data = await _insert_state_keys(file_enriched, username, browser, content, pg_conn, dpapi_manager) + + logger.debug("Completed processing Chromium Local State", object_id=object_id) + return state_key_data + + +def _parse_app_bound_key(app_bound_key_b64: str) -> tuple[bytes, str | None]: + """Parse app-bound encrypted key and extract system masterkey GUID. + + Args: + app_bound_key_b64: Base64 encoded app-bound key + + Returns: + Tuple of (decoded_bytes, system_masterkey_guid) + + Raises: + ValueError: If the key doesn't have APPB header + """ + try: + app_bound_key_bytes = base64.b64decode(app_bound_key_b64, validate=True) + + # Check for APPB header (first 4 bytes) + if len(app_bound_key_bytes) < 4 or app_bound_key_bytes[:4] != b"APPB": + raise ValueError("App-bound key does not have APPB header") + + # Remove APPB header and parse remaining as DPAPI blob + dpapi_bytes = app_bound_key_bytes[4:] + + blob = DPAPI_BLOB(dpapi_bytes) + if blob.rawData is not None: + blob.rawData = blob.rawData[: len(blob.getData())] + system_masterkey_guid = bin_to_string(blob["GuidMasterKey"]).lower() + return app_bound_key_bytes, system_masterkey_guid + + return app_bound_key_bytes, None + + except Exception as e: + logger.warning("Failed to parse app-bound key", error=str(e)) + return base64.b64decode(app_bound_key_b64), None + + + +async def _add_user_masterkey_link(file_enriched, username: str | None, masterkey_guid: UUID) -> None: + """Add file linking entry for user masterkey.""" + + # Skip trying to figure out the username/drive if we can + if r"AppData/Local/Google/Chrome/User Data" in file_enriched.path: + masterkey_path = posixpath.normpath( + posixpath.join( + file_enriched.path, + "../../../../../Roaming/Microsoft/Protect/", + str(masterkey_guid), + ) + ) + else: + # Not an AppData path, so build path based on the drive letter and username (if available) + drive = get_drive_from_path(file_enriched.path) or "" + + if not username: + username = "" + + masterkey_path = ( + f"{drive}/Users/{username}/AppData/Roaming/Microsoft/Protect//{masterkey_guid}" + ) + + await add_file_linking(file_enriched.source, file_enriched.path, masterkey_path, "windows:user_masterkey") + + +def _get_chromekey_from_source(source: str, pg_conn) -> bytes | None: + """Get decrypted Chrome key from chrome_keys table by source. + + Args: + source: Source value to match + pg_conn: PostgreSQL connection + + Returns: + Decrypted key bytes if found and decrypted, None otherwise + """ + try: + with pg_conn.cursor() as cur: + cur.execute( + "SELECT key_bytes_dec FROM chromium.chrome_keys WHERE source = %s AND key_is_decrypted = TRUE", + (source,), + ) + result = cur.fetchone() + return result[0] if result else None + except Exception as e: + logger.warning("Failed to lookup chrome key from source", error=str(e)) + return None + + +async def _insert_state_keys( + file_enriched, + username: str | None, + browser: str, + content: str, + pg_conn, + dpapi_manager: DpapiManager, +) -> dict | None: + """Parse Local State JSON and insert state keys into chromium.state_keys table.""" + try: + # Parse JSON content + data = json.loads(content) + + # Extract os_crypt section + os_crypt = data.get("os_crypt", {}) + + # Get Chrome key from chrome_keys table if available + chromekey = _get_chromekey_from_source(file_enriched.source, pg_conn) + + key_bytes_dec = b"" + key_is_decrypted = False + app_bound_key_system_dec = b"" + app_bound_key_user_dec = b"" + app_bound_key_dec = b"" + app_bound_key_is_decrypted = False + + # Get encrypted_key (pre v127) + encrypted_key_b64 = os_crypt.get("encrypted_key") + key_bytes_enc = b"" + key_masterkey_guid = None + + if encrypted_key_b64: + logger.debug("Found app bound key encrypted_key in Local State") + key_bytes_enc = base64.b64decode(encrypted_key_b64) + + if len(key_bytes_enc) < 5 or key_bytes_enc[:5] != b"DPAPI": + raise ValueError("Encrypted key does not have DPAPI header") + + # Remove DPAPI header and parse remaining as DPAPI blob + dpapi_blob_bytes = key_bytes_enc[5:] + + encryption_type, _ = detect_encryption_type(dpapi_blob_bytes) + if encryption_type != "dpapi": + raise Exception(f"Unsupported encryption type for v1 state key: {encryption_type}") + + dpapi_blob = Blob.from_bytes(dpapi_blob_bytes) + key_masterkey_guid = str(dpapi_blob.masterkey_guid) + try: + key_bytes_dec = await dpapi_manager.decrypt_blob(dpapi_blob) + if key_bytes_dec: + key_is_decrypted = True + logger.debug( + "Successfully decrypted encrypted_key state key", + masterkey_guid=dpapi_blob.masterkey_guid, + ) + else: + logger.debug("Failed to decrypt encrypted_key state key with DPAPI") + except (MasterKeyNotFoundError, MasterKeyNotDecryptedError) as e: + logger.debug( + "Masterkey not found or not decrypted for encrypted_key state key", + masterkey_guid=dpapi_blob.masterkey_guid, + reason=type(e).__name__, + ) + + await _add_user_masterkey_link(file_enriched, username, dpapi_blob.masterkey_guid) + + except Exception as e: + logger.warning(f"Unable to decrypt state key DPAPI blob: {dpapi_blob.masterkey_guid}", error=str(e)) + + # Get app_bound_encrypted_key (post v127) + app_bound_key_b64 = os_crypt.get("app_bound_encrypted_key") + app_bound_key_enc = b"" + app_bound_key_system_masterkey_guid = None + app_bound_key_user_masterkey_guid = None + + if app_bound_key_b64: + logger.debug("Found v2 app bound key encrypted_key in Local State") + app_bound_key_enc, app_bound_key_system_masterkey_guid = _parse_app_bound_key(app_bound_key_b64) + logger.debug(f"app_bound_key_system_masterkey_guid: {app_bound_key_system_masterkey_guid}") + + drive = get_drive_from_path(file_enriched.path) or "" + masterkey_path = ( + f"{drive}/Windows/System32/Microsoft/Protect/S-1-5-18/User/{app_bound_key_system_masterkey_guid}" + ) + + # Filename format: _ + # Hash comes from Chromium calling NCryptOpenKey with the key name of "Google Chromekey1" + # Hashing algorithm is described here: https://gist.github.com/leechristensen/40acb67ff5b788d6b78d81443b66b444 + cng_system_private_key_path = ( + f"{drive}/ProgramData/Microsoft/Crypto/SystemKeys/7096db7aeb75c0d3497ecd56d355a695_" + ) + + # add the masterkey file path (now that we know the key GUID) as a link/listing + await add_file_linking(file_enriched.source, file_enriched.path, masterkey_path, "windows:system_masterkey") + await add_file_linking(file_enriched.source, file_enriched.path, cng_system_private_key_path, "windows:cng_system_private_key - Contains Chrome key used to encrypt the Local State") + + try: + # Parse only the DPAPI portion (after APPB header) + if len(app_bound_key_enc) >= 4 and app_bound_key_enc[:4] == b"APPB": + dpapi_portion = app_bound_key_enc[4:] + # Step 1 - decrypt with a SYSTEM masterkey + app_bound_key_system_dec = await dpapi_manager.decrypt_blob(Blob.from_bytes(dpapi_portion)) + else: + logger.warning("App-bound key missing APPB header, cannot decrypt") + app_bound_key_system_dec = b"" + + if app_bound_key_system_dec: + user_blob = Blob.from_bytes(app_bound_key_system_dec) + app_bound_key_user_masterkey_guid = str(user_blob.masterkey_guid) + + try: + # Step 2 - decrypt with a _user_ masterkey + abe_blob_bytes = await dpapi_manager.decrypt_blob(user_blob) + if abe_blob_bytes: + # Store the intermediate value after USER key decryption + app_bound_key_user_dec = abe_blob_bytes + + # Step 3 - parse and derive the final ABE key (using the Chromekey for v3) + abe_parsed = parse_abe_blob(abe_blob_bytes, chromekey) + + if abe_parsed: + app_bound_key_dec = derive_abe_key(abe_parsed) + + if app_bound_key_dec: + app_bound_key_is_decrypted = True + logger.debug( + "Successfully derived ABE key", + version=abe_parsed.get("version"), + system_masterkey_guid=app_bound_key_system_masterkey_guid, + ) + else: + raise Exception("Failed to derive ABE key") + else: + raise Exception("Failed to parse ABE blob") + else: + raise Exception("Failed to decrypt ABE blob with user masterkey") + except (MasterKeyNotFoundError, MasterKeyNotDecryptedError) as e: + logger.debug( + "ABE key not decrypted. Masterkey not found or not decrypted", + masterkey_guid=user_blob.masterkey_guid, + reason=type(e).__name__, + ) + + await _add_user_masterkey_link(file_enriched, username, user_blob.masterkey_guid) + + except Exception as e: + logger.warning(f"Unable to decrypt/process final app bound key blob: {e}") + + except Exception as e: + logger.warning(f"Unable to decrypt intermediate/outer app bound key blob: {e}") + + # Skip if no keys are present + if not key_bytes_enc and not app_bound_key_enc: + logger.warning("No encryption keys found in Local State file") + return None + + # Prepare data for PostgreSQL + state_key_data = { + "originating_object_id": file_enriched.object_id, + "agent_id": file_enriched.agent_id, + "source": file_enriched.source, + "project": file_enriched.project, + "username": username, + "browser": browser, + "key_masterkey_guid": key_masterkey_guid, + "key_bytes_enc": key_bytes_enc, + "key_bytes_dec": key_bytes_dec, + "key_is_decrypted": key_is_decrypted, + "app_bound_key_enc": app_bound_key_enc, + "app_bound_key_system_masterkey_guid": app_bound_key_system_masterkey_guid, + "app_bound_key_user_masterkey_guid": app_bound_key_user_masterkey_guid, + "app_bound_key_system_dec": app_bound_key_system_dec, + "app_bound_key_user_dec": app_bound_key_user_dec, + "app_bound_key_dec": app_bound_key_dec, + "app_bound_key_is_decrypted": app_bound_key_is_decrypted, + } + + # Create a serializable copy for return (hex encode binary data) + serializable_data = state_key_data.copy() + for key, value in serializable_data.items(): + if isinstance(value, bytes): + serializable_data[key] = value.hex() + + # Insert into PostgreSQL + with pg_conn.cursor() as cur: + insert_sql = """ + INSERT INTO chromium.state_keys + (originating_object_id, agent_id, source, project, username, browser, + key_masterkey_guid, key_bytes_enc, key_bytes_dec, key_is_decrypted, + app_bound_key_enc, app_bound_key_system_masterkey_guid, + app_bound_key_user_masterkey_guid, app_bound_key_system_dec, app_bound_key_user_dec, + app_bound_key_dec, app_bound_key_is_decrypted) + VALUES (%(originating_object_id)s, %(agent_id)s, %(source)s, %(project)s, + %(username)s, %(browser)s, %(key_masterkey_guid)s, %(key_bytes_enc)s, + %(key_bytes_dec)s, %(key_is_decrypted)s, %(app_bound_key_enc)s, + %(app_bound_key_system_masterkey_guid)s, %(app_bound_key_user_masterkey_guid)s, + %(app_bound_key_system_dec)s, %(app_bound_key_user_dec)s, + %(app_bound_key_dec)s, %(app_bound_key_is_decrypted)s) + ON CONFLICT (source, username, browser) + DO UPDATE SET + key_masterkey_guid = EXCLUDED.key_masterkey_guid, + key_bytes_enc = EXCLUDED.key_bytes_enc, + key_bytes_dec = EXCLUDED.key_bytes_dec, + key_is_decrypted = EXCLUDED.key_is_decrypted, + app_bound_key_enc = EXCLUDED.app_bound_key_enc, + app_bound_key_system_masterkey_guid = EXCLUDED.app_bound_key_system_masterkey_guid, + app_bound_key_user_masterkey_guid = EXCLUDED.app_bound_key_user_masterkey_guid, + app_bound_key_system_dec = EXCLUDED.app_bound_key_system_dec, + app_bound_key_user_dec = EXCLUDED.app_bound_key_user_dec, + app_bound_key_dec = EXCLUDED.app_bound_key_dec, + app_bound_key_is_decrypted = EXCLUDED.app_bound_key_is_decrypted + """ + + cur.execute(insert_sql, state_key_data) + + # Get the inserted state key ID + cur.execute( + "SELECT id FROM chromium.state_keys WHERE source = %s AND username = %s AND browser = %s", + (file_enriched.source, username, browser), + ) + result = cur.fetchone() + if result: + state_key_id = result[0] + + cur.execute( + """ + UPDATE chromium.logins + SET state_key_id = %s + WHERE source = %s AND username = %s AND browser = %s + AND state_key_id IS NULL + AND encryption_type IN ('key', 'abe') + """, + (state_key_id, file_enriched.source, username, browser), + ) + logins_updated = cur.rowcount + + cur.execute( + """ + UPDATE chromium.cookies + SET state_key_id = %s + WHERE source = %s AND username = %s AND browser = %s + AND state_key_id IS NULL + AND encryption_type IN ('key', 'abe') + """, + (state_key_id, file_enriched.source, username, browser), + ) + cookies_updated = cur.rowcount + + logger.info( + "Updated existing entries with state key ID", + state_key_id=state_key_id, + logins_updated=logins_updated, + cookies_updated=cookies_updated, + ) + + pg_conn.commit() + + logger.info( + "Inserted state keys into database", + has_encrypted_key=bool(key_bytes_enc), + has_app_bound_key=bool(app_bound_key_enc), + ) + + return serializable_data + + except json.JSONDecodeError as e: + logger.exception("Failed to parse Local State JSON", error=str(e)) + raise + except Exception as e: + logger.exception("Error processing Local State", error=str(e)) + raise + + +async def retry_decrypt_state_key(state_key_id: int, dpapi_manager: DpapiManager, pg_conn) -> dict: + """Attempt to decrypt a single state_key record using currently available masterkeys. + + Args: + state_key_id: The ID of the state_key record to decrypt + dpapi_manager: DpapiManager instance for decryption + pg_conn: PostgreSQL connection + + Returns: + Dict with decryption results: { + "decrypted_v1": bool, + "decrypted_abe_stage1": bool, + "decrypted_abe_stage2": bool + } + """ + result = { + "decrypted_v1": False, + "decrypted_abe_stage1": False, + "decrypted_abe_stage2": False, + } + + # Fetch the state_key record + with pg_conn.cursor() as cur: + cur.execute( + """ + SELECT id, source, username, browser, + key_masterkey_guid, key_bytes_enc, key_bytes_dec, key_is_decrypted, + app_bound_key_enc, app_bound_key_system_masterkey_guid, + app_bound_key_user_masterkey_guid, app_bound_key_system_dec, + app_bound_key_user_dec, app_bound_key_dec, app_bound_key_is_decrypted + FROM chromium.state_keys + WHERE id = %s + """, + (state_key_id,), + ) + row = cur.fetchone() + + if not row: + logger.warning("State key not found", state_key_id=state_key_id) + return result + + ( + record_id, + source, + username, + browser, + key_masterkey_guid, + key_bytes_enc, + key_bytes_dec, + key_is_decrypted, + app_bound_key_enc, + app_bound_key_system_masterkey_guid, + app_bound_key_user_masterkey_guid, + app_bound_key_system_dec, + app_bound_key_user_dec, + app_bound_key_dec, + app_bound_key_is_decrypted, + ) = row + + # Try to decrypt pre-v127 encrypted_key + if key_bytes_enc and len(key_bytes_enc) > 0 and not key_is_decrypted: + try: + # Remove DPAPI header (first 5 bytes) + if len(key_bytes_enc) >= 5 and key_bytes_enc[:5] == b"DPAPI": + dpapi_blob_bytes = key_bytes_enc[5:] + + dpapi_blob = Blob.from_bytes(dpapi_blob_bytes) + try: + key_bytes_dec = await dpapi_manager.decrypt_blob(dpapi_blob) + if key_bytes_dec: + key_is_decrypted = True + result["decrypted_v1"] = True + logger.debug( + "Successfully decrypted v1 state key", + state_key_id=state_key_id, + masterkey_guid=dpapi_blob.masterkey_guid, + ) + + # Update database + with pg_conn.cursor() as cur: + cur.execute( + """ + UPDATE chromium.state_keys + SET key_bytes_dec = %s, key_is_decrypted = %s, + key_masterkey_guid = %s + WHERE id = %s + """, + (key_bytes_dec, key_is_decrypted, str(dpapi_blob.masterkey_guid), state_key_id), + ) + + except (MasterKeyNotFoundError, MasterKeyNotDecryptedError): + # Masterkey still not available, skip silently + pass + except Exception as e: + logger.warning("Error decrypting v1 state key", state_key_id=state_key_id, error=str(e)) + + except Exception as e: + logger.warning("Error processing v1 state key", state_key_id=state_key_id, error=str(e)) + + # Try to decrypt post-v127 app_bound_encrypted_key + if app_bound_key_enc and len(app_bound_key_enc) > 0: + # Stage 1: Decrypt outer layer with SYSTEM masterkey + if len(app_bound_key_system_dec) == 0: + try: + if len(app_bound_key_enc) >= 4 and app_bound_key_enc[:4] == b"APPB": + dpapi_portion = app_bound_key_enc[4:] + system_blob = Blob.from_bytes(dpapi_portion) + try: + app_bound_key_system_dec = await dpapi_manager.decrypt_blob(system_blob) + if app_bound_key_system_dec: + result["decrypted_abe_stage1"] = True + logger.debug( + "Successfully decrypted ABE stage 1 (SYSTEM key)", + state_key_id=state_key_id, + system_masterkey_guid=system_blob.masterkey_guid, + ) + + # Parse the intermediate blob to get user masterkey GUID + try: + user_blob = Blob.from_bytes(app_bound_key_system_dec) + app_bound_key_user_masterkey_guid = str(user_blob.masterkey_guid) + except Exception: + app_bound_key_user_masterkey_guid = None + + # Update database with stage 1 results + with pg_conn.cursor() as cur: + cur.execute( + """ + UPDATE chromium.state_keys + SET app_bound_key_system_dec = %s, + app_bound_key_system_masterkey_guid = %s, + app_bound_key_user_masterkey_guid = %s + WHERE id = %s + """, + ( + app_bound_key_system_dec, + str(system_blob.masterkey_guid), + app_bound_key_user_masterkey_guid, + state_key_id, + ), + ) + + except (MasterKeyNotFoundError, MasterKeyNotDecryptedError): + # SYSTEM masterkey still not available + pass + except Exception as e: + logger.warning("Error decrypting ABE stage 1", state_key_id=state_key_id, error=str(e)) + + except Exception as e: + logger.warning("Error processing ABE stage 1", state_key_id=state_key_id, error=str(e)) + + # Stage 2: Decrypt inner layer with USER masterkey and derive final key + if len(app_bound_key_system_dec) > 0 and not app_bound_key_is_decrypted: + try: + user_blob = Blob.from_bytes(app_bound_key_system_dec) + try: + abe_blob_bytes = await dpapi_manager.decrypt_blob(user_blob) + if abe_blob_bytes: + # Store the intermediate value after USER key decryption + app_bound_key_user_dec = abe_blob_bytes + + # Get chrome_key from database + chromekey = _get_chromekey_from_source(source, pg_conn) + + # Always attempt to parse the ABE blob (works for v2 without chromekey) + abe_parsed = parse_abe_blob(abe_blob_bytes, chromekey) + + if abe_parsed: + app_bound_key_dec = derive_abe_key(abe_parsed) + if app_bound_key_dec: + app_bound_key_is_decrypted = True + result["decrypted_abe_stage2"] = True + logger.debug( + "Successfully decrypted ABE stage 2 (USER key + ABE derivation)", + state_key_id=state_key_id, + user_masterkey_guid=user_blob.masterkey_guid, + abe_version=abe_parsed.get("version"), + ) + + # Update database with final key and intermediate user_dec + with pg_conn.cursor() as cur: + cur.execute( + """ + UPDATE chromium.state_keys + SET app_bound_key_user_dec = %s, + app_bound_key_dec = %s, + app_bound_key_is_decrypted = %s, + app_bound_key_user_masterkey_guid = %s + WHERE id = %s + """, + ( + app_bound_key_user_dec, + app_bound_key_dec, + app_bound_key_is_decrypted, + str(user_blob.masterkey_guid), + state_key_id, + ), + ) + else: + logger.warning("Failed to derive ABE key", state_key_id=state_key_id) + # Save the intermediate user_dec value for later retry + with pg_conn.cursor() as cur: + cur.execute( + """ + UPDATE chromium.state_keys + SET app_bound_key_user_dec = %s, + app_bound_key_user_masterkey_guid = %s + WHERE id = %s + """, + ( + app_bound_key_user_dec, + str(user_blob.masterkey_guid), + state_key_id, + ), + ) + else: + # Parsing failed - likely v3 waiting for chromekey + if chromekey is None: + logger.warning( + "ABE parsing failed, likely v3 waiting for Chrome key", + state_key_id=state_key_id, + source=source, + ) + else: + logger.warning("Failed to parse ABE blob", state_key_id=state_key_id) + + # Save the intermediate user_dec value for later retry + with pg_conn.cursor() as cur: + cur.execute( + """ + UPDATE chromium.state_keys + SET app_bound_key_user_dec = %s, + app_bound_key_user_masterkey_guid = %s + WHERE id = %s + """, + ( + app_bound_key_user_dec, + str(user_blob.masterkey_guid), + state_key_id, + ), + ) + + except (MasterKeyNotFoundError, MasterKeyNotDecryptedError): + # USER masterkey still not available + pass + except Exception as e: + logger.warning("Error decrypting ABE stage 2", state_key_id=state_key_id, error=str(e)) + + except Exception as e: + logger.warning("Error processing ABE stage 2", state_key_id=state_key_id, error=str(e)) + + # If we made any progress, update linked logins and cookies + if result["decrypted_v1"] or result["decrypted_abe_stage2"]: + try: + with pg_conn.cursor() as cur: + cur.execute( + """ + UPDATE chromium.logins + SET state_key_id = %s + WHERE source = %s AND username = %s AND browser = %s + AND state_key_id IS NULL + AND encryption_type IN ('key', 'abe') + """, + (state_key_id, source, username, browser), + ) + logins_updated = cur.rowcount + + cur.execute( + """ + UPDATE chromium.cookies + SET state_key_id = %s + WHERE source = %s AND username = %s AND browser = %s + AND state_key_id IS NULL + AND encryption_type IN ('key', 'abe') + """, + (state_key_id, source, username, browser), + ) + cookies_updated = cur.rowcount + + if logins_updated > 0 or cookies_updated > 0: + logger.info( + "Linked state key to existing logins/cookies", + state_key_id=state_key_id, + logins_updated=logins_updated, + cookies_updated=cookies_updated, + ) + + except Exception as e: + logger.warning("Error linking logins/cookies to state key", state_key_id=state_key_id, error=str(e)) + + # Commit all changes + pg_conn.commit() + + return result + + +async def retry_decrypt_state_keys_for_masterkey( + masterkey_guid: UUID, dpapi_manager: DpapiManager, masterkey_type: str | None = None +) -> dict: + """Find all state_keys waiting for this masterkey and try to decrypt them. + + Args: + masterkey_guid: The GUID of the newly available masterkey + dpapi_manager: DpapiManager instance for decryption + masterkey_type: Optional masterkey type ('system', 'user', 'unknown') for optimization + + Returns: + Dict with statistics: { + "state_keys_attempted": int, + "state_keys_progressed": int, + "errors": list + } + """ + result = {"state_keys_attempted": 0, "state_keys_progressed": 0, "errors": []} + + conn_str = get_postgres_connection_str() + + try: + with psycopg.connect(conn_str) as pg_conn: + # Find all state_keys that might need this masterkey + with pg_conn.cursor() as cur: + # Build query based on masterkey type for optimization + if masterkey_type == "system": + # SYSTEM keys only used for v2 ABE stage 1 + query = """ + SELECT DISTINCT id FROM chromium.state_keys + WHERE app_bound_key_system_masterkey_guid = %s + AND length(app_bound_key_system_dec) = 0 + """ + cur.execute(query, (str(masterkey_guid),)) + elif masterkey_type == "user": + # USER keys used for both v1 and v2 ABE stage 2 + query = """ + SELECT DISTINCT id FROM chromium.state_keys + WHERE (key_masterkey_guid = %s AND key_is_decrypted = FALSE) + OR (app_bound_key_user_masterkey_guid = %s AND app_bound_key_is_decrypted = FALSE) + """ + cur.execute(query, (str(masterkey_guid), str(masterkey_guid))) + else: + # Unknown type - check all possible uses + query = """ + SELECT DISTINCT id FROM chromium.state_keys + WHERE (key_masterkey_guid = %s AND key_is_decrypted = FALSE) + OR (app_bound_key_system_masterkey_guid = %s AND length(app_bound_key_system_dec) = 0) + OR (app_bound_key_user_masterkey_guid = %s AND app_bound_key_is_decrypted = FALSE) + """ + cur.execute(query, (str(masterkey_guid), str(masterkey_guid), str(masterkey_guid))) + + state_key_ids = [row[0] for row in cur.fetchall()] + + logger.debug( + "Found state keys potentially waiting for masterkey", + masterkey_guid=masterkey_guid, + masterkey_type=masterkey_type, + count=len(state_key_ids), + ) + + # Try to decrypt each state_key + for state_key_id in state_key_ids: + result["state_keys_attempted"] += 1 + try: + decrypt_result = await retry_decrypt_state_key(state_key_id, dpapi_manager, pg_conn) + + # Check if any progress was made + if ( + decrypt_result["decrypted_v1"] + or decrypt_result["decrypted_abe_stage1"] + or decrypt_result["decrypted_abe_stage2"] + ): + result["state_keys_progressed"] += 1 + + except Exception as e: + error_msg = f"Error processing state_key {state_key_id}: {str(e)}" + logger.warning("Failed to retry decrypt state key", state_key_id=state_key_id, error=str(e)) + result["errors"].append(error_msg) + + logger.info( + "Completed retroactive state_key decryption for masterkey", + masterkey_guid=masterkey_guid, + attempted=result["state_keys_attempted"], + progressed=result["state_keys_progressed"], + errors=len(result["errors"]), + ) + + except Exception as e: + error_msg = f"Database error during retroactive decryption: {str(e)}" + logger.exception("Error in retry_decrypt_state_keys_for_masterkey", masterkey_guid=masterkey_guid, error=str(e)) + result["errors"].append(error_msg) + + return result + + +async def retry_decrypt_state_keys_for_chromekey(source: str, chromekey: bytes) -> dict: + """Find all state_keys from a source waiting for chromekey and try to decrypt them. + + This function handles v3 ABE decryption where the USER masterkey has already been + applied (app_bound_key_user_dec is populated) but the chromekey is needed to + complete the final derivation. + + Args: + source: The source identifier (hostname) for the chromekey + chromekey: The decrypted Chrome key bytes + + Returns: + Dict with statistics: { + "state_keys_attempted": int, + "state_keys_decrypted": int, + "errors": list + } + """ + result = {"state_keys_attempted": 0, "state_keys_decrypted": 0, "errors": []} + + conn_str = get_postgres_connection_str() + + try: + with psycopg.connect(conn_str) as pg_conn: + # Find all state_keys from this source that have user_dec but aren't fully decrypted + with pg_conn.cursor() as cur: + query = """ + SELECT id, source, username, browser, app_bound_key_user_dec, + app_bound_key_user_masterkey_guid + FROM chromium.state_keys + WHERE source = %s + AND app_bound_key_is_decrypted = FALSE + AND app_bound_key_user_dec IS NOT NULL + AND length(app_bound_key_user_dec) > 0 + """ + cur.execute(query, (source,)) + state_keys = cur.fetchall() + + logger.debug( + "Found state keys waiting for chromekey", + source=source, + count=len(state_keys), + ) + + # Try to decrypt each state_key with the chromekey + for row in state_keys: + state_key_id, source, username, browser, app_bound_key_user_dec, user_masterkey_guid = row + result["state_keys_attempted"] += 1 + + try: + # Parse and derive the final ABE key using the chromekey + abe_parsed = parse_abe_blob(app_bound_key_user_dec, chromekey) + logger.debug( + "[retry_decrypt_state_keys_for_chromekey] Parsed ABE blob", + state_key_id=state_key_id, + abe_parsed=abe_parsed, + ) + + if abe_parsed: + app_bound_key_dec = derive_abe_key(abe_parsed) + logger.debug( + "[retry_decrypt_state_keys_for_chromekey] Derived ABE key", + state_key_id=state_key_id, + success=bool(app_bound_key_dec), + ) + + if app_bound_key_dec: + # Update database with final decrypted key + with pg_conn.cursor() as cur: + cur.execute( + """ + UPDATE chromium.state_keys + SET app_bound_key_dec = %s, + app_bound_key_is_decrypted = TRUE + WHERE id = %s + """, + (app_bound_key_dec, state_key_id), + ) + + # Link to existing logins/cookies + cur.execute( + """ + UPDATE chromium.logins + SET state_key_id = %s + WHERE source = %s AND username = %s AND browser = %s + AND state_key_id IS NULL + AND encryption_type IN ('key', 'abe') + """, + (state_key_id, source, username, browser), + ) + logins_updated = cur.rowcount + + cur.execute( + """ + UPDATE chromium.cookies + SET state_key_id = %s + WHERE source = %s AND username = %s AND browser = %s + AND state_key_id IS NULL + AND encryption_type IN ('key', 'abe') + """, + (state_key_id, source, username, browser), + ) + cookies_updated = cur.rowcount + + pg_conn.commit() + result["state_keys_decrypted"] += 1 + + logger.debug( + "Successfully decrypted ABE v3 with chromekey", + state_key_id=state_key_id, + source=source, + username=username, + browser=browser, + abe_version=abe_parsed.get("version"), + logins_updated=logins_updated, + cookies_updated=cookies_updated, + ) + else: + logger.warning( + "Failed to derive ABE key with chromekey", + state_key_id=state_key_id, + source=source, + ) + else: + logger.warning( + "Failed to parse ABE blob with chromekey", + state_key_id=state_key_id, + source=source, + ) + + except Exception as e: + error_msg = f"Error processing state_key {state_key_id}: {str(e)}" + logger.warning( + "Failed to decrypt state key with chromekey", + state_key_id=state_key_id, + error=str(e), + ) + result["errors"].append(error_msg) + + logger.info( + "Completed retroactive state_key decryption for chromekey", + source=source, + attempted=result["state_keys_attempted"], + decrypted=result["state_keys_decrypted"], + errors=len(result["errors"]), + ) + + except Exception as e: + error_msg = f"Database error during retroactive chromekey decryption: {str(e)}" + logger.exception("Error in retry_decrypt_state_keys_for_chromekey", source=source, error=str(e)) + result["errors"].append(error_msg) + + return result diff --git a/libs/chromium/chromium/logins.py b/libs/chromium/chromium/logins.py new file mode 100644 index 0000000..4b95d00 --- /dev/null +++ b/libs/chromium/chromium/logins.py @@ -0,0 +1,254 @@ +"""Chromium Login Data file parsing and database operations.""" + +import asyncio +import sqlite3 + +import psycopg +from common.logger import get_logger +from common.state_helpers import get_file_enriched +from common.storage import StorageMinio +from nemesis_dpapi import Blob, DpapiManager + +from .helpers import ( + convert_chromium_timestamp, + decrypt_chrome_string, + detect_encryption_type, + get_postgres_connection_str, + get_state_key_bytes, + get_state_key_id, + is_sqlite3, + parse_chromium_file_path, + try_decrypt_with_all_keys, +) + +logger = get_logger(__name__) + + +def process_chromium_logins( + object_id: str, file_path: str | None = None, dpapi_manager: DpapiManager | None = None +) -> None: + """Process Chromium Login Data file and insert logins into database. + + Args: + object_id: The object ID of the Login Data file + file_path: Optional path to already downloaded file + dpapi_manager: DPAPI manager for decryption + """ + logger.info("Processing Chromium Login Data file", object_id=object_id) + + file_enriched = get_file_enriched(object_id) + + # Extract username and browser from file path + username, browser = parse_chromium_file_path(file_enriched.path or "") + logger.debug("[process_chromium_logins]", username=username, browser=browser) + + # Get database file + if file_path: + db_path = file_path + else: + storage = StorageMinio() + with storage.download(file_enriched.object_id) as temp_file: + db_path = temp_file.name + + if is_sqlite3(db_path) is False: + logger.warning( + "Login Data file is not a valid SQLite3 database", object_id=object_id, file_path=file_enriched.path + ) + return + + conn_str = get_postgres_connection_str() + with psycopg.connect(conn_str) as pg_conn: + _insert_logins(file_enriched, username, browser, db_path, pg_conn, dpapi_manager) + + logger.debug("Completed processing Chromium Login Data", object_id=object_id) + + +def _insert_logins( + file_enriched, username: str | None, browser: str, db_path: str, pg_conn, dpapi_manager: DpapiManager | None = None +) -> None: + """Extract logins from Login Data database and insert into chromium.logins table.""" + try: + # Read from SQLite + with sqlite3.connect(db_path) as conn: + conn.text_factory = bytes # Get raw bytes, we'll handle text decoding manually + cursor = conn.cursor() + + # Query logins table + cursor.execute(""" + SELECT origin_url, username_value, signon_realm, date_created, + date_last_used, date_password_modified, times_used, password_value + FROM logins + """) + rows = cursor.fetchall() + + if not rows: + return + + # Prepare data for PostgreSQL + logins_data = [] + for row in rows: + ( + origin_url, + username_value, + signon_realm, + date_created, + date_last_used, + date_password_modified, + times_used, + password_value, + ) = row + + # Decode text fields from bytes (since we set text_factory = bytes) + origin_url = origin_url.decode("utf-8", errors="replace") if origin_url else None + username_value = username_value.decode("utf-8", errors="replace") if username_value else None + signon_realm = signon_realm.decode("utf-8", errors="replace") if signon_realm else None + + # password_value is already binary (what we want) + if password_value is None: + password_value = b"" + + # Detect encryption type and get masterkey GUID + encryption_type, masterkey_guid = detect_encryption_type(password_value) + is_decrypted = False + password_value_dec = None + + # Try DPAPI decryption first + if masterkey_guid and dpapi_manager: + try: + password_dec_bytes = asyncio.run(dpapi_manager.decrypt_blob(Blob.from_bytes(password_value))) + if password_dec_bytes: + password_value_dec = password_dec_bytes.decode("utf-8", errors="replace") + is_decrypted = True + except: + pass + + # Get state key ID for key/abe encryption + state_key_id = None + if encryption_type in ["key", "abe"]: + # Try primary approach: get state key based on username/browser + if username: # Only try primary approach if username was extracted + state_key_id = get_state_key_id(file_enriched.source, username, browser, pg_conn) + if state_key_id: + # Retrieve the pre-processed state key for decryption + state_key_bytes = get_state_key_bytes(state_key_id, encryption_type, pg_conn) + if state_key_bytes: + try: + password_dec_bytes = decrypt_chrome_string( + password_value, state_key_bytes, encryption_type + ) + if password_dec_bytes: + # For passwords, may need to strip offset bytes depending on version + if encryption_type == "key" and len(password_dec_bytes) > 32: + # v10/v11 passwords may have 32-byte prefix + 16-byte suffix + password_dec_bytes = password_dec_bytes[32:-16] + elif encryption_type == "key" and len(password_dec_bytes) > 16: + # Or just 16-byte suffix + password_dec_bytes = password_dec_bytes[:-16] + # v20 passwords typically don't have offset like cookies + + password_value_dec = password_dec_bytes.decode("utf-8", errors="replace") + is_decrypted = True + except Exception as e: + logger.debug( + "Failed to decrypt password with state key", + state_key_id=state_key_id, + encryption_type=encryption_type, + error=str(e), + ) + + # Backup approach: try all state keys from the same source if primary failed + if not is_decrypted: + logger.debug( + "Primary decryption failed, trying backup approach with all keys from source", + source=file_enriched.source, + encryption_type=encryption_type, + ) + backup_decrypted_bytes, backup_state_key_id = try_decrypt_with_all_keys( + password_value, file_enriched.source, encryption_type, pg_conn + ) + if backup_decrypted_bytes and backup_state_key_id: + # Apply the same offset handling as above + if encryption_type == "abe" and len(backup_decrypted_bytes) > 32: + # v20 cookies typically have 32-byte offset + backup_decrypted_bytes = backup_decrypted_bytes[32:] + elif encryption_type == "key" and len(backup_decrypted_bytes) > 48: + # v10/v11 cookies may have 32-byte prefix + 16-byte suffix + backup_decrypted_bytes = backup_decrypted_bytes[32:-16] + elif encryption_type == "key" and len(backup_decrypted_bytes) > 16: + # Or just 16-byte suffix + backup_decrypted_bytes = backup_decrypted_bytes[:-16] + + password_value_dec = backup_decrypted_bytes.decode("utf-8", errors="replace") + is_decrypted = True + state_key_id = backup_state_key_id + logger.debug( + "Successfully decrypted cookie using backup approach", state_key_id=backup_state_key_id + ) + + login_data = { + "originating_object_id": file_enriched.object_id, + "agent_id": file_enriched.agent_id, + "source": file_enriched.source, + "project": file_enriched.project, + "username": username, + "browser": browser, + "origin_url": origin_url, + "username_value": username_value, + "signon_realm": signon_realm, + "date_created": convert_chromium_timestamp(date_created), + "date_last_used": convert_chromium_timestamp(date_last_used), + "date_password_modified": convert_chromium_timestamp(date_password_modified), + "times_used": times_used, + "encryption_type": encryption_type, + "masterkey_guid": masterkey_guid, + "state_key_id": state_key_id, + "is_decrypted": is_decrypted, + "password_value_enc": password_value, + "password_value_dec": password_value_dec, + } + logins_data.append(login_data) + + # Insert into PostgreSQL + with pg_conn.cursor() as cur: + insert_sql = """ + INSERT INTO chromium.logins + (originating_object_id, agent_id, source, project, username, browser, + origin_url, username_value, signon_realm, date_created, date_last_used, + date_password_modified, times_used, encryption_type, masterkey_guid, + state_key_id, is_decrypted, password_value_enc, password_value_dec) + VALUES (%(originating_object_id)s, %(agent_id)s, %(source)s, %(project)s, + %(username)s, %(browser)s, %(origin_url)s, %(username_value)s, + %(signon_realm)s, %(date_created)s, %(date_last_used)s, + %(date_password_modified)s, %(times_used)s, %(encryption_type)s, + %(masterkey_guid)s, %(state_key_id)s, %(is_decrypted)s, + %(password_value_enc)s, %(password_value_dec)s) + ON CONFLICT (source, username, browser, origin_url, username_value) + DO UPDATE SET + origin_url = EXCLUDED.origin_url, + username_value = EXCLUDED.username_value, + signon_realm = EXCLUDED.signon_realm, + date_created = EXCLUDED.date_created, + date_last_used = EXCLUDED.date_last_used, + date_password_modified = EXCLUDED.date_password_modified, + times_used = EXCLUDED.times_used, + encryption_type = EXCLUDED.encryption_type, + masterkey_guid = EXCLUDED.masterkey_guid, + state_key_id = EXCLUDED.state_key_id, + is_decrypted = EXCLUDED.is_decrypted, + password_value_enc = EXCLUDED.password_value_enc, + password_value_dec = EXCLUDED.password_value_dec + """ + + cur.executemany(insert_sql, logins_data) + pg_conn.commit() + + logger.info("Inserted logins into database", count=len(logins_data)) + + except Exception as e: + logger.exception( + "Error processing Login Data", + error=str(e), + object_id=file_enriched.object_id, + file_path=file_enriched.path, + ) + raise diff --git a/libs/chromium/chromium/retry.py b/libs/chromium/chromium/retry.py new file mode 100644 index 0000000..b9c8bbd --- /dev/null +++ b/libs/chromium/chromium/retry.py @@ -0,0 +1,426 @@ +"""Retry decryption logic for Chromium cookies and logins.""" + +from uuid import UUID + +import psycopg +from common.logger import get_logger +from nemesis_dpapi import Blob, DpapiManager, MasterKeyNotDecryptedError, MasterKeyNotFoundError + +from .helpers import decrypt_chrome_string, get_postgres_connection_str, get_state_key_bytes + +logger = get_logger(__name__) + + +async def retry_decrypt_chromium_data( + masterkey_guid: UUID, dpapi_manager: DpapiManager, masterkey_type: str | None = None +) -> dict: + """Retry decrypting cookies and logins that failed to decrypt previously. + + When a new masterkey becomes available, this function: + 1. Retries DPAPI-encrypted cookies/logins that use this masterkey + 2. Retries key/abe-encrypted cookies/logins (state keys may now be decrypted) + + Args: + masterkey_guid: The GUID of the newly available masterkey + dpapi_manager: DpapiManager instance for decryption + masterkey_type: Optional masterkey type ('system', 'user', 'unknown') + + Returns: + Dict with statistics: { + "cookies_attempted": int, + "cookies_decrypted": int, + "logins_attempted": int, + "logins_decrypted": int, + "errors": list + } + """ + result = { + "cookies_attempted": 0, + "cookies_decrypted": 0, + "logins_attempted": 0, + "logins_decrypted": 0, + "errors": [], + } + + conn_str = get_postgres_connection_str() + + try: + with psycopg.connect(conn_str) as pg_conn: + # Retry cookies + cookies_result = await _retry_decrypt_cookies(masterkey_guid, dpapi_manager, pg_conn) + result["cookies_attempted"] = cookies_result["attempted"] + result["cookies_decrypted"] = cookies_result["decrypted"] + result["errors"].extend(cookies_result["errors"]) + + # Retry logins + logins_result = await _retry_decrypt_logins(masterkey_guid, dpapi_manager, pg_conn) + result["logins_attempted"] = logins_result["attempted"] + result["logins_decrypted"] = logins_result["decrypted"] + result["errors"].extend(logins_result["errors"]) + + logger.warning( + "Completed retroactive chromium data decryption for masterkey", + masterkey_guid=masterkey_guid, + cookies_attempted=result["cookies_attempted"], + cookies_decrypted=result["cookies_decrypted"], + logins_attempted=result["logins_attempted"], + logins_decrypted=result["logins_decrypted"], + errors=len(result["errors"]), + ) + + except Exception as e: + error_msg = f"Database error during retroactive chromium data decryption: {str(e)}" + logger.exception("Error in retry_decrypt_chromium_data", masterkey_guid=masterkey_guid, error=str(e)) + result["errors"].append(error_msg) + + return result + + +async def _retry_decrypt_cookies(masterkey_guid: UUID, dpapi_manager: DpapiManager, pg_conn) -> dict: + """Retry decrypting cookies that previously failed. + + Args: + masterkey_guid: The GUID of the newly available masterkey + dpapi_manager: DpapiManager instance for decryption + pg_conn: PostgreSQL connection + + Returns: + Dict with {"attempted": int, "decrypted": int, "errors": list} + """ + result = {"attempted": 0, "decrypted": 0, "errors": []} + + # Find all undecrypted cookies + # For DPAPI: only those matching this masterkey + # For key/abe: all undecrypted (state keys may now be available) + with pg_conn.cursor() as cur: + cur.execute( + """ + SELECT id, encryption_type, masterkey_guid, state_key_id, + value_enc, source, username, browser + FROM chromium.cookies + WHERE is_decrypted = FALSE + AND ( + (encryption_type = 'dpapi' AND masterkey_guid = %s) + OR encryption_type IN ('key', 'abe') + ) + """, + (masterkey_guid,), + ) + cookies = cur.fetchall() + + logger.debug( + "Found undecrypted cookies to retry", + masterkey_guid=masterkey_guid, + count=len(cookies), + ) + + for cookie in cookies: + result["attempted"] += 1 + cookie_id, encryption_type, cookie_masterkey_guid, state_key_id, value_enc, source, username, browser = cookie + + try: + value_dec = None + decrypted = False + + # Try DPAPI decryption + if encryption_type == "dpapi" and cookie_masterkey_guid == masterkey_guid: + try: + value_dec_bytes = await dpapi_manager.decrypt_blob(Blob.from_bytes(value_enc)) + if value_dec_bytes: + value_dec = value_dec_bytes.decode("utf-8", errors="replace") + decrypted = True + logger.debug("Successfully decrypted cookie with DPAPI", cookie_id=cookie_id) + except (MasterKeyNotFoundError, MasterKeyNotDecryptedError): + # Still not available + pass + except Exception as e: + logger.warning("Error decrypting cookie with DPAPI", cookie_id=cookie_id, error=str(e)) + + # Try state key decryption (key/abe) + elif encryption_type in ["key", "abe"]: + # Try with existing state_key_id if available + if state_key_id: + state_key_bytes = get_state_key_bytes(state_key_id, encryption_type, pg_conn) + if state_key_bytes: + try: + value_dec_bytes = decrypt_chrome_string(value_enc, state_key_bytes, encryption_type) + if value_dec_bytes: + # Apply offset handling for cookies + if encryption_type == "abe" and len(value_dec_bytes) > 32: + value_dec_bytes = value_dec_bytes[32:] + elif encryption_type == "key" and len(value_dec_bytes) > 48: + value_dec_bytes = value_dec_bytes[32:-16] + elif encryption_type == "key" and len(value_dec_bytes) > 16: + value_dec_bytes = value_dec_bytes[:-16] + + value_dec = value_dec_bytes.decode("utf-8", errors="replace") + decrypted = True + logger.debug( + "Successfully decrypted cookie with state key", + cookie_id=cookie_id, + state_key_id=state_key_id, + ) + except Exception as e: + logger.debug( + "Failed to decrypt cookie with state key", + cookie_id=cookie_id, + state_key_id=state_key_id, + error=str(e), + ) + + # If no state_key_id or decryption failed, try to find matching state key + if not decrypted and username and browser: + try: + with pg_conn.cursor() as cur2: + # Try to find a decrypted state key for this source/username/browser + cur2.execute( + """ + SELECT id FROM chromium.state_keys + WHERE source = %s AND username = %s AND browser = %s + AND ( + (key_is_decrypted = TRUE AND %s = 'key') + OR (app_bound_key_is_decrypted = TRUE AND %s = 'abe') + ) + """, + (source, username, browser, encryption_type, encryption_type), + ) + state_key_row = cur2.fetchone() + if state_key_row: + new_state_key_id = state_key_row[0] + state_key_bytes = get_state_key_bytes(new_state_key_id, encryption_type, pg_conn) + if state_key_bytes: + try: + value_dec_bytes = decrypt_chrome_string( + value_enc, state_key_bytes, encryption_type + ) + if value_dec_bytes: + # Apply offset handling + if encryption_type == "abe" and len(value_dec_bytes) > 32: + value_dec_bytes = value_dec_bytes[32:] + elif encryption_type == "key" and len(value_dec_bytes) > 48: + value_dec_bytes = value_dec_bytes[32:-16] + elif encryption_type == "key" and len(value_dec_bytes) > 16: + value_dec_bytes = value_dec_bytes[:-16] + + value_dec = value_dec_bytes.decode("utf-8", errors="replace") + decrypted = True + state_key_id = new_state_key_id + logger.debug( + "Successfully decrypted cookie with newly found state key", + cookie_id=cookie_id, + state_key_id=new_state_key_id, + ) + except Exception as e: + logger.debug( + "Failed to decrypt cookie with newly found state key", + cookie_id=cookie_id, + error=str(e), + ) + except Exception as e: + logger.debug("Error looking up state key for cookie", cookie_id=cookie_id, error=str(e)) + + # Update database if decrypted + if decrypted and value_dec: + with pg_conn.cursor() as cur: + cur.execute( + """ + UPDATE chromium.cookies + SET value_dec = %s, is_decrypted = TRUE, state_key_id = %s + WHERE id = %s + """, + (value_dec, state_key_id, cookie_id), + ) + pg_conn.commit() + result["decrypted"] += 1 + + except Exception as e: + error_msg = f"Error processing cookie {cookie_id}: {str(e)}" + logger.warning("Failed to retry decrypt cookie", cookie_id=cookie_id, error=str(e)) + result["errors"].append(error_msg) + + return result + + +async def _retry_decrypt_logins(masterkey_guid: UUID, dpapi_manager: DpapiManager, pg_conn) -> dict: + """Retry decrypting logins that previously failed. + + Args: + masterkey_guid: The GUID of the newly available masterkey + dpapi_manager: DpapiManager instance for decryption + pg_conn: PostgreSQL connection + + Returns: + Dict with {"attempted": int, "decrypted": int, "errors": list} + """ + result = {"attempted": 0, "decrypted": 0, "errors": []} + + # Find all undecrypted logins + with pg_conn.cursor() as cur: + cur.execute( + """ + SELECT id, encryption_type, masterkey_guid, state_key_id, + password_value_enc, source, username, browser + FROM chromium.logins + WHERE is_decrypted = FALSE + AND ( + (encryption_type = 'dpapi' AND masterkey_guid = %s) + OR encryption_type IN ('key', 'abe') + ) + """, + (masterkey_guid,), + ) + logins = cur.fetchall() + + logger.debug( + "Found undecrypted logins to retry", + masterkey_guid=masterkey_guid, + count=len(logins), + ) + + for login in logins: + result["attempted"] += 1 + ( + login_id, + encryption_type, + login_masterkey_guid, + state_key_id, + password_value_enc, + source, + username, + browser, + ) = login + + try: + password_dec = None + decrypted = False + + # Try DPAPI decryption + if encryption_type == "dpapi" and login_masterkey_guid == masterkey_guid: + try: + password_dec_bytes = await dpapi_manager.decrypt_blob(Blob.from_bytes(password_value_enc)) + if password_dec_bytes: + password_dec = password_dec_bytes.decode("utf-8", errors="replace") + decrypted = True + logger.debug("Successfully decrypted login with DPAPI", login_id=login_id) + except (MasterKeyNotFoundError, MasterKeyNotDecryptedError): + # Still not available + pass + except Exception as e: + logger.warning("Error decrypting login with DPAPI", login_id=login_id, error=str(e)) + + # Try state key decryption (key/abe) + elif encryption_type in ["key", "abe"]: + # Try with existing state_key_id if available + if state_key_id: + state_key_bytes = get_state_key_bytes(state_key_id, encryption_type, pg_conn) + if state_key_bytes: + try: + password_dec_bytes = decrypt_chrome_string( + password_value_enc, state_key_bytes, encryption_type + ) + if password_dec_bytes: + # Apply offset handling for passwords + if encryption_type == "key" and len(password_dec_bytes) > 32: + password_dec_bytes = password_dec_bytes[32:-16] + elif encryption_type == "key" and len(password_dec_bytes) > 16: + password_dec_bytes = password_dec_bytes[:-16] + # v20 passwords typically don't have offset like cookies + + password_dec = password_dec_bytes.decode("utf-8", errors="replace") + decrypted = True + logger.debug( + "Successfully decrypted login with state key", + login_id=login_id, + state_key_id=state_key_id, + ) + except Exception as e: + logger.debug( + "Failed to decrypt login with state key", + login_id=login_id, + state_key_id=state_key_id, + error=str(e), + ) + + # If no state_key_id or decryption failed, try to find matching state key + if not decrypted and source: + try: + with pg_conn.cursor() as cur2: + if username and browser: + # Try to find a decrypted state key for this source/username/browser + cur2.execute( + """ + SELECT id FROM chromium.state_keys + WHERE source = %s AND username = %s AND browser = %s + AND ( + (key_is_decrypted = TRUE AND %s = 'key') + OR (app_bound_key_is_decrypted = TRUE AND %s = 'abe') + ) + """, + (source, username, browser, encryption_type, encryption_type), + ) + else: + # if no username/password, just restrict to SOURCE + cur2.execute( + """ + SELECT id FROM chromium.state_keys + WHERE source = %s + AND ( + (key_is_decrypted = TRUE AND %s = 'key') + OR (app_bound_key_is_decrypted = TRUE AND %s = 'abe') + ) + """, + (source, encryption_type, encryption_type), + ) + state_key_row = cur2.fetchone() + if state_key_row: + new_state_key_id = state_key_row[0] + state_key_bytes = get_state_key_bytes(new_state_key_id, encryption_type, pg_conn) + if state_key_bytes: + try: + password_dec_bytes = decrypt_chrome_string( + password_value_enc, state_key_bytes, encryption_type + ) + if password_dec_bytes: + # Apply offset handling + if encryption_type == "key" and len(password_dec_bytes) > 32: + password_dec_bytes = password_dec_bytes[32:-16] + elif encryption_type == "key" and len(password_dec_bytes) > 16: + password_dec_bytes = password_dec_bytes[:-16] + + password_dec = password_dec_bytes.decode("utf-8", errors="replace") + decrypted = True + state_key_id = new_state_key_id + logger.debug( + "Successfully decrypted login with newly found state key", + login_id=login_id, + state_key_id=new_state_key_id, + ) + except Exception as e: + logger.debug( + "Failed to decrypt login with newly found state key", + login_id=login_id, + error=str(e), + ) + except Exception as e: + logger.debug("Error looking up state key for login", login_id=login_id, error=str(e)) + + # Update database if decrypted + if decrypted and password_dec: + with pg_conn.cursor() as cur: + cur.execute( + """ + UPDATE chromium.logins + SET password_value_dec = %s, is_decrypted = TRUE, state_key_id = %s + WHERE id = %s + """, + (password_dec, state_key_id, login_id), + ) + pg_conn.commit() + result["decrypted"] += 1 + + except Exception as e: + error_msg = f"Error processing login {login_id}: {str(e)}" + logger.warning("Failed to retry decrypt login", login_id=login_id, error=str(e)) + result["errors"].append(error_msg) + + return result diff --git a/libs/chromium/poetry.lock b/libs/chromium/poetry.lock new file mode 100644 index 0000000..a1dd585 --- /dev/null +++ b/libs/chromium/poetry.lock @@ -0,0 +1,2600 @@ +# This file is automatically @generated by Poetry 2.0.1 and should not be changed by hand. + +[[package]] +name = "aiohappyeyeballs" +version = "2.6.1" +description = "Happy Eyeballs for asyncio" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8"}, + {file = "aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558"}, +] + +[[package]] +name = "aiohttp" +version = "3.13.0" +description = "Async http client/server framework (asyncio)" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "aiohttp-3.13.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ca69ec38adf5cadcc21d0b25e2144f6a25b7db7bea7e730bac25075bc305eff0"}, + {file = "aiohttp-3.13.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:240f99f88a9a6beb53ebadac79a2e3417247aa756202ed234b1dbae13d248092"}, + {file = "aiohttp-3.13.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a4676b978a9711531e7cea499d4cdc0794c617a1c0579310ab46c9fdf5877702"}, + {file = "aiohttp-3.13.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:48fcdd5bc771cbbab8ccc9588b8b6447f6a30f9fe00898b1a5107098e00d6793"}, + {file = "aiohttp-3.13.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:eeea0cdd2f687e210c8f605f322d7b0300ba55145014a5dbe98bd4be6fff1f6c"}, + {file = "aiohttp-3.13.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:10b3f01d5aeb632adaaf39c5e93f040a550464a768d54c514050c635adcbb9d0"}, + {file = "aiohttp-3.13.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a4dc0b83e25267f42ef065ea57653de4365b56d7bc4e4cfc94fabe56998f8ee6"}, + {file = "aiohttp-3.13.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:72714919ed9b90f030f761c20670e529c4af96c31bd000917dd0c9afd1afb731"}, + {file = "aiohttp-3.13.0-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:564be41e85318403fdb176e9e5b3e852d528392f42f2c1d1efcbeeed481126d7"}, + {file = "aiohttp-3.13.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:84912962071087286333f70569362e10793f73f45c48854e6859df11001eb2d3"}, + {file = "aiohttp-3.13.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:90b570f1a146181c3d6ae8f755de66227ded49d30d050479b5ae07710f7894c5"}, + {file = "aiohttp-3.13.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:2d71ca30257ce756e37a6078b1dff2d9475fee13609ad831eac9a6531bea903b"}, + {file = "aiohttp-3.13.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:cd45eb70eca63f41bb156b7dffbe1a7760153b69892d923bdb79a74099e2ed90"}, + {file = "aiohttp-3.13.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:5ae3a19949a27982c7425a7a5a963c1268fdbabf0be15ab59448cbcf0f992519"}, + {file = "aiohttp-3.13.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ea6df292013c9f050cbf3f93eee9953d6e5acd9e64a0bf4ca16404bfd7aa9bcc"}, + {file = "aiohttp-3.13.0-cp310-cp310-win32.whl", hash = "sha256:3b64f22fbb6dcd5663de5ef2d847a5638646ef99112503e6f7704bdecb0d1c4d"}, + {file = "aiohttp-3.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:f8d877aa60d80715b2afc565f0f1aea66565824c229a2d065b31670e09fed6d7"}, + {file = "aiohttp-3.13.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:99eb94e97a42367fef5fc11e28cb2362809d3e70837f6e60557816c7106e2e20"}, + {file = "aiohttp-3.13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4696665b2713021c6eba3e2b882a86013763b442577fe5d2056a42111e732eca"}, + {file = "aiohttp-3.13.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3e6a38366f7f0d0f6ed7a1198055150c52fda552b107dad4785c0852ad7685d1"}, + {file = "aiohttp-3.13.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aab715b1a0c37f7f11f9f1f579c6fbaa51ef569e47e3c0a4644fba46077a9409"}, + {file = "aiohttp-3.13.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7972c82bed87d7bd8e374b60a6b6e816d75ba4f7c2627c2d14eed216e62738e1"}, + {file = "aiohttp-3.13.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ca8313cb852af788c78d5afdea24c40172cbfff8b35e58b407467732fde20390"}, + {file = "aiohttp-3.13.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6c333a2385d2a6298265f4b3e960590f787311b87f6b5e6e21bb8375914ef504"}, + {file = "aiohttp-3.13.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cc6d5fc5edbfb8041d9607f6a417997fa4d02de78284d386bea7ab767b5ea4f3"}, + {file = "aiohttp-3.13.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7ddedba3d0043349edc79df3dc2da49c72b06d59a45a42c1c8d987e6b8d175b8"}, + {file = "aiohttp-3.13.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:23ca762140159417a6bbc959ca1927f6949711851e56f2181ddfe8d63512b5ad"}, + {file = "aiohttp-3.13.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:bfe824d6707a5dc3c5676685f624bc0c63c40d79dc0239a7fd6c034b98c25ebe"}, + {file = "aiohttp-3.13.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:3c11fa5dd2ef773a8a5a6daa40243d83b450915992eab021789498dc87acc114"}, + {file = "aiohttp-3.13.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:00fdfe370cffede3163ba9d3f190b32c0cfc8c774f6f67395683d7b0e48cdb8a"}, + {file = "aiohttp-3.13.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:6475e42ef92717a678bfbf50885a682bb360a6f9c8819fb1a388d98198fdcb80"}, + {file = "aiohttp-3.13.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:77da5305a410910218b99f2a963092f4277d8a9c1f429c1ff1b026d1826bd0b6"}, + {file = "aiohttp-3.13.0-cp311-cp311-win32.whl", hash = "sha256:2f9d9ea547618d907f2ee6670c9a951f059c5994e4b6de8dcf7d9747b420c820"}, + {file = "aiohttp-3.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:0f19f7798996d4458c669bd770504f710014926e9970f4729cf55853ae200469"}, + {file = "aiohttp-3.13.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1c272a9a18a5ecc48a7101882230046b83023bb2a662050ecb9bfcb28d9ab53a"}, + {file = "aiohttp-3.13.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:97891a23d7fd4e1afe9c2f4473e04595e4acb18e4733b910b6577b74e7e21985"}, + {file = "aiohttp-3.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:475bd56492ce5f4cffe32b5533c6533ee0c406d1d0e6924879f83adcf51da0ae"}, + {file = "aiohttp-3.13.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c32ada0abb4bc94c30be2b681c42f058ab104d048da6f0148280a51ce98add8c"}, + {file = "aiohttp-3.13.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:4af1f8877ca46ecdd0bc0d4a6b66d4b2bddc84a79e2e8366bc0d5308e76bceb8"}, + {file = "aiohttp-3.13.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e04ab827ec4f775817736b20cdc8350f40327f9b598dec4e18c9ffdcbea88a93"}, + {file = "aiohttp-3.13.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a6d9487b9471ec36b0faedf52228cd732e89be0a2bbd649af890b5e2ce422353"}, + {file = "aiohttp-3.13.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e66c57416352f36bf98f6641ddadd47c93740a22af7150d3e9a1ef6e983f9a8"}, + {file = "aiohttp-3.13.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:469167d5372f5bb3aedff4fc53035d593884fff2617a75317740e885acd48b04"}, + {file = "aiohttp-3.13.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a9f3546b503975a69b547c9fd1582cad10ede1ce6f3e313a2f547c73a3d7814f"}, + {file = "aiohttp-3.13.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:6b4174fcec98601f0cfdf308ee29a6ae53c55f14359e848dab4e94009112ee7d"}, + {file = "aiohttp-3.13.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a533873a7a4ec2270fb362ee5a0d3b98752e4e1dc9042b257cd54545a96bd8ed"}, + {file = "aiohttp-3.13.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:ce887c5e54411d607ee0959cac15bb31d506d86a9bcaddf0b7e9d63325a7a802"}, + {file = "aiohttp-3.13.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:d871f6a30d43e32fc9252dc7b9febe1a042b3ff3908aa83868d7cf7c9579a59b"}, + {file = "aiohttp-3.13.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:222c828243b4789d79a706a876910f656fad4381661691220ba57b2ab4547865"}, + {file = "aiohttp-3.13.0-cp312-cp312-win32.whl", hash = "sha256:682d2e434ff2f1108314ff7f056ce44e457f12dbed0249b24e106e385cf154b9"}, + {file = "aiohttp-3.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:0a2be20eb23888df130214b91c262a90e2de1553d6fb7de9e9010cec994c0ff2"}, + {file = "aiohttp-3.13.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:00243e51f16f6ec0fb021659d4af92f675f3cf9f9b39efd142aa3ad641d8d1e6"}, + {file = "aiohttp-3.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:059978d2fddc462e9211362cbc8446747ecd930537fa559d3d25c256f032ff54"}, + {file = "aiohttp-3.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:564b36512a7da3b386143c611867e3f7cfb249300a1bf60889bd9985da67ab77"}, + {file = "aiohttp-3.13.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4aa995b9156ae499393d949a456a7ab0b994a8241a96db73a3b73c7a090eff6a"}, + {file = "aiohttp-3.13.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:55ca0e95a3905f62f00900255ed807c580775174252999286f283e646d675a49"}, + {file = "aiohttp-3.13.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:49ce7525853a981fc35d380aa2353536a01a9ec1b30979ea4e35966316cace7e"}, + {file = "aiohttp-3.13.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2117be9883501eaf95503bd313eb4c7a23d567edd44014ba15835a1e9ec6d852"}, + {file = "aiohttp-3.13.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d169c47e40c911f728439da853b6fd06da83761012e6e76f11cb62cddae7282b"}, + {file = "aiohttp-3.13.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:703ad3f742fc81e543638a7bebddd35acadaa0004a5e00535e795f4b6f2c25ca"}, + {file = "aiohttp-3.13.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5bf635c3476f4119b940cc8d94ad454cbe0c377e61b4527f0192aabeac1e9370"}, + {file = "aiohttp-3.13.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:cfe6285ef99e7ee51cef20609be2bc1dd0e8446462b71c9db8bb296ba632810a"}, + {file = "aiohttp-3.13.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:34d8af6391c5f2e69749d7f037b614b8c5c42093c251f336bdbfa4b03c57d6c4"}, + {file = "aiohttp-3.13.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:12f5d820fadc5848d4559ea838aef733cf37ed2a1103bba148ac2f5547c14c29"}, + {file = "aiohttp-3.13.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:0f1338b61ea66f4757a0544ed8a02ccbf60e38d9cfb3225888888dd4475ebb96"}, + {file = "aiohttp-3.13.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:582770f82513419512da096e8df21ca44f86a2e56e25dc93c5ab4df0fe065bf0"}, + {file = "aiohttp-3.13.0-cp313-cp313-win32.whl", hash = "sha256:3194b8cab8dbc882f37c13ef1262e0a3d62064fa97533d3aa124771f7bf1ecee"}, + {file = "aiohttp-3.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:7897298b3eedc790257fef8a6ec582ca04e9dbe568ba4a9a890913b925b8ea21"}, + {file = "aiohttp-3.13.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:c417f8c2e1137775569297c584a8a7144e5d1237789eae56af4faf1894a0b861"}, + {file = "aiohttp-3.13.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:f84b53326abf8e56ebc28a35cebf4a0f396a13a76300f500ab11fe0573bf0b52"}, + {file = "aiohttp-3.13.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:990a53b9d6a30b2878789e490758e568b12b4a7fb2527d0c89deb9650b0e5813"}, + {file = "aiohttp-3.13.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c811612711e01b901e18964b3e5dec0d35525150f5f3f85d0aee2935f059910a"}, + {file = "aiohttp-3.13.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:ee433e594d7948e760b5c2a78cc06ac219df33b0848793cf9513d486a9f90a52"}, + {file = "aiohttp-3.13.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:19bb08e56f57c215e9572cd65cb6f8097804412c54081d933997ddde3e5ac579"}, + {file = "aiohttp-3.13.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f27b7488144eb5dd9151cf839b195edd1569629d90ace4c5b6b18e4e75d1e63a"}, + {file = "aiohttp-3.13.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d812838c109757a11354a161c95708ae4199c4fd4d82b90959b20914c1d097f6"}, + {file = "aiohttp-3.13.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7c20db99da682f9180fa5195c90b80b159632fb611e8dbccdd99ba0be0970620"}, + {file = "aiohttp-3.13.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:cf8b0870047900eb1f17f453b4b3953b8ffbf203ef56c2f346780ff930a4d430"}, + {file = "aiohttp-3.13.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:5b8a5557d5af3f4e3add52a58c4cf2b8e6e59fc56b261768866f5337872d596d"}, + {file = "aiohttp-3.13.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:052bcdd80c1c54b8a18a9ea0cd5e36f473dc8e38d51b804cea34841f677a9971"}, + {file = "aiohttp-3.13.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:76484ba17b2832776581b7ab466d094e48eba74cb65a60aea20154dae485e8bd"}, + {file = "aiohttp-3.13.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:62d8a0adcdaf62ee56bfb37737153251ac8e4b27845b3ca065862fb01d99e247"}, + {file = "aiohttp-3.13.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5004d727499ecb95f7c9147dd0bfc5b5670f71d355f0bd26d7af2d3af8e07d2f"}, + {file = "aiohttp-3.13.0-cp314-cp314-win32.whl", hash = "sha256:a1c20c26af48aea984f63f96e5d7af7567c32cb527e33b60a0ef0a6313cf8b03"}, + {file = "aiohttp-3.13.0-cp314-cp314-win_amd64.whl", hash = "sha256:56f7d230ec66e799fbfd8350e9544f8a45a4353f1cf40c1fea74c1780f555b8f"}, + {file = "aiohttp-3.13.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:2fd35177dc483ae702f07b86c782f4f4b100a8ce4e7c5778cea016979023d9fd"}, + {file = "aiohttp-3.13.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:4df1984c8804ed336089e88ac81a9417b1fd0db7c6f867c50a9264488797e778"}, + {file = "aiohttp-3.13.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:e68c0076052dd911a81d3acc4ef2911cc4ef65bf7cadbfbc8ae762da24da858f"}, + {file = "aiohttp-3.13.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bc95c49853cd29613e4fe4ff96d73068ff89b89d61e53988442e127e8da8e7ba"}, + {file = "aiohttp-3.13.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3b3bdc89413117b40cc39baae08fd09cbdeb839d421c4e7dce6a34f6b54b3ac1"}, + {file = "aiohttp-3.13.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3e77a729df23be2116acc4e9de2767d8e92445fbca68886dd991dc912f473755"}, + {file = "aiohttp-3.13.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e88ab34826d6eeb6c67e6e92400b9ec653faf5092a35f07465f44c9f1c429f82"}, + {file = "aiohttp-3.13.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:019dbef24fe28ce2301419dd63a2b97250d9760ca63ee2976c2da2e3f182f82e"}, + {file = "aiohttp-3.13.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:2c4aeaedd20771b7b4bcdf0ae791904445df6d856c02fc51d809d12d17cffdc7"}, + {file = "aiohttp-3.13.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:b3a8e6a2058a0240cfde542b641d0e78b594311bc1a710cbcb2e1841417d5cb3"}, + {file = "aiohttp-3.13.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:f8e38d55ca36c15f36d814ea414ecb2401d860de177c49f84a327a25b3ee752b"}, + {file = "aiohttp-3.13.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:a921edbe971aade1bf45bcbb3494e30ba6863a5c78f28be992c42de980fd9108"}, + {file = "aiohttp-3.13.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:474cade59a447cb4019c0dce9f0434bf835fb558ea932f62c686fe07fe6db6a1"}, + {file = "aiohttp-3.13.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:99a303ad960747c33b65b1cb65d01a62ac73fa39b72f08a2e1efa832529b01ed"}, + {file = "aiohttp-3.13.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:bb34001fc1f05f6b323e02c278090c07a47645caae3aa77ed7ed8a3ce6abcce9"}, + {file = "aiohttp-3.13.0-cp314-cp314t-win32.whl", hash = "sha256:dea698b64235d053def7d2f08af9302a69fcd760d1c7bd9988fd5d3b6157e657"}, + {file = "aiohttp-3.13.0-cp314-cp314t-win_amd64.whl", hash = "sha256:1f164699a060c0b3616459d13c1464a981fddf36f892f0a5027cbd45121fb14b"}, + {file = "aiohttp-3.13.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:fcc425fb6fd2a00c6d91c85d084c6b75a61bc8bc12159d08e17c5711df6c5ba4"}, + {file = "aiohttp-3.13.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7c2c4c9ce834801651f81d6760d0a51035b8b239f58f298de25162fcf6f8bb64"}, + {file = "aiohttp-3.13.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f91e8f9053a07177868e813656ec57599cd2a63238844393cd01bd69c2e40147"}, + {file = "aiohttp-3.13.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:df46d9a3d78ec19b495b1107bf26e4fcf97c900279901f4f4819ac5bb2a02a4c"}, + {file = "aiohttp-3.13.0-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3b1eb9871cbe43b6ca6fac3544682971539d8a1d229e6babe43446279679609d"}, + {file = "aiohttp-3.13.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:62a3cddf8d9a2eae1f79585fa81d32e13d0c509bb9e7ad47d33c83b45a944df7"}, + {file = "aiohttp-3.13.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0f735e680c323ee7e9ef8e2ea26425c7dbc2ede0086fa83ce9d7ccab8a089f26"}, + {file = "aiohttp-3.13.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6a51839f778b0e283b43cd82bb17f1835ee2cc1bf1101765e90ae886e53e751c"}, + {file = "aiohttp-3.13.0-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ac90cfab65bc281d6752f22db5fa90419e33220af4b4fa53b51f5948f414c0e7"}, + {file = "aiohttp-3.13.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:62fd54f3e6f17976962ba67f911d62723c760a69d54f5d7b74c3ceb1a4e9ef8d"}, + {file = "aiohttp-3.13.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:cf2b60b65df05b6b2fa0d887f2189991a0dbf44a0dd18359001dc8fcdb7f1163"}, + {file = "aiohttp-3.13.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:1ccedfe280e804d9a9d7fe8b8c4309d28e364b77f40309c86596baa754af50b1"}, + {file = "aiohttp-3.13.0-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:ea01ffbe23df53ece0c8732d1585b3d6079bb8c9ee14f3745daf000051415a31"}, + {file = "aiohttp-3.13.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:19ba8625fa69523627b67f7e9901b587a4952470f68814d79cdc5bc460e9b885"}, + {file = "aiohttp-3.13.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:4b14bfae90598d331b5061fd15a7c290ea0c15b34aeb1cf620464bb5ec02a602"}, + {file = "aiohttp-3.13.0-cp39-cp39-win32.whl", hash = "sha256:cf7a4b976da219e726d0043fc94ae8169c0dba1d3a059b3c1e2c964bafc5a77d"}, + {file = "aiohttp-3.13.0-cp39-cp39-win_amd64.whl", hash = "sha256:6b9697d15231aeaed4786f090c9c8bc3ab5f0e0a6da1e76c135a310def271020"}, + {file = "aiohttp-3.13.0.tar.gz", hash = "sha256:378dbc57dd8cf341ce243f13fa1fa5394d68e2e02c15cd5f28eae35a70ec7f67"}, +] + +[package.dependencies] +aiohappyeyeballs = ">=2.5.0" +aiosignal = ">=1.4.0" +attrs = ">=17.3.0" +frozenlist = ">=1.1.1" +multidict = ">=4.5,<7.0" +propcache = ">=0.2.0" +yarl = ">=1.17.0,<2.0" + +[package.extras] +speedups = ["Brotli", "aiodns (>=3.3.0)", "brotlicffi", "zstandard"] + +[[package]] +name = "aiosignal" +version = "1.4.0" +description = "aiosignal: a list of registered asynchronous callbacks" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e"}, + {file = "aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7"}, +] + +[package.dependencies] +frozenlist = ">=1.1.0" +typing-extensions = {version = ">=4.2", markers = "python_version < \"3.13\""} + +[[package]] +name = "annotated-types" +version = "0.7.0" +description = "Reusable constraint types to use with typing.Annotated" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, + {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, +] + +[[package]] +name = "anyio" +version = "4.11.0" +description = "High-level concurrency and networking framework on top of asyncio or Trio" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "anyio-4.11.0-py3-none-any.whl", hash = "sha256:0287e96f4d26d4149305414d4e3bc32f0dcd0862365a4bddea19d7a1ec38c4fc"}, + {file = "anyio-4.11.0.tar.gz", hash = "sha256:82a8d0b81e318cc5ce71a5f1f8b5c4e63619620b63141ef8c995fa0db95a57c4"}, +] + +[package.dependencies] +idna = ">=2.8" +sniffio = ">=1.1" +typing_extensions = {version = ">=4.5", markers = "python_version < \"3.13\""} + +[package.extras] +trio = ["trio (>=0.31.0)"] + +[[package]] +name = "argon2-cffi" +version = "25.1.0" +description = "Argon2 for Python" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "argon2_cffi-25.1.0-py3-none-any.whl", hash = "sha256:fdc8b074db390fccb6eb4a3604ae7231f219aa669a2652e0f20e16ba513d5741"}, + {file = "argon2_cffi-25.1.0.tar.gz", hash = "sha256:694ae5cc8a42f4c4e2bf2ca0e64e51e23a040c6a517a85074683d3959e1346c1"}, +] + +[package.dependencies] +argon2-cffi-bindings = "*" + +[[package]] +name = "argon2-cffi-bindings" +version = "25.1.0" +description = "Low-level CFFI bindings for Argon2" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:3d3f05610594151994ca9ccb3c771115bdb4daef161976a266f0dd8aa9996b8f"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:8b8efee945193e667a396cbc7b4fb7d357297d6234d30a489905d96caabde56b"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:3c6702abc36bf3ccba3f802b799505def420a1b7039862014a65db3205967f5a"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a1c70058c6ab1e352304ac7e3b52554daadacd8d453c1752e547c76e9c99ac44"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e2fd3bfbff3c5d74fef31a722f729bf93500910db650c925c2d6ef879a7e51cb"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c4f9665de60b1b0e99bcd6be4f17d90339698ce954cfd8d9cf4f91c995165a92"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ba92837e4a9aa6a508c8d2d7883ed5a8f6c308c89a4790e1e447a220deb79a85"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-win32.whl", hash = "sha256:84a461d4d84ae1295871329b346a97f68eade8c53b6ed9a7ca2d7467f3c8ff6f"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b55aec3565b65f56455eebc9b9f34130440404f27fe21c3b375bf1ea4d8fbae6"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:87c33a52407e4c41f3b70a9c2d3f6056d88b10dad7695be708c5021673f55623"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:aecba1723ae35330a008418a91ea6cfcedf6d31e5fbaa056a166462ff066d500"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:2630b6240b495dfab90aebe159ff784d08ea999aa4b0d17efa734055a07d2f44"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:7aef0c91e2c0fbca6fc68e7555aa60ef7008a739cbe045541e438373bc54d2b0"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e021e87faa76ae0d413b619fe2b65ab9a037f24c60a1e6cc43457ae20de6dc6"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d3e924cfc503018a714f94a49a149fdc0b644eaead5d1f089330399134fa028a"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:c87b72589133f0346a1cb8d5ecca4b933e3c9b64656c9d175270a000e73b288d"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1db89609c06afa1a214a69a462ea741cf735b29a57530478c06eb81dd403de99"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-win32.whl", hash = "sha256:473bcb5f82924b1becbb637b63303ec8d10e84c8d241119419897a26116515d2"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-win_amd64.whl", hash = "sha256:a98cd7d17e9f7ce244c0803cad3c23a7d379c301ba618a5fa76a67d116618b98"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-win_arm64.whl", hash = "sha256:b0fdbcf513833809c882823f98dc2f931cf659d9a1429616ac3adebb49f5db94"}, + {file = "argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:6dca33a9859abf613e22733131fc9194091c1fa7cb3e131c143056b4856aa47e"}, + {file = "argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:21378b40e1b8d1655dd5310c84a40fc19a9aa5e6366e835ceb8576bf0fea716d"}, + {file = "argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d588dec224e2a83edbdc785a5e6f3c6cd736f46bfd4b441bbb5aa1f5085e584"}, + {file = "argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5acb4e41090d53f17ca1110c3427f0a130f944b896fc8c83973219c97f57b690"}, + {file = "argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:da0c79c23a63723aa5d782250fbf51b768abca630285262fb5144ba5ae01e520"}, + {file = "argon2_cffi_bindings-25.1.0.tar.gz", hash = "sha256:b957f3e6ea4d55d820e40ff76f450952807013d361a65d7f28acc0acbf29229d"}, +] + +[package.dependencies] +cffi = [ + {version = ">=1.0.1", markers = "python_version < \"3.14\""}, + {version = ">=2.0.0b1", markers = "python_version >= \"3.14\""}, +] + +[[package]] +name = "asyncpg" +version = "0.30.0" +description = "An asyncio PostgreSQL driver" +optional = false +python-versions = ">=3.8.0" +groups = ["main"] +files = [ + {file = "asyncpg-0.30.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bfb4dd5ae0699bad2b233672c8fc5ccbd9ad24b89afded02341786887e37927e"}, + {file = "asyncpg-0.30.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dc1f62c792752a49f88b7e6f774c26077091b44caceb1983509edc18a2222ec0"}, + {file = "asyncpg-0.30.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3152fef2e265c9c24eec4ee3d22b4f4d2703d30614b0b6753e9ed4115c8a146f"}, + {file = "asyncpg-0.30.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c7255812ac85099a0e1ffb81b10dc477b9973345793776b128a23e60148dd1af"}, + {file = "asyncpg-0.30.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:578445f09f45d1ad7abddbff2a3c7f7c291738fdae0abffbeb737d3fc3ab8b75"}, + {file = "asyncpg-0.30.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c42f6bb65a277ce4d93f3fba46b91a265631c8df7250592dd4f11f8b0152150f"}, + {file = "asyncpg-0.30.0-cp310-cp310-win32.whl", hash = "sha256:aa403147d3e07a267ada2ae34dfc9324e67ccc4cdca35261c8c22792ba2b10cf"}, + {file = "asyncpg-0.30.0-cp310-cp310-win_amd64.whl", hash = "sha256:fb622c94db4e13137c4c7f98834185049cc50ee01d8f657ef898b6407c7b9c50"}, + {file = "asyncpg-0.30.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5e0511ad3dec5f6b4f7a9e063591d407eee66b88c14e2ea636f187da1dcfff6a"}, + {file = "asyncpg-0.30.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:915aeb9f79316b43c3207363af12d0e6fd10776641a7de8a01212afd95bdf0ed"}, + {file = "asyncpg-0.30.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c198a00cce9506fcd0bf219a799f38ac7a237745e1d27f0e1f66d3707c84a5a"}, + {file = "asyncpg-0.30.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3326e6d7381799e9735ca2ec9fd7be4d5fef5dcbc3cb555d8a463d8460607956"}, + {file = "asyncpg-0.30.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:51da377487e249e35bd0859661f6ee2b81db11ad1f4fc036194bc9cb2ead5056"}, + {file = "asyncpg-0.30.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bc6d84136f9c4d24d358f3b02be4b6ba358abd09f80737d1ac7c444f36108454"}, + {file = "asyncpg-0.30.0-cp311-cp311-win32.whl", hash = "sha256:574156480df14f64c2d76450a3f3aaaf26105869cad3865041156b38459e935d"}, + {file = "asyncpg-0.30.0-cp311-cp311-win_amd64.whl", hash = "sha256:3356637f0bd830407b5597317b3cb3571387ae52ddc3bca6233682be88bbbc1f"}, + {file = "asyncpg-0.30.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c902a60b52e506d38d7e80e0dd5399f657220f24635fee368117b8b5fce1142e"}, + {file = "asyncpg-0.30.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:aca1548e43bbb9f0f627a04666fedaca23db0a31a84136ad1f868cb15deb6e3a"}, + {file = "asyncpg-0.30.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c2a2ef565400234a633da0eafdce27e843836256d40705d83ab7ec42074efb3"}, + {file = "asyncpg-0.30.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1292b84ee06ac8a2ad8e51c7475aa309245874b61333d97411aab835c4a2f737"}, + {file = "asyncpg-0.30.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0f5712350388d0cd0615caec629ad53c81e506b1abaaf8d14c93f54b35e3595a"}, + {file = "asyncpg-0.30.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:db9891e2d76e6f425746c5d2da01921e9a16b5a71a1c905b13f30e12a257c4af"}, + {file = "asyncpg-0.30.0-cp312-cp312-win32.whl", hash = "sha256:68d71a1be3d83d0570049cd1654a9bdfe506e794ecc98ad0873304a9f35e411e"}, + {file = "asyncpg-0.30.0-cp312-cp312-win_amd64.whl", hash = "sha256:9a0292c6af5c500523949155ec17b7fe01a00ace33b68a476d6b5059f9630305"}, + {file = "asyncpg-0.30.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:05b185ebb8083c8568ea8a40e896d5f7af4b8554b64d7719c0eaa1eb5a5c3a70"}, + {file = "asyncpg-0.30.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c47806b1a8cbb0a0db896f4cd34d89942effe353a5035c62734ab13b9f938da3"}, + {file = "asyncpg-0.30.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b6fde867a74e8c76c71e2f64f80c64c0f3163e687f1763cfaf21633ec24ec33"}, + {file = "asyncpg-0.30.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46973045b567972128a27d40001124fbc821c87a6cade040cfcd4fa8a30bcdc4"}, + {file = "asyncpg-0.30.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9110df111cabc2ed81aad2f35394a00cadf4f2e0635603db6ebbd0fc896f46a4"}, + {file = "asyncpg-0.30.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:04ff0785ae7eed6cc138e73fc67b8e51d54ee7a3ce9b63666ce55a0bf095f7ba"}, + {file = "asyncpg-0.30.0-cp313-cp313-win32.whl", hash = "sha256:ae374585f51c2b444510cdf3595b97ece4f233fde739aa14b50e0d64e8a7a590"}, + {file = "asyncpg-0.30.0-cp313-cp313-win_amd64.whl", hash = "sha256:f59b430b8e27557c3fb9869222559f7417ced18688375825f8f12302c34e915e"}, + {file = "asyncpg-0.30.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:29ff1fc8b5bf724273782ff8b4f57b0f8220a1b2324184846b39d1ab4122031d"}, + {file = "asyncpg-0.30.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:64e899bce0600871b55368b8483e5e3e7f1860c9482e7f12e0a771e747988168"}, + {file = "asyncpg-0.30.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b290f4726a887f75dcd1b3006f484252db37602313f806e9ffc4e5996cfe5cb"}, + {file = "asyncpg-0.30.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f86b0e2cd3f1249d6fe6fd6cfe0cd4538ba994e2d8249c0491925629b9104d0f"}, + {file = "asyncpg-0.30.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:393af4e3214c8fa4c7b86da6364384c0d1b3298d45803375572f415b6f673f38"}, + {file = "asyncpg-0.30.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:fd4406d09208d5b4a14db9a9dbb311b6d7aeeab57bded7ed2f8ea41aeef39b34"}, + {file = "asyncpg-0.30.0-cp38-cp38-win32.whl", hash = "sha256:0b448f0150e1c3b96cb0438a0d0aa4871f1472e58de14a3ec320dbb2798fb0d4"}, + {file = "asyncpg-0.30.0-cp38-cp38-win_amd64.whl", hash = "sha256:f23b836dd90bea21104f69547923a02b167d999ce053f3d502081acea2fba15b"}, + {file = "asyncpg-0.30.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6f4e83f067b35ab5e6371f8a4c93296e0439857b4569850b178a01385e82e9ad"}, + {file = "asyncpg-0.30.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5df69d55add4efcd25ea2a3b02025b669a285b767bfbf06e356d68dbce4234ff"}, + {file = "asyncpg-0.30.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3479a0d9a852c7c84e822c073622baca862d1217b10a02dd57ee4a7a081f708"}, + {file = "asyncpg-0.30.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26683d3b9a62836fad771a18ecf4659a30f348a561279d6227dab96182f46144"}, + {file = "asyncpg-0.30.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1b982daf2441a0ed314bd10817f1606f1c28b1136abd9e4f11335358c2c631cb"}, + {file = "asyncpg-0.30.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1c06a3a50d014b303e5f6fc1e5f95eb28d2cee89cf58384b700da621e5d5e547"}, + {file = "asyncpg-0.30.0-cp39-cp39-win32.whl", hash = "sha256:1b11a555a198b08f5c4baa8f8231c74a366d190755aa4f99aacec5970afe929a"}, + {file = "asyncpg-0.30.0-cp39-cp39-win_amd64.whl", hash = "sha256:8b684a3c858a83cd876f05958823b68e8d14ec01bb0c0d14a6704c5bf9711773"}, + {file = "asyncpg-0.30.0.tar.gz", hash = "sha256:c551e9928ab6707602f44811817f82ba3c446e018bfe1d3abecc8ba5f3eac851"}, +] + +[package.extras] +docs = ["Sphinx (>=8.1.3,<8.2.0)", "sphinx-rtd-theme (>=1.2.2)"] +gssauth = ["gssapi", "sspilib"] +test = ["distro (>=1.9.0,<1.10.0)", "flake8 (>=6.1,<7.0)", "flake8-pyi (>=24.1.0,<24.2.0)", "gssapi", "k5test", "mypy (>=1.8.0,<1.9.0)", "sspilib", "uvloop (>=0.15.3)"] + +[[package]] +name = "attrs" +version = "25.4.0" +description = "Classes Without Boilerplate" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373"}, + {file = "attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11"}, +] + +[[package]] +name = "blinker" +version = "1.9.0" +description = "Fast, simple object-to-object and broadcast signaling" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "blinker-1.9.0-py3-none-any.whl", hash = "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc"}, + {file = "blinker-1.9.0.tar.gz", hash = "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf"}, +] + +[[package]] +name = "certifi" +version = "2025.10.5" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "certifi-2025.10.5-py3-none-any.whl", hash = "sha256:0f212c2744a9bb6de0c56639a6f68afe01ecd92d91f14ae897c4fe7bbeeef0de"}, + {file = "certifi-2025.10.5.tar.gz", hash = "sha256:47c09d31ccf2acf0be3f701ea53595ee7e0b8fa08801c6624be771df09ae7b43"}, +] + +[[package]] +name = "cffi" +version = "2.0.0" +description = "Foreign Function Interface for Python calling C code." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44"}, + {file = "cffi-2.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49"}, + {file = "cffi-2.0.0-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c"}, + {file = "cffi-2.0.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb"}, + {file = "cffi-2.0.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0"}, + {file = "cffi-2.0.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4"}, + {file = "cffi-2.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453"}, + {file = "cffi-2.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495"}, + {file = "cffi-2.0.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5"}, + {file = "cffi-2.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb"}, + {file = "cffi-2.0.0-cp310-cp310-win32.whl", hash = "sha256:1f72fb8906754ac8a2cc3f9f5aaa298070652a0ffae577e0ea9bd480dc3c931a"}, + {file = "cffi-2.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:b18a3ed7d5b3bd8d9ef7a8cb226502c6bf8308df1525e1cc676c3680e7176739"}, + {file = "cffi-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe"}, + {file = "cffi-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c"}, + {file = "cffi-2.0.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92"}, + {file = "cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93"}, + {file = "cffi-2.0.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5"}, + {file = "cffi-2.0.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664"}, + {file = "cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26"}, + {file = "cffi-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9"}, + {file = "cffi-2.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414"}, + {file = "cffi-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743"}, + {file = "cffi-2.0.0-cp311-cp311-win32.whl", hash = "sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5"}, + {file = "cffi-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5"}, + {file = "cffi-2.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d"}, + {file = "cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d"}, + {file = "cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c"}, + {file = "cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe"}, + {file = "cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062"}, + {file = "cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e"}, + {file = "cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037"}, + {file = "cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba"}, + {file = "cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94"}, + {file = "cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187"}, + {file = "cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18"}, + {file = "cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5"}, + {file = "cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6"}, + {file = "cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb"}, + {file = "cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca"}, + {file = "cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b"}, + {file = "cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b"}, + {file = "cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2"}, + {file = "cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3"}, + {file = "cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26"}, + {file = "cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c"}, + {file = "cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b"}, + {file = "cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27"}, + {file = "cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75"}, + {file = "cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91"}, + {file = "cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5"}, + {file = "cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13"}, + {file = "cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b"}, + {file = "cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c"}, + {file = "cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef"}, + {file = "cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775"}, + {file = "cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205"}, + {file = "cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1"}, + {file = "cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f"}, + {file = "cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25"}, + {file = "cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad"}, + {file = "cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9"}, + {file = "cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d"}, + {file = "cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c"}, + {file = "cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8"}, + {file = "cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc"}, + {file = "cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592"}, + {file = "cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512"}, + {file = "cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4"}, + {file = "cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e"}, + {file = "cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6"}, + {file = "cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9"}, + {file = "cffi-2.0.0-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:fe562eb1a64e67dd297ccc4f5addea2501664954f2692b69a76449ec7913ecbf"}, + {file = "cffi-2.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:de8dad4425a6ca6e4e5e297b27b5c824ecc7581910bf9aee86cb6835e6812aa7"}, + {file = "cffi-2.0.0-cp39-cp39-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:4647afc2f90d1ddd33441e5b0e85b16b12ddec4fca55f0d9671fef036ecca27c"}, + {file = "cffi-2.0.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3f4d46d8b35698056ec29bca21546e1551a205058ae1a181d871e278b0b28165"}, + {file = "cffi-2.0.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:e6e73b9e02893c764e7e8d5bb5ce277f1a009cd5243f8228f75f842bf937c534"}, + {file = "cffi-2.0.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:cb527a79772e5ef98fb1d700678fe031e353e765d1ca2d409c92263c6d43e09f"}, + {file = "cffi-2.0.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:61d028e90346df14fedc3d1e5441df818d095f3b87d286825dfcbd6459b7ef63"}, + {file = "cffi-2.0.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0f6084a0ea23d05d20c3edcda20c3d006f9b6f3fefeac38f59262e10cef47ee2"}, + {file = "cffi-2.0.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:1cd13c99ce269b3ed80b417dcd591415d3372bcac067009b6e0f59c7d4015e65"}, + {file = "cffi-2.0.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:89472c9762729b5ae1ad974b777416bfda4ac5642423fa93bd57a09204712322"}, + {file = "cffi-2.0.0-cp39-cp39-win32.whl", hash = "sha256:2081580ebb843f759b9f617314a24ed5738c51d2aee65d31e02f6f7a2b97707a"}, + {file = "cffi-2.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:b882b3df248017dba09d6b16defe9b5c407fe32fc7c65a9c69798e6175601be9"}, + {file = "cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529"}, +] + +[package.dependencies] +pycparser = {version = "*", markers = "implementation_name != \"PyPy\""} + +[[package]] +name = "charset-normalizer" +version = "3.4.4" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "charset_normalizer-3.4.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e824f1492727fa856dd6eda4f7cee25f8518a12f3c4a56a74e8095695089cf6d"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4bd5d4137d500351a30687c2d3971758aac9a19208fc110ccb9d7188fbe709e8"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:027f6de494925c0ab2a55eab46ae5129951638a49a34d87f4c3eda90f696b4ad"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f820802628d2694cb7e56db99213f930856014862f3fd943d290ea8438d07ca8"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:798d75d81754988d2565bff1b97ba5a44411867c0cf32b77a7e8f8d84796b10d"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d1bb833febdff5c8927f922386db610b49db6e0d4f4ee29601d71e7c2694313"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9cd98cdc06614a2f768d2b7286d66805f94c48cde050acdbbb7db2600ab3197e"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:077fbb858e903c73f6c9db43374fd213b0b6a778106bc7032446a8e8b5b38b93"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:244bfb999c71b35de57821b8ea746b24e863398194a4014e4c76adc2bbdfeff0"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:64b55f9dce520635f018f907ff1b0df1fdc31f2795a922fb49dd14fbcdf48c84"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:faa3a41b2b66b6e50f84ae4a68c64fcd0c44355741c6374813a800cd6695db9e"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6515f3182dbe4ea06ced2d9e8666d97b46ef4c75e326b79bb624110f122551db"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cc00f04ed596e9dc0da42ed17ac5e596c6ccba999ba6bd92b0e0aef2f170f2d6"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-win32.whl", hash = "sha256:f34be2938726fc13801220747472850852fe6b1ea75869a048d6f896838c896f"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:a61900df84c667873b292c3de315a786dd8dac506704dea57bc957bd31e22c7d"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-win_arm64.whl", hash = "sha256:cead0978fc57397645f12578bfd2d5ea9138ea0fac82b2f63f7f7c6877986a69"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6e1fcf0720908f200cd21aa4e6750a48ff6ce4afe7ff5a79a90d5ed8a08296f8"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f819d5fe9234f9f82d75bdfa9aef3a3d72c4d24a6e57aeaebba32a704553aa0"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a59cb51917aa591b1c4e6a43c132f0cdc3c76dbad6155df4e28ee626cc77a0a3"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8ef3c867360f88ac904fd3f5e1f902f13307af9052646963ee08ff4f131adafc"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d9e45d7faa48ee908174d8fe84854479ef838fc6a705c9315372eacbc2f02897"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:840c25fb618a231545cbab0564a799f101b63b9901f2569faecd6b222ac72381"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ca5862d5b3928c4940729dacc329aa9102900382fea192fc5e52eb69d6093815"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d9c7f57c3d666a53421049053eaacdd14bbd0a528e2186fcb2e672effd053bb0"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:277e970e750505ed74c832b4bf75dac7476262ee2a013f5574dd49075879e161"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:31fd66405eaf47bb62e8cd575dc621c56c668f27d46a61d975a249930dd5e2a4"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:0d3d8f15c07f86e9ff82319b3d9ef6f4bf907608f53fe9d92b28ea9ae3d1fd89"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:9f7fcd74d410a36883701fafa2482a6af2ff5ba96b9a620e9e0721e28ead5569"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ebf3e58c7ec8a8bed6d66a75d7fb37b55e5015b03ceae72a8e7c74495551e224"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-win32.whl", hash = "sha256:eecbc200c7fd5ddb9a7f16c7decb07b566c29fa2161a16cf67b8d068bd21690a"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:5ae497466c7901d54b639cf42d5b8c1b6a4fead55215500d2f486d34db48d016"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-win_arm64.whl", hash = "sha256:65e2befcd84bc6f37095f5961e68a6f077bf44946771354a28ad434c2cce0ae1"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0a98e6759f854bd25a58a73fa88833fba3b7c491169f86ce1180c948ab3fd394"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b5b290ccc2a263e8d185130284f8501e3e36c5e02750fc6b6bdeb2e9e96f1e25"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74bb723680f9f7a6234dcf67aea57e708ec1fbdf5699fb91dfd6f511b0a320ef"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f1e34719c6ed0b92f418c7c780480b26b5d9c50349e9a9af7d76bf757530350d"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2437418e20515acec67d86e12bf70056a33abdacb5cb1655042f6538d6b085a8"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11d694519d7f29d6cd09f6ac70028dba10f92f6cdd059096db198c283794ac86"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ac1c4a689edcc530fc9d9aa11f5774b9e2f33f9a0c6a57864e90908f5208d30a"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:21d142cc6c0ec30d2efee5068ca36c128a30b0f2c53c1c07bd78cb6bc1d3be5f"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:5dbe56a36425d26d6cfb40ce79c314a2e4dd6211d51d6d2191c00bed34f354cc"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5bfbb1b9acf3334612667b61bd3002196fe2a1eb4dd74d247e0f2a4d50ec9bbf"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:d055ec1e26e441f6187acf818b73564e6e6282709e9bcb5b63f5b23068356a15"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:af2d8c67d8e573d6de5bc30cdb27e9b95e49115cd9baad5ddbd1a6207aaa82a9"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:780236ac706e66881f3b7f2f32dfe90507a09e67d1d454c762cf642e6e1586e0"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-win32.whl", hash = "sha256:5833d2c39d8896e4e19b689ffc198f08ea58116bee26dea51e362ecc7cd3ed26"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:a79cfe37875f822425b89a82333404539ae63dbdddf97f84dcbc3d339aae9525"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:376bec83a63b8021bb5c8ea75e21c4ccb86e7e45ca4eb81146091b56599b80c3"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-win32.whl", hash = "sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-win32.whl", hash = "sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ce8a0633f41a967713a59c4139d29110c07e826d131a316b50ce11b1d79b4f84"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eaabd426fe94daf8fd157c32e571c85cb12e66692f15516a83a03264b08d06c3"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c4ef880e27901b6cc782f1b95f82da9313c0eb95c3af699103088fa0ac3ce9ac"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2aaba3b0819274cc41757a1da876f810a3e4d7b6eb25699253a4effef9e8e4af"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:778d2e08eda00f4256d7f672ca9fef386071c9202f5e4607920b86d7803387f2"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f155a433c2ec037d4e8df17d18922c3a0d9b3232a396690f17175d2946f0218d"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a8bf8d0f749c5757af2142fe7903a9df1d2e8aa3841559b2bad34b08d0e2bcf3"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:194f08cbb32dc406d6e1aea671a68be0823673db2832b38405deba2fb0d88f63"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_armv7l.whl", hash = "sha256:6aee717dcfead04c6eb1ce3bd29ac1e22663cdea57f943c87d1eab9a025438d7"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:cd4b7ca9984e5e7985c12bc60a6f173f3c958eae74f3ef6624bb6b26e2abbae4"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_riscv64.whl", hash = "sha256:b7cf1017d601aa35e6bb650b6ad28652c9cd78ee6caff19f3c28d03e1c80acbf"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:e912091979546adf63357d7e2ccff9b44f026c075aeaf25a52d0e95ad2281074"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:5cb4d72eea50c8868f5288b7f7f33ed276118325c1dfd3957089f6b519e1382a"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-win32.whl", hash = "sha256:837c2ce8c5a65a2035be9b3569c684358dfbf109fd3b6969630a87535495ceaa"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:44c2a8734b333e0578090c4cd6b16f275e07aa6614ca8715e6c038e865e70576"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a9768c477b9d7bd54bc0c86dbaebdec6f03306675526c9927c0e8a04e8f94af9"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1bee1e43c28aa63cb16e5c14e582580546b08e535299b8b6158a7c9c768a1f3d"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:fd44c878ea55ba351104cb93cc85e74916eb8fa440ca7903e57575e97394f608"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0f04b14ffe5fdc8c4933862d8306109a2c51e0704acfa35d51598eb45a1e89fc"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:cd09d08005f958f370f539f186d10aec3377d55b9eeb0d796025d4886119d76e"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4fe7859a4e3e8457458e2ff592f15ccb02f3da787fcd31e0183879c3ad4692a1"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fa09f53c465e532f4d3db095e0c55b615f010ad81803d383195b6b5ca6cbf5f3"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:7fa17817dc5625de8a027cb8b26d9fefa3ea28c8253929b8d6649e705d2835b6"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:5947809c8a2417be3267efc979c47d76a079758166f7d43ef5ae8e9f92751f88"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:4902828217069c3c5c71094537a8e623f5d097858ac6ca8252f7b4d10b7560f1"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:7c308f7e26e4363d79df40ca5b2be1c6ba9f02bdbccfed5abddb7859a6ce72cf"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:2c9d3c380143a1fedbff95a312aa798578371eb29da42106a29019368a475318"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:cb01158d8b88ee68f15949894ccc6712278243d95f344770fa7593fa2d94410c"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-win32.whl", hash = "sha256:2677acec1a2f8ef614c6888b5b4ae4060cc184174a938ed4e8ef690e15d3e505"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:f8e160feb2aed042cd657a72acc0b481212ed28b1b9a95c0cee1621b524e1966"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-win_arm64.whl", hash = "sha256:b5d84d37db046c5ca74ee7bb47dd6cbc13f80665fdde3e8040bdd3fb015ecb50"}, + {file = "charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f"}, + {file = "charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a"}, +] + +[[package]] +name = "click" +version = "8.3.0" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "click-8.3.0-py3-none-any.whl", hash = "sha256:9b9f285302c6e3064f4330c05f05b81945b2a39544279343e6e7c5f27a9baddc"}, + {file = "click-8.3.0.tar.gz", hash = "sha256:e7b8232224eba16f4ebe410c25ced9f7875cb5f3263ffc93cc3e8da705e229c4"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["main", "dev"] +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] +markers = {main = "platform_system == \"Windows\"", dev = "sys_platform == \"win32\""} + +[[package]] +name = "common" +version = "0.1.0" +description = "" +optional = false +python-versions = ">=3.12,<4.0" +groups = ["main"] +files = [] +develop = true + +[package.dependencies] +dapr = "1.16.0" +fastapi = "^0.115.6" +minio = "^7.2.14" +pydantic = "^2.10.5" +structlog = "^25.1.0" + +[package.source] +type = "directory" +url = "../common" + +[[package]] +name = "cryptography" +version = "42.0.8" +description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "cryptography-42.0.8-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:81d8a521705787afe7a18d5bfb47ea9d9cc068206270aad0b96a725022e18d2e"}, + {file = "cryptography-42.0.8-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:961e61cefdcb06e0c6d7e3a1b22ebe8b996eb2bf50614e89384be54c48c6b63d"}, + {file = "cryptography-42.0.8-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e3ec3672626e1b9e55afd0df6d774ff0e953452886e06e0f1eb7eb0c832e8902"}, + {file = "cryptography-42.0.8-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e599b53fd95357d92304510fb7bda8523ed1f79ca98dce2f43c115950aa78801"}, + {file = "cryptography-42.0.8-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:5226d5d21ab681f432a9c1cf8b658c0cb02533eece706b155e5fbd8a0cdd3949"}, + {file = "cryptography-42.0.8-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:6b7c4f03ce01afd3b76cf69a5455caa9cfa3de8c8f493e0d3ab7d20611c8dae9"}, + {file = "cryptography-42.0.8-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:2346b911eb349ab547076f47f2e035fc8ff2c02380a7cbbf8d87114fa0f1c583"}, + {file = "cryptography-42.0.8-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:ad803773e9df0b92e0a817d22fd8a3675493f690b96130a5e24f1b8fabbea9c7"}, + {file = "cryptography-42.0.8-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:2f66d9cd9147ee495a8374a45ca445819f8929a3efcd2e3df6428e46c3cbb10b"}, + {file = "cryptography-42.0.8-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:d45b940883a03e19e944456a558b67a41160e367a719833c53de6911cabba2b7"}, + {file = "cryptography-42.0.8-cp37-abi3-win32.whl", hash = "sha256:a0c5b2b0585b6af82d7e385f55a8bc568abff8923af147ee3c07bd8b42cda8b2"}, + {file = "cryptography-42.0.8-cp37-abi3-win_amd64.whl", hash = "sha256:57080dee41209e556a9a4ce60d229244f7a66ef52750f813bfbe18959770cfba"}, + {file = "cryptography-42.0.8-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:dea567d1b0e8bc5764b9443858b673b734100c2871dc93163f58c46a97a83d28"}, + {file = "cryptography-42.0.8-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4783183f7cb757b73b2ae9aed6599b96338eb957233c58ca8f49a49cc32fd5e"}, + {file = "cryptography-42.0.8-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0608251135d0e03111152e41f0cc2392d1e74e35703960d4190b2e0f4ca9c70"}, + {file = "cryptography-42.0.8-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:dc0fdf6787f37b1c6b08e6dfc892d9d068b5bdb671198c72072828b80bd5fe4c"}, + {file = "cryptography-42.0.8-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:9c0c1716c8447ee7dbf08d6db2e5c41c688544c61074b54fc4564196f55c25a7"}, + {file = "cryptography-42.0.8-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:fff12c88a672ab9c9c1cf7b0c80e3ad9e2ebd9d828d955c126be4fd3e5578c9e"}, + {file = "cryptography-42.0.8-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:cafb92b2bc622cd1aa6a1dce4b93307792633f4c5fe1f46c6b97cf67073ec961"}, + {file = "cryptography-42.0.8-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:31f721658a29331f895a5a54e7e82075554ccfb8b163a18719d342f5ffe5ecb1"}, + {file = "cryptography-42.0.8-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b297f90c5723d04bcc8265fc2a0f86d4ea2e0f7ab4b6994459548d3a6b992a14"}, + {file = "cryptography-42.0.8-cp39-abi3-win32.whl", hash = "sha256:2f88d197e66c65be5e42cd72e5c18afbfae3f741742070e3019ac8f4ac57262c"}, + {file = "cryptography-42.0.8-cp39-abi3-win_amd64.whl", hash = "sha256:fa76fbb7596cc5839320000cdd5d0955313696d9511debab7ee7278fc8b5c84a"}, + {file = "cryptography-42.0.8-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:ba4f0a211697362e89ad822e667d8d340b4d8d55fae72cdd619389fb5912eefe"}, + {file = "cryptography-42.0.8-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:81884c4d096c272f00aeb1f11cf62ccd39763581645b0812e99a91505fa48e0c"}, + {file = "cryptography-42.0.8-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c9bb2ae11bfbab395bdd072985abde58ea9860ed84e59dbc0463a5d0159f5b71"}, + {file = "cryptography-42.0.8-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:7016f837e15b0a1c119d27ecd89b3515f01f90a8615ed5e9427e30d9cdbfed3d"}, + {file = "cryptography-42.0.8-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5a94eccb2a81a309806027e1670a358b99b8fe8bfe9f8d329f27d72c094dde8c"}, + {file = "cryptography-42.0.8-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:dec9b018df185f08483f294cae6ccac29e7a6e0678996587363dc352dc65c842"}, + {file = "cryptography-42.0.8-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:343728aac38decfdeecf55ecab3264b015be68fc2816ca800db649607aeee648"}, + {file = "cryptography-42.0.8-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:013629ae70b40af70c9a7a5db40abe5d9054e6f4380e50ce769947b73bf3caad"}, + {file = "cryptography-42.0.8.tar.gz", hash = "sha256:8d09d05439ce7baa8e9e95b07ec5b6c886f548deb7e0f69ef25f64b3bce842f2"}, +] + +[package.dependencies] +cffi = {version = ">=1.12", markers = "platform_python_implementation != \"PyPy\""} + +[package.extras] +docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.1.1)"] +docstest = ["pyenchant (>=1.6.11)", "readme-renderer", "sphinxcontrib-spelling (>=4.0.1)"] +nox = ["nox"] +pep8test = ["check-sdist", "click", "mypy", "ruff"] +sdist = ["build"] +ssh = ["bcrypt (>=3.1.5)"] +test = ["certifi", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] +test-randomorder = ["pytest-randomly"] + +[[package]] +name = "dapr" +version = "1.16.0" +description = "The official release of Dapr Python SDK." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "dapr-1.16.0-py3-none-any.whl", hash = "sha256:076dd559a0b450eae24b1c2ae779c9299ed3e06a05c1f72719a6613af8d19ced"}, + {file = "dapr-1.16.0.tar.gz", hash = "sha256:c7e3d005552a598d07608d0d502b2bc432e86678f94b2beccc13a096b9198684"}, +] + +[package.dependencies] +aiohttp = ">=3.9.0b0" +grpcio = ">=1.37.0" +grpcio-status = ">=1.37.0" +protobuf = ">=4.22" +python-dateutil = ">=2.8.1" +typing-extensions = ">=4.4.0" + +[[package]] +name = "dnspython" +version = "2.8.0" +description = "DNS toolkit" +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "dnspython-2.8.0-py3-none-any.whl", hash = "sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af"}, + {file = "dnspython-2.8.0.tar.gz", hash = "sha256:181d3c6996452cb1189c4046c61599b84a5a86e099562ffde77d26984ff26d0f"}, +] + +[package.extras] +dev = ["black (>=25.1.0)", "coverage (>=7.0)", "flake8 (>=7)", "hypercorn (>=0.17.0)", "mypy (>=1.17)", "pylint (>=3)", "pytest (>=8.4)", "pytest-cov (>=6.2.0)", "quart-trio (>=0.12.0)", "sphinx (>=8.2.0)", "sphinx-rtd-theme (>=3.0.0)", "twine (>=6.1.0)", "wheel (>=0.45.0)"] +dnssec = ["cryptography (>=45)"] +doh = ["h2 (>=4.2.0)", "httpcore (>=1.0.0)", "httpx (>=0.28.0)"] +doq = ["aioquic (>=1.2.0)"] +idna = ["idna (>=3.10)"] +trio = ["trio (>=0.30)"] +wmi = ["wmi (>=1.5.1)"] + +[[package]] +name = "dpapick3" +version = "0.7.1" +description = "A native implementation of DPAPI" +optional = false +python-versions = ">=3.2" +groups = ["main"] +files = [ + {file = "dpapick3-0.7.1-py3-none-any.whl", hash = "sha256:61999f6d4d08231799d3d62e3a48502476fcc0c4d29f12bb8ed064e3352f5da9"}, + {file = "dpapick3-0.7.1.tar.gz", hash = "sha256:3449366800d5bb313dd6d8d9d259d1b94498881ac74ced163749617a431921cb"}, +] + +[package.dependencies] +pyasn1 = "*" +pycryptodome = "*" +python-registry = "*" + +[[package]] +name = "enum-compat" +version = "0.0.3" +description = "enum/enum34 compatibility package" +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "enum-compat-0.0.3.tar.gz", hash = "sha256:3677daabed56a6f724451d585662253d8fb4e5569845aafa8bb0da36b1a8751e"}, + {file = "enum_compat-0.0.3-py3-none-any.whl", hash = "sha256:88091b617c7fc3bbbceae50db5958023c48dc40b50520005aa3bf27f8f7ea157"}, +] + +[[package]] +name = "fastapi" +version = "0.115.14" +description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "fastapi-0.115.14-py3-none-any.whl", hash = "sha256:6c0c8bf9420bd58f565e585036d971872472b4f7d3f6c73b698e10cffdefb3ca"}, + {file = "fastapi-0.115.14.tar.gz", hash = "sha256:b1de15cdc1c499a4da47914db35d0e4ef8f1ce62b624e94e0e5824421df99739"}, +] + +[package.dependencies] +pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0" +starlette = ">=0.40.0,<0.47.0" +typing-extensions = ">=4.8.0" + +[package.extras] +all = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=3.1.5)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.18)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"] +standard = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "jinja2 (>=3.1.5)", "python-multipart (>=0.0.18)", "uvicorn[standard] (>=0.12.0)"] + +[[package]] +name = "file-linking" +version = "0.1.0" +description = "Modules Nemesis uses to handle file links and listings" +optional = false +python-versions = ">=3.12,<4.0" +groups = ["main"] +files = [] +develop = true + +[package.dependencies] +common = {path = "../common", develop = true} +dapr = "1.16.0" +psycopg = {version = ">=3.0.0,<4.0.0", extras = ["binary"]} +pyyaml = "^6.0.3" +structlog = ">=20.0.0,<30.0.0" + +[package.source] +type = "directory" +url = "../file_linking" + +[[package]] +name = "flask" +version = "3.1.2" +description = "A simple framework for building complex web applications." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "flask-3.1.2-py3-none-any.whl", hash = "sha256:ca1d8112ec8a6158cc29ea4858963350011b5c846a414cdb7a954aa9e967d03c"}, + {file = "flask-3.1.2.tar.gz", hash = "sha256:bf656c15c80190ed628ad08cdfd3aaa35beb087855e2f494910aa3774cc4fd87"}, +] + +[package.dependencies] +blinker = ">=1.9.0" +click = ">=8.1.3" +itsdangerous = ">=2.2.0" +jinja2 = ">=3.1.2" +markupsafe = ">=2.1.1" +werkzeug = ">=3.1.0" + +[package.extras] +async = ["asgiref (>=3.2)"] +dotenv = ["python-dotenv"] + +[[package]] +name = "frozenlist" +version = "1.8.0" +description = "A list-like structure which implements collections.abc.MutableSequence" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "frozenlist-1.8.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b37f6d31b3dcea7deb5e9696e529a6aa4a898adc33db82da12e4c60a7c4d2011"}, + {file = "frozenlist-1.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ef2b7b394f208233e471abc541cc6991f907ffd47dc72584acee3147899d6565"}, + {file = "frozenlist-1.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a88f062f072d1589b7b46e951698950e7da00442fc1cacbe17e19e025dc327ad"}, + {file = "frozenlist-1.8.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f57fb59d9f385710aa7060e89410aeb5058b99e62f4d16b08b91986b9a2140c2"}, + {file = "frozenlist-1.8.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:799345ab092bee59f01a915620b5d014698547afd011e691a208637312db9186"}, + {file = "frozenlist-1.8.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c23c3ff005322a6e16f71bf8692fcf4d5a304aaafe1e262c98c6d4adc7be863e"}, + {file = "frozenlist-1.8.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8a76ea0f0b9dfa06f254ee06053d93a600865b3274358ca48a352ce4f0798450"}, + {file = "frozenlist-1.8.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c7366fe1418a6133d5aa824ee53d406550110984de7637d65a178010f759c6ef"}, + {file = "frozenlist-1.8.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:13d23a45c4cebade99340c4165bd90eeb4a56c6d8a9d8aa49568cac19a6d0dc4"}, + {file = "frozenlist-1.8.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:e4a3408834f65da56c83528fb52ce7911484f0d1eaf7b761fc66001db1646eff"}, + {file = "frozenlist-1.8.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:42145cd2748ca39f32801dad54aeea10039da6f86e303659db90db1c4b614c8c"}, + {file = "frozenlist-1.8.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:e2de870d16a7a53901e41b64ffdf26f2fbb8917b3e6ebf398098d72c5b20bd7f"}, + {file = "frozenlist-1.8.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:20e63c9493d33ee48536600d1a5c95eefc870cd71e7ab037763d1fbb89cc51e7"}, + {file = "frozenlist-1.8.0-cp310-cp310-win32.whl", hash = "sha256:adbeebaebae3526afc3c96fad434367cafbfd1b25d72369a9e5858453b1bb71a"}, + {file = "frozenlist-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:667c3777ca571e5dbeb76f331562ff98b957431df140b54c85fd4d52eea8d8f6"}, + {file = "frozenlist-1.8.0-cp310-cp310-win_arm64.whl", hash = "sha256:80f85f0a7cc86e7a54c46d99c9e1318ff01f4687c172ede30fd52d19d1da1c8e"}, + {file = "frozenlist-1.8.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:09474e9831bc2b2199fad6da3c14c7b0fbdd377cce9d3d77131be28906cb7d84"}, + {file = "frozenlist-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:17c883ab0ab67200b5f964d2b9ed6b00971917d5d8a92df149dc2c9779208ee9"}, + {file = "frozenlist-1.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fa47e444b8ba08fffd1c18e8cdb9a75db1b6a27f17507522834ad13ed5922b93"}, + {file = "frozenlist-1.8.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2552f44204b744fba866e573be4c1f9048d6a324dfe14475103fd51613eb1d1f"}, + {file = "frozenlist-1.8.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:957e7c38f250991e48a9a73e6423db1bb9dd14e722a10f6b8bb8e16a0f55f695"}, + {file = "frozenlist-1.8.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8585e3bb2cdea02fc88ffa245069c36555557ad3609e83be0ec71f54fd4abb52"}, + {file = "frozenlist-1.8.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:edee74874ce20a373d62dc28b0b18b93f645633c2943fd90ee9d898550770581"}, + {file = "frozenlist-1.8.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c9a63152fe95756b85f31186bddf42e4c02c6321207fd6601a1c89ebac4fe567"}, + {file = "frozenlist-1.8.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b6db2185db9be0a04fecf2f241c70b63b1a242e2805be291855078f2b404dd6b"}, + {file = "frozenlist-1.8.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:f4be2e3d8bc8aabd566f8d5b8ba7ecc09249d74ba3c9ed52e54dc23a293f0b92"}, + {file = "frozenlist-1.8.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:c8d1634419f39ea6f5c427ea2f90ca85126b54b50837f31497f3bf38266e853d"}, + {file = "frozenlist-1.8.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:1a7fa382a4a223773ed64242dbe1c9c326ec09457e6b8428efb4118c685c3dfd"}, + {file = "frozenlist-1.8.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:11847b53d722050808926e785df837353bd4d75f1d494377e59b23594d834967"}, + {file = "frozenlist-1.8.0-cp311-cp311-win32.whl", hash = "sha256:27c6e8077956cf73eadd514be8fb04d77fc946a7fe9f7fe167648b0b9085cc25"}, + {file = "frozenlist-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:ac913f8403b36a2c8610bbfd25b8013488533e71e62b4b4adce9c86c8cea905b"}, + {file = "frozenlist-1.8.0-cp311-cp311-win_arm64.whl", hash = "sha256:d4d3214a0f8394edfa3e303136d0575eece0745ff2b47bd2cb2e66dd92d4351a"}, + {file = "frozenlist-1.8.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:78f7b9e5d6f2fdb88cdde9440dc147259b62b9d3b019924def9f6478be254ac1"}, + {file = "frozenlist-1.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:229bf37d2e4acdaf808fd3f06e854a4a7a3661e871b10dc1f8f1896a3b05f18b"}, + {file = "frozenlist-1.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f833670942247a14eafbb675458b4e61c82e002a148f49e68257b79296e865c4"}, + {file = "frozenlist-1.8.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:494a5952b1c597ba44e0e78113a7266e656b9794eec897b19ead706bd7074383"}, + {file = "frozenlist-1.8.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96f423a119f4777a4a056b66ce11527366a8bb92f54e541ade21f2374433f6d4"}, + {file = "frozenlist-1.8.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3462dd9475af2025c31cc61be6652dfa25cbfb56cbbf52f4ccfe029f38decaf8"}, + {file = "frozenlist-1.8.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4c800524c9cd9bac5166cd6f55285957fcfc907db323e193f2afcd4d9abd69b"}, + {file = "frozenlist-1.8.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d6a5df73acd3399d893dafc71663ad22534b5aa4f94e8a2fabfe856c3c1b6a52"}, + {file = "frozenlist-1.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:405e8fe955c2280ce66428b3ca55e12b3c4e9c336fb2103a4937e891c69a4a29"}, + {file = "frozenlist-1.8.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:908bd3f6439f2fef9e85031b59fd4f1297af54415fb60e4254a95f75b3cab3f3"}, + {file = "frozenlist-1.8.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:294e487f9ec720bd8ffcebc99d575f7eff3568a08a253d1ee1a0378754b74143"}, + {file = "frozenlist-1.8.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:74c51543498289c0c43656701be6b077f4b265868fa7f8a8859c197006efb608"}, + {file = "frozenlist-1.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:776f352e8329135506a1d6bf16ac3f87bc25b28e765949282dcc627af36123aa"}, + {file = "frozenlist-1.8.0-cp312-cp312-win32.whl", hash = "sha256:433403ae80709741ce34038da08511d4a77062aa924baf411ef73d1146e74faf"}, + {file = "frozenlist-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:34187385b08f866104f0c0617404c8eb08165ab1272e884abc89c112e9c00746"}, + {file = "frozenlist-1.8.0-cp312-cp312-win_arm64.whl", hash = "sha256:fe3c58d2f5db5fbd18c2987cba06d51b0529f52bc3a6cdc33d3f4eab725104bd"}, + {file = "frozenlist-1.8.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8d92f1a84bb12d9e56f818b3a746f3efba93c1b63c8387a73dde655e1e42282a"}, + {file = "frozenlist-1.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:96153e77a591c8adc2ee805756c61f59fef4cf4073a9275ee86fe8cba41241f7"}, + {file = "frozenlist-1.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f21f00a91358803399890ab167098c131ec2ddd5f8f5fd5fe9c9f2c6fcd91e40"}, + {file = "frozenlist-1.8.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fb30f9626572a76dfe4293c7194a09fb1fe93ba94c7d4f720dfae3b646b45027"}, + {file = "frozenlist-1.8.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eaa352d7047a31d87dafcacbabe89df0aa506abb5b1b85a2fb91bc3faa02d822"}, + {file = "frozenlist-1.8.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:03ae967b4e297f58f8c774c7eabcce57fe3c2434817d4385c50661845a058121"}, + {file = "frozenlist-1.8.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f6292f1de555ffcc675941d65fffffb0a5bcd992905015f85d0592201793e0e5"}, + {file = "frozenlist-1.8.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29548f9b5b5e3460ce7378144c3010363d8035cea44bc0bf02d57f5a685e084e"}, + {file = "frozenlist-1.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ec3cc8c5d4084591b4237c0a272cc4f50a5b03396a47d9caaf76f5d7b38a4f11"}, + {file = "frozenlist-1.8.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:517279f58009d0b1f2e7c1b130b377a349405da3f7621ed6bfae50b10adf20c1"}, + {file = "frozenlist-1.8.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:db1e72ede2d0d7ccb213f218df6a078a9c09a7de257c2fe8fcef16d5925230b1"}, + {file = "frozenlist-1.8.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b4dec9482a65c54a5044486847b8a66bf10c9cb4926d42927ec4e8fd5db7fed8"}, + {file = "frozenlist-1.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:21900c48ae04d13d416f0e1e0c4d81f7931f73a9dfa0b7a8746fb2fe7dd970ed"}, + {file = "frozenlist-1.8.0-cp313-cp313-win32.whl", hash = "sha256:8b7b94a067d1c504ee0b16def57ad5738701e4ba10cec90529f13fa03c833496"}, + {file = "frozenlist-1.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:878be833caa6a3821caf85eb39c5ba92d28e85df26d57afb06b35b2efd937231"}, + {file = "frozenlist-1.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:44389d135b3ff43ba8cc89ff7f51f5a0bb6b63d829c8300f79a2fe4fe61bcc62"}, + {file = "frozenlist-1.8.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:e25ac20a2ef37e91c1b39938b591457666a0fa835c7783c3a8f33ea42870db94"}, + {file = "frozenlist-1.8.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:07cdca25a91a4386d2e76ad992916a85038a9b97561bf7a3fd12d5d9ce31870c"}, + {file = "frozenlist-1.8.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4e0c11f2cc6717e0a741f84a527c52616140741cd812a50422f83dc31749fb52"}, + {file = "frozenlist-1.8.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b3210649ee28062ea6099cfda39e147fa1bc039583c8ee4481cb7811e2448c51"}, + {file = "frozenlist-1.8.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:581ef5194c48035a7de2aefc72ac6539823bb71508189e5de01d60c9dcd5fa65"}, + {file = "frozenlist-1.8.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3ef2d026f16a2b1866e1d86fc4e1291e1ed8a387b2c333809419a2f8b3a77b82"}, + {file = "frozenlist-1.8.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5500ef82073f599ac84d888e3a8c1f77ac831183244bfd7f11eaa0289fb30714"}, + {file = "frozenlist-1.8.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:50066c3997d0091c411a66e710f4e11752251e6d2d73d70d8d5d4c76442a199d"}, + {file = "frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:5c1c8e78426e59b3f8005e9b19f6ff46e5845895adbde20ece9218319eca6506"}, + {file = "frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:eefdba20de0d938cec6a89bd4d70f346a03108a19b9df4248d3cf0d88f1b0f51"}, + {file = "frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:cf253e0e1c3ceb4aaff6df637ce033ff6535fb8c70a764a8f46aafd3d6ab798e"}, + {file = "frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:032efa2674356903cd0261c4317a561a6850f3ac864a63fc1583147fb05a79b0"}, + {file = "frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6da155091429aeba16851ecb10a9104a108bcd32f6c1642867eadaee401c1c41"}, + {file = "frozenlist-1.8.0-cp313-cp313t-win32.whl", hash = "sha256:0f96534f8bfebc1a394209427d0f8a63d343c9779cda6fc25e8e121b5fd8555b"}, + {file = "frozenlist-1.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5d63a068f978fc69421fb0e6eb91a9603187527c86b7cd3f534a5b77a592b888"}, + {file = "frozenlist-1.8.0-cp313-cp313t-win_arm64.whl", hash = "sha256:bf0a7e10b077bf5fb9380ad3ae8ce20ef919a6ad93b4552896419ac7e1d8e042"}, + {file = "frozenlist-1.8.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cee686f1f4cadeb2136007ddedd0aaf928ab95216e7691c63e50a8ec066336d0"}, + {file = "frozenlist-1.8.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:119fb2a1bd47307e899c2fac7f28e85b9a543864df47aa7ec9d3c1b4545f096f"}, + {file = "frozenlist-1.8.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4970ece02dbc8c3a92fcc5228e36a3e933a01a999f7094ff7c23fbd2beeaa67c"}, + {file = "frozenlist-1.8.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:cba69cb73723c3f329622e34bdbf5ce1f80c21c290ff04256cff1cd3c2036ed2"}, + {file = "frozenlist-1.8.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:778a11b15673f6f1df23d9586f83c4846c471a8af693a22e066508b77d201ec8"}, + {file = "frozenlist-1.8.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0325024fe97f94c41c08872db482cf8ac4800d80e79222c6b0b7b162d5b13686"}, + {file = "frozenlist-1.8.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:97260ff46b207a82a7567b581ab4190bd4dfa09f4db8a8b49d1a958f6aa4940e"}, + {file = "frozenlist-1.8.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:54b2077180eb7f83dd52c40b2750d0a9f175e06a42e3213ce047219de902717a"}, + {file = "frozenlist-1.8.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2f05983daecab868a31e1da44462873306d3cbfd76d1f0b5b69c473d21dbb128"}, + {file = "frozenlist-1.8.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:33f48f51a446114bc5d251fb2954ab0164d5be02ad3382abcbfe07e2531d650f"}, + {file = "frozenlist-1.8.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:154e55ec0655291b5dd1b8731c637ecdb50975a2ae70c606d100750a540082f7"}, + {file = "frozenlist-1.8.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:4314debad13beb564b708b4a496020e5306c7333fa9a3ab90374169a20ffab30"}, + {file = "frozenlist-1.8.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:073f8bf8becba60aa931eb3bc420b217bb7d5b8f4750e6f8b3be7f3da85d38b7"}, + {file = "frozenlist-1.8.0-cp314-cp314-win32.whl", hash = "sha256:bac9c42ba2ac65ddc115d930c78d24ab8d4f465fd3fc473cdedfccadb9429806"}, + {file = "frozenlist-1.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:3e0761f4d1a44f1d1a47996511752cf3dcec5bbdd9cc2b4fe595caf97754b7a0"}, + {file = "frozenlist-1.8.0-cp314-cp314-win_arm64.whl", hash = "sha256:d1eaff1d00c7751b7c6662e9c5ba6eb2c17a2306ba5e2a37f24ddf3cc953402b"}, + {file = "frozenlist-1.8.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:d3bb933317c52d7ea5004a1c442eef86f426886fba134ef8cf4226ea6ee1821d"}, + {file = "frozenlist-1.8.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:8009897cdef112072f93a0efdce29cd819e717fd2f649ee3016efd3cd885a7ed"}, + {file = "frozenlist-1.8.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2c5dcbbc55383e5883246d11fd179782a9d07a986c40f49abe89ddf865913930"}, + {file = "frozenlist-1.8.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:39ecbc32f1390387d2aa4f5a995e465e9e2f79ba3adcac92d68e3e0afae6657c"}, + {file = "frozenlist-1.8.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92db2bf818d5cc8d9c1f1fc56b897662e24ea5adb36ad1f1d82875bd64e03c24"}, + {file = "frozenlist-1.8.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2dc43a022e555de94c3b68a4ef0b11c4f747d12c024a520c7101709a2144fb37"}, + {file = "frozenlist-1.8.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cb89a7f2de3602cfed448095bab3f178399646ab7c61454315089787df07733a"}, + {file = "frozenlist-1.8.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:33139dc858c580ea50e7e60a1b0ea003efa1fd42e6ec7fdbad78fff65fad2fd2"}, + {file = "frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:168c0969a329b416119507ba30b9ea13688fafffac1b7822802537569a1cb0ef"}, + {file = "frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:28bd570e8e189d7f7b001966435f9dac6718324b5be2990ac496cf1ea9ddb7fe"}, + {file = "frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b2a095d45c5d46e5e79ba1e5b9cb787f541a8dee0433836cea4b96a2c439dcd8"}, + {file = "frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:eab8145831a0d56ec9c4139b6c3e594c7a83c2c8be25d5bcf2d86136a532287a"}, + {file = "frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:974b28cf63cc99dfb2188d8d222bc6843656188164848c4f679e63dae4b0708e"}, + {file = "frozenlist-1.8.0-cp314-cp314t-win32.whl", hash = "sha256:342c97bf697ac5480c0a7ec73cd700ecfa5a8a40ac923bd035484616efecc2df"}, + {file = "frozenlist-1.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:06be8f67f39c8b1dc671f5d83aaefd3358ae5cdcf8314552c57e7ed3e6475bdd"}, + {file = "frozenlist-1.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:102e6314ca4da683dca92e3b1355490fed5f313b768500084fbe6371fddfdb79"}, + {file = "frozenlist-1.8.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:d8b7138e5cd0647e4523d6685b0eac5d4be9a184ae9634492f25c6eb38c12a47"}, + {file = "frozenlist-1.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a6483e309ca809f1efd154b4d37dc6d9f61037d6c6a81c2dc7a15cb22c8c5dca"}, + {file = "frozenlist-1.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1b9290cf81e95e93fdf90548ce9d3c1211cf574b8e3f4b3b7cb0537cf2227068"}, + {file = "frozenlist-1.8.0-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:59a6a5876ca59d1b63af8cd5e7ffffb024c3dc1e9cf9301b21a2e76286505c95"}, + {file = "frozenlist-1.8.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6dc4126390929823e2d2d9dc79ab4046ed74680360fc5f38b585c12c66cdf459"}, + {file = "frozenlist-1.8.0-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:332db6b2563333c5671fecacd085141b5800cb866be16d5e3eb15a2086476675"}, + {file = "frozenlist-1.8.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9ff15928d62a0b80bb875655c39bf517938c7d589554cbd2669be42d97c2cb61"}, + {file = "frozenlist-1.8.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7bf6cdf8e07c8151fba6fe85735441240ec7f619f935a5205953d58009aef8c6"}, + {file = "frozenlist-1.8.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:48e6d3f4ec5c7273dfe83ff27c91083c6c9065af655dc2684d2c200c94308bb5"}, + {file = "frozenlist-1.8.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:1a7607e17ad33361677adcd1443edf6f5da0ce5e5377b798fba20fae194825f3"}, + {file = "frozenlist-1.8.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:5a3a935c3a4e89c733303a2d5a7c257ea44af3a56c8202df486b7f5de40f37e1"}, + {file = "frozenlist-1.8.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:940d4a017dbfed9daf46a3b086e1d2167e7012ee297fef9e1c545c4d022f5178"}, + {file = "frozenlist-1.8.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:b9be22a69a014bc47e78072d0ecae716f5eb56c15238acca0f43d6eb8e4a5bda"}, + {file = "frozenlist-1.8.0-cp39-cp39-win32.whl", hash = "sha256:1aa77cb5697069af47472e39612976ed05343ff2e84a3dcf15437b232cbfd087"}, + {file = "frozenlist-1.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:7398c222d1d405e796970320036b1b563892b65809d9e5261487bb2c7f7b5c6a"}, + {file = "frozenlist-1.8.0-cp39-cp39-win_arm64.whl", hash = "sha256:b4f3b365f31c6cd4af24545ca0a244a53688cad8834e32f56831c4923b50a103"}, + {file = "frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d"}, + {file = "frozenlist-1.8.0.tar.gz", hash = "sha256:3ede829ed8d842f6cd48fc7081d7a41001a56f1f38603f9d49bf3020d59a31ad"}, +] + +[[package]] +name = "googleapis-common-protos" +version = "1.70.0" +description = "Common protobufs used in Google APIs" +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "googleapis_common_protos-1.70.0-py3-none-any.whl", hash = "sha256:b8bfcca8c25a2bb253e0e0b0adaf8c00773e5e6af6fd92397576680b807e0fd8"}, + {file = "googleapis_common_protos-1.70.0.tar.gz", hash = "sha256:0e1b44e0ea153e6594f9f394fef15193a68aaaea2d843f83e2742717ca753257"}, +] + +[package.dependencies] +protobuf = ">=3.20.2,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<7.0.0" + +[package.extras] +grpc = ["grpcio (>=1.44.0,<2.0.0)"] + +[[package]] +name = "grpcio" +version = "1.75.1" +description = "HTTP/2-based RPC framework" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "grpcio-1.75.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:1712b5890b22547dd29f3215c5788d8fc759ce6dd0b85a6ba6e2731f2d04c088"}, + {file = "grpcio-1.75.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:8d04e101bba4b55cea9954e4aa71c24153ba6182481b487ff376da28d4ba46cf"}, + {file = "grpcio-1.75.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:683cfc70be0c1383449097cba637317e4737a357cfc185d887fd984206380403"}, + {file = "grpcio-1.75.1-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:491444c081a54dcd5e6ada57314321ae526377f498d4aa09d975c3241c5b9e1c"}, + {file = "grpcio-1.75.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ce08d4e112d0d38487c2b631ec8723deac9bc404e9c7b1011426af50a79999e4"}, + {file = "grpcio-1.75.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5a2acda37fc926ccc4547977ac3e56b1df48fe200de968e8c8421f6e3093df6c"}, + {file = "grpcio-1.75.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:745c5fe6bf05df6a04bf2d11552c7d867a2690759e7ab6b05c318a772739bd75"}, + {file = "grpcio-1.75.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:259526a7159d39e2db40d566fe3e8f8e034d0fb2db5bf9c00e09aace655a4c2b"}, + {file = "grpcio-1.75.1-cp310-cp310-win32.whl", hash = "sha256:f4b29b9aabe33fed5df0a85e5f13b09ff25e2c05bd5946d25270a8bd5682dac9"}, + {file = "grpcio-1.75.1-cp310-cp310-win_amd64.whl", hash = "sha256:cf2e760978dcce7ff7d465cbc7e276c3157eedc4c27aa6de7b594c7a295d3d61"}, + {file = "grpcio-1.75.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:573855ca2e58e35032aff30bfbd1ee103fbcf4472e4b28d4010757700918e326"}, + {file = "grpcio-1.75.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:6a4996a2c8accc37976dc142d5991adf60733e223e5c9a2219e157dc6a8fd3a2"}, + {file = "grpcio-1.75.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b1ea1bbe77ecbc1be00af2769f4ae4a88ce93be57a4f3eebd91087898ed749f9"}, + {file = "grpcio-1.75.1-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:e5b425aee54cc5e3e3c58f00731e8a33f5567965d478d516d35ef99fd648ab68"}, + {file = "grpcio-1.75.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0049a7bf547dafaeeb1db17079ce79596c298bfe308fc084d023c8907a845b9a"}, + {file = "grpcio-1.75.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5b8ea230c7f77c0a1a3208a04a1eda164633fb0767b4cefd65a01079b65e5b1f"}, + {file = "grpcio-1.75.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:36990d629c3c9fb41e546414e5af52d0a7af37ce7113d9682c46d7e2919e4cca"}, + {file = "grpcio-1.75.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b10ad908118d38c2453ade7ff790e5bce36580c3742919007a2a78e3a1e521ca"}, + {file = "grpcio-1.75.1-cp311-cp311-win32.whl", hash = "sha256:d6be2b5ee7bea656c954dcf6aa8093c6f0e6a3ef9945c99d99fcbfc88c5c0bfe"}, + {file = "grpcio-1.75.1-cp311-cp311-win_amd64.whl", hash = "sha256:61c692fb05956b17dd6d1ab480f7f10ad0536dba3bc8fd4e3c7263dc244ed772"}, + {file = "grpcio-1.75.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:7b888b33cd14085d86176b1628ad2fcbff94cfbbe7809465097aa0132e58b018"}, + {file = "grpcio-1.75.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:8775036efe4ad2085975531d221535329f5dac99b6c2a854a995456098f99546"}, + {file = "grpcio-1.75.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:bb658f703468d7fbb5dcc4037c65391b7dc34f808ac46ed9136c24fc5eeb041d"}, + {file = "grpcio-1.75.1-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:4b7177a1cdb3c51b02b0c0a256b0a72fdab719600a693e0e9037949efffb200b"}, + {file = "grpcio-1.75.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7d4fa6ccc3ec2e68a04f7b883d354d7fea22a34c44ce535a2f0c0049cf626ddf"}, + {file = "grpcio-1.75.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3d86880ecaeb5b2f0a8afa63824de93adb8ebe4e49d0e51442532f4e08add7d6"}, + {file = "grpcio-1.75.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a8041d2f9e8a742aeae96f4b047ee44e73619f4f9d24565e84d5446c623673b6"}, + {file = "grpcio-1.75.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3652516048bf4c314ce12be37423c79829f46efffb390ad64149a10c6071e8de"}, + {file = "grpcio-1.75.1-cp312-cp312-win32.whl", hash = "sha256:44b62345d8403975513af88da2f3d5cc76f73ca538ba46596f92a127c2aea945"}, + {file = "grpcio-1.75.1-cp312-cp312-win_amd64.whl", hash = "sha256:b1e191c5c465fa777d4cafbaacf0c01e0d5278022082c0abbd2ee1d6454ed94d"}, + {file = "grpcio-1.75.1-cp313-cp313-linux_armv7l.whl", hash = "sha256:3bed22e750d91d53d9e31e0af35a7b0b51367e974e14a4ff229db5b207647884"}, + {file = "grpcio-1.75.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:5b8f381eadcd6ecaa143a21e9e80a26424c76a0a9b3d546febe6648f3a36a5ac"}, + {file = "grpcio-1.75.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5bf4001d3293e3414d0cf99ff9b1139106e57c3a66dfff0c5f60b2a6286ec133"}, + {file = "grpcio-1.75.1-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:9f82ff474103e26351dacfe8d50214e7c9322960d8d07ba7fa1d05ff981c8b2d"}, + {file = "grpcio-1.75.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0ee119f4f88d9f75414217823d21d75bfe0e6ed40135b0cbbfc6376bc9f7757d"}, + {file = "grpcio-1.75.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:664eecc3abe6d916fa6cf8dd6b778e62fb264a70f3430a3180995bf2da935446"}, + {file = "grpcio-1.75.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:c32193fa08b2fbebf08fe08e84f8a0aad32d87c3ad42999c65e9449871b1c66e"}, + {file = "grpcio-1.75.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5cebe13088b9254f6e615bcf1da9131d46cfa4e88039454aca9cb65f639bd3bc"}, + {file = "grpcio-1.75.1-cp313-cp313-win32.whl", hash = "sha256:4b4c678e7ed50f8ae8b8dbad15a865ee73ce12668b6aaf411bf3258b5bc3f970"}, + {file = "grpcio-1.75.1-cp313-cp313-win_amd64.whl", hash = "sha256:5573f51e3f296a1bcf71e7a690c092845fb223072120f4bdb7a5b48e111def66"}, + {file = "grpcio-1.75.1-cp314-cp314-linux_armv7l.whl", hash = "sha256:c05da79068dd96723793bffc8d0e64c45f316248417515f28d22204d9dae51c7"}, + {file = "grpcio-1.75.1-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:06373a94fd16ec287116a825161dca179a0402d0c60674ceeec8c9fba344fe66"}, + {file = "grpcio-1.75.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4484f4b7287bdaa7a5b3980f3c7224c3c622669405d20f69549f5fb956ad0421"}, + {file = "grpcio-1.75.1-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:2720c239c1180eee69f7883c1d4c83fc1a495a2535b5fa322887c70bf02b16e8"}, + {file = "grpcio-1.75.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:07a554fa31c668cf0e7a188678ceeca3cb8fead29bbe455352e712ec33ca701c"}, + {file = "grpcio-1.75.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:3e71a2105210366bfc398eef7f57a664df99194f3520edb88b9c3a7e46ee0d64"}, + {file = "grpcio-1.75.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:8679aa8a5b67976776d3c6b0521e99d1c34db8a312a12bcfd78a7085cb9b604e"}, + {file = "grpcio-1.75.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:aad1c774f4ebf0696a7f148a56d39a3432550612597331792528895258966dc0"}, + {file = "grpcio-1.75.1-cp314-cp314-win32.whl", hash = "sha256:62ce42d9994446b307649cb2a23335fa8e927f7ab2cbf5fcb844d6acb4d85f9c"}, + {file = "grpcio-1.75.1-cp314-cp314-win_amd64.whl", hash = "sha256:f86e92275710bea3000cb79feca1762dc0ad3b27830dd1a74e82ab321d4ee464"}, + {file = "grpcio-1.75.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:c09fba33327c3ac11b5c33dbdd8218eef8990d78f83b1656d628831812a8c0fb"}, + {file = "grpcio-1.75.1-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:7e21400b037be29545704889e72e586c238e346dcb2d08d8a7288d16c883a9ec"}, + {file = "grpcio-1.75.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c12121e509b9f8b0914d10054d24120237d19e870b1cd82acbb8a9b9ddd198a3"}, + {file = "grpcio-1.75.1-cp39-cp39-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:73577a93e692b3474b1bfe84285d098de36705dbd838bb4d6a056d326e4dc880"}, + {file = "grpcio-1.75.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e19e7dfa0d7ca7dea22be464339e18ac608fd75d88c56770c646cdabe54bc724"}, + {file = "grpcio-1.75.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:4e1c28f51c1cf67eccdfc1065e8e866c9ed622f09773ca60947089c117f848a1"}, + {file = "grpcio-1.75.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:030a6164bc2ca726052778c0cf8e3249617a34e368354f9e6107c27ad4af8c28"}, + {file = "grpcio-1.75.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:67697efef5a98d46d5db7b1720fa4043536f8b8e5072a5d61cfca762f287e939"}, + {file = "grpcio-1.75.1-cp39-cp39-win32.whl", hash = "sha256:52015cf73eb5d76f6404e0ce0505a69b51fd1f35810b3a01233b34b10baafb41"}, + {file = "grpcio-1.75.1-cp39-cp39-win_amd64.whl", hash = "sha256:9fe51e4a1f896ea84ac750900eae34d9e9b896b5b1e4a30b02dc31ad29f36383"}, + {file = "grpcio-1.75.1.tar.gz", hash = "sha256:3e81d89ece99b9ace23a6916880baca613c03a799925afb2857887efa8b1b3d2"}, +] + +[package.dependencies] +typing-extensions = ">=4.12,<5.0" + +[package.extras] +protobuf = ["grpcio-tools (>=1.75.1)"] + +[[package]] +name = "grpcio-status" +version = "1.75.1" +description = "Status proto mapping for gRPC" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "grpcio_status-1.75.1-py3-none-any.whl", hash = "sha256:f681b301be26dcf7abf5c765d4a22e4098765e1a65cbdfa3efca384edf8e4e3c"}, + {file = "grpcio_status-1.75.1.tar.gz", hash = "sha256:8162afa21833a2085c91089cc395ad880fac1378a1d60233d976649ed724cbf8"}, +] + +[package.dependencies] +googleapis-common-protos = ">=1.5.5" +grpcio = ">=1.75.1" +protobuf = ">=6.31.1,<7.0.0" + +[[package]] +name = "idna" +version = "3.11" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea"}, + {file = "idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902"}, +] + +[package.extras] +all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] + +[[package]] +name = "impacket" +version = "0.12.0" +description = "Network protocols Constructors and Dissectors" +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "impacket-0.12.0.tar.gz", hash = "sha256:89587d1b836a5220d74848c934757962b382886dca8b1b4a0c44d693f2600643"}, +] + +[package.dependencies] +charset_normalizer = "*" +flask = ">=1.0" +ldap3 = ">2.5.0,<2.5.2 || >2.5.2,<2.6 || >2.6" +ldapdomaindump = ">=0.9.0" +pyasn1 = ">=0.2.3" +pyasn1_modules = "*" +pycryptodomex = "*" +pyOpenSSL = "24.0.0" +pyreadline3 = {version = "*", markers = "sys_platform == \"win32\""} +setuptools = "*" +six = "*" + +[[package]] +name = "iniconfig" +version = "2.1.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760"}, + {file = "iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"}, +] + +[[package]] +name = "itsdangerous" +version = "2.2.0" +description = "Safely pass data to untrusted environments and back." +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef"}, + {file = "itsdangerous-2.2.0.tar.gz", hash = "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173"}, +] + +[[package]] +name = "jinja2" +version = "3.1.6" +description = "A very fast and expressive template engine." +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67"}, + {file = "jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d"}, +] + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "ldap3" +version = "2.9.1" +description = "A strictly RFC 4510 conforming LDAP V3 pure Python client library" +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "ldap3-2.9.1-py2.py3-none-any.whl", hash = "sha256:5869596fc4948797020d3f03b7939da938778a0f9e2009f7a072ccf92b8e8d70"}, + {file = "ldap3-2.9.1.tar.gz", hash = "sha256:f3e7fc4718e3f09dda568b57100095e0ce58633bcabbed8667ce3f8fbaa4229f"}, +] + +[package.dependencies] +pyasn1 = ">=0.4.6" + +[[package]] +name = "ldapdomaindump" +version = "0.10.0" +description = "Active Directory information dumper via LDAP" +optional = false +python-versions = ">=3.6" +groups = ["main"] +files = [ + {file = "ldapdomaindump-0.10.0-py3-none-any.whl", hash = "sha256:3797259596df7a5e1fda98388c96b1d94196f5da5551f1af1aaeedda0c9f5a11"}, + {file = "ldapdomaindump-0.10.0.tar.gz", hash = "sha256:cbc66b32a7787473ffd169c5319acde46c02fdc9d444556e6448e0def91d3299"}, +] + +[package.dependencies] +dnspython = "*" +ldap3 = ">2.5.0,<2.5.2 || >2.5.2,<2.6 || >2.6" + +[[package]] +name = "markupsafe" +version = "3.0.3" +description = "Safely add untrusted strings to HTML/XML markup." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "markupsafe-3.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559"}, + {file = "markupsafe-3.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419"}, + {file = "markupsafe-3.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695"}, + {file = "markupsafe-3.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591"}, + {file = "markupsafe-3.0.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c"}, + {file = "markupsafe-3.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f"}, + {file = "markupsafe-3.0.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6"}, + {file = "markupsafe-3.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1"}, + {file = "markupsafe-3.0.3-cp310-cp310-win32.whl", hash = "sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa"}, + {file = "markupsafe-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8"}, + {file = "markupsafe-3.0.3-cp310-cp310-win_arm64.whl", hash = "sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1"}, + {file = "markupsafe-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad"}, + {file = "markupsafe-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a"}, + {file = "markupsafe-3.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50"}, + {file = "markupsafe-3.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf"}, + {file = "markupsafe-3.0.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f"}, + {file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a"}, + {file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115"}, + {file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a"}, + {file = "markupsafe-3.0.3-cp311-cp311-win32.whl", hash = "sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19"}, + {file = "markupsafe-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01"}, + {file = "markupsafe-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c"}, + {file = "markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e"}, + {file = "markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce"}, + {file = "markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d"}, + {file = "markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d"}, + {file = "markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a"}, + {file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b"}, + {file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f"}, + {file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b"}, + {file = "markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d"}, + {file = "markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c"}, + {file = "markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f"}, + {file = "markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795"}, + {file = "markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219"}, + {file = "markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6"}, + {file = "markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676"}, + {file = "markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9"}, + {file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1"}, + {file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc"}, + {file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12"}, + {file = "markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed"}, + {file = "markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5"}, + {file = "markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485"}, + {file = "markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73"}, + {file = "markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37"}, + {file = "markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19"}, + {file = "markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025"}, + {file = "markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6"}, + {file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f"}, + {file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb"}, + {file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009"}, + {file = "markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354"}, + {file = "markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218"}, + {file = "markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287"}, + {file = "markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe"}, + {file = "markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026"}, + {file = "markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737"}, + {file = "markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97"}, + {file = "markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d"}, + {file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda"}, + {file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf"}, + {file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe"}, + {file = "markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9"}, + {file = "markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581"}, + {file = "markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4"}, + {file = "markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab"}, + {file = "markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175"}, + {file = "markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634"}, + {file = "markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50"}, + {file = "markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e"}, + {file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5"}, + {file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523"}, + {file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc"}, + {file = "markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d"}, + {file = "markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9"}, + {file = "markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa"}, + {file = "markupsafe-3.0.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:15d939a21d546304880945ca1ecb8a039db6b4dc49b2c5a400387cdae6a62e26"}, + {file = "markupsafe-3.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f71a396b3bf33ecaa1626c255855702aca4d3d9fea5e051b41ac59a9c1c41edc"}, + {file = "markupsafe-3.0.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f4b68347f8c5eab4a13419215bdfd7f8c9b19f2b25520968adfad23eb0ce60c"}, + {file = "markupsafe-3.0.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e8fc20152abba6b83724d7ff268c249fa196d8259ff481f3b1476383f8f24e42"}, + {file = "markupsafe-3.0.3-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:949b8d66bc381ee8b007cd945914c721d9aba8e27f71959d750a46f7c282b20b"}, + {file = "markupsafe-3.0.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:3537e01efc9d4dccdf77221fb1cb3b8e1a38d5428920e0657ce299b20324d758"}, + {file = "markupsafe-3.0.3-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:591ae9f2a647529ca990bc681daebdd52c8791ff06c2bfa05b65163e28102ef2"}, + {file = "markupsafe-3.0.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a320721ab5a1aba0a233739394eb907f8c8da5c98c9181d1161e77a0c8e36f2d"}, + {file = "markupsafe-3.0.3-cp39-cp39-win32.whl", hash = "sha256:df2449253ef108a379b8b5d6b43f4b1a8e81a061d6537becd5582fba5f9196d7"}, + {file = "markupsafe-3.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:7c3fb7d25180895632e5d3148dbdc29ea38ccb7fd210aa27acbd1201a1902c6e"}, + {file = "markupsafe-3.0.3-cp39-cp39-win_arm64.whl", hash = "sha256:38664109c14ffc9e7437e86b4dceb442b0096dfe3541d7864d9cbe1da4cf36c8"}, + {file = "markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698"}, +] + +[[package]] +name = "minio" +version = "7.2.18" +description = "MinIO Python SDK for Amazon S3 Compatible Cloud Storage" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "minio-7.2.18-py3-none-any.whl", hash = "sha256:f23a6edbff8d0bc4b5c1a61b2628a01c5a3342aefc613ff9c276012e6321108f"}, + {file = "minio-7.2.18.tar.gz", hash = "sha256:173402a5716099159c5659f9de75be204ebe248557b9f1cc9cf45aa70e9d3024"}, +] + +[package.dependencies] +argon2-cffi = "*" +certifi = "*" +pycryptodome = "*" +typing-extensions = "*" +urllib3 = "*" + +[[package]] +name = "multidict" +version = "6.7.0" +description = "multidict implementation" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "multidict-6.7.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:9f474ad5acda359c8758c8accc22032c6abe6dc87a8be2440d097785e27a9349"}, + {file = "multidict-6.7.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4b7a9db5a870f780220e931d0002bbfd88fb53aceb6293251e2c839415c1b20e"}, + {file = "multidict-6.7.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:03ca744319864e92721195fa28c7a3b2bc7b686246b35e4078c1e4d0eb5466d3"}, + {file = "multidict-6.7.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f0e77e3c0008bc9316e662624535b88d360c3a5d3f81e15cf12c139a75250046"}, + {file = "multidict-6.7.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:08325c9e5367aa379a3496aa9a022fe8837ff22e00b94db256d3a1378c76ab32"}, + {file = "multidict-6.7.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e2862408c99f84aa571ab462d25236ef9cb12a602ea959ba9c9009a54902fc73"}, + {file = "multidict-6.7.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4d72a9a2d885f5c208b0cb91ff2ed43636bb7e345ec839ff64708e04f69a13cc"}, + {file = "multidict-6.7.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:478cc36476687bac1514d651cbbaa94b86b0732fb6855c60c673794c7dd2da62"}, + {file = "multidict-6.7.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6843b28b0364dc605f21481c90fadb5f60d9123b442eb8a726bb74feef588a84"}, + {file = "multidict-6.7.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:23bfeee5316266e5ee2d625df2d2c602b829435fc3a235c2ba2131495706e4a0"}, + {file = "multidict-6.7.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:680878b9f3d45c31e1f730eef731f9b0bc1da456155688c6745ee84eb818e90e"}, + {file = "multidict-6.7.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:eb866162ef2f45063acc7a53a88ef6fe8bf121d45c30ea3c9cd87ce7e191a8d4"}, + {file = "multidict-6.7.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:df0e3bf7993bdbeca5ac25aa859cf40d39019e015c9c91809ba7093967f7a648"}, + {file = "multidict-6.7.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:661709cdcd919a2ece2234f9bae7174e5220c80b034585d7d8a755632d3e2111"}, + {file = "multidict-6.7.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:096f52730c3fb8ed419db2d44391932b63891b2c5ed14850a7e215c0ba9ade36"}, + {file = "multidict-6.7.0-cp310-cp310-win32.whl", hash = "sha256:afa8a2978ec65d2336305550535c9c4ff50ee527914328c8677b3973ade52b85"}, + {file = "multidict-6.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:b15b3afff74f707b9275d5ba6a91ae8f6429c3ffb29bbfd216b0b375a56f13d7"}, + {file = "multidict-6.7.0-cp310-cp310-win_arm64.whl", hash = "sha256:4b73189894398d59131a66ff157837b1fafea9974be486d036bb3d32331fdbf0"}, + {file = "multidict-6.7.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4d409aa42a94c0b3fa617708ef5276dfe81012ba6753a0370fcc9d0195d0a1fc"}, + {file = "multidict-6.7.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:14c9e076eede3b54c636f8ce1c9c252b5f057c62131211f0ceeec273810c9721"}, + {file = "multidict-6.7.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4c09703000a9d0fa3c3404b27041e574cc7f4df4c6563873246d0e11812a94b6"}, + {file = "multidict-6.7.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:a265acbb7bb33a3a2d626afbe756371dce0279e7b17f4f4eda406459c2b5ff1c"}, + {file = "multidict-6.7.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:51cb455de290ae462593e5b1cb1118c5c22ea7f0d3620d9940bf695cea5a4bd7"}, + {file = "multidict-6.7.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:db99677b4457c7a5c5a949353e125ba72d62b35f74e26da141530fbb012218a7"}, + {file = "multidict-6.7.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f470f68adc395e0183b92a2f4689264d1ea4b40504a24d9882c27375e6662bb9"}, + {file = "multidict-6.7.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0db4956f82723cc1c270de9c6e799b4c341d327762ec78ef82bb962f79cc07d8"}, + {file = "multidict-6.7.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3e56d780c238f9e1ae66a22d2adf8d16f485381878250db8d496623cd38b22bd"}, + {file = "multidict-6.7.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9d14baca2ee12c1a64740d4531356ba50b82543017f3ad6de0deb943c5979abb"}, + {file = "multidict-6.7.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:295a92a76188917c7f99cda95858c822f9e4aae5824246bba9b6b44004ddd0a6"}, + {file = "multidict-6.7.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:39f1719f57adbb767ef592a50ae5ebb794220d1188f9ca93de471336401c34d2"}, + {file = "multidict-6.7.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:0a13fb8e748dfc94749f622de065dd5c1def7e0d2216dba72b1d8069a389c6ff"}, + {file = "multidict-6.7.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e3aa16de190d29a0ea1b48253c57d99a68492c8dd8948638073ab9e74dc9410b"}, + {file = "multidict-6.7.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a048ce45dcdaaf1defb76b2e684f997fb5abf74437b6cb7b22ddad934a964e34"}, + {file = "multidict-6.7.0-cp311-cp311-win32.whl", hash = "sha256:a90af66facec4cebe4181b9e62a68be65e45ac9b52b67de9eec118701856e7ff"}, + {file = "multidict-6.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:95b5ffa4349df2887518bb839409bcf22caa72d82beec453216802f475b23c81"}, + {file = "multidict-6.7.0-cp311-cp311-win_arm64.whl", hash = "sha256:329aa225b085b6f004a4955271a7ba9f1087e39dcb7e65f6284a988264a63912"}, + {file = "multidict-6.7.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8a3862568a36d26e650a19bb5cbbba14b71789032aebc0423f8cc5f150730184"}, + {file = "multidict-6.7.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:960c60b5849b9b4f9dcc9bea6e3626143c252c74113df2c1540aebce70209b45"}, + {file = "multidict-6.7.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2049be98fb57a31b4ccf870bf377af2504d4ae35646a19037ec271e4c07998aa"}, + {file = "multidict-6.7.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0934f3843a1860dd465d38895c17fce1f1cb37295149ab05cd1b9a03afacb2a7"}, + {file = "multidict-6.7.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b3e34f3a1b8131ba06f1a73adab24f30934d148afcd5f5de9a73565a4404384e"}, + {file = "multidict-6.7.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:efbb54e98446892590dc2458c19c10344ee9a883a79b5cec4bc34d6656e8d546"}, + {file = "multidict-6.7.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a35c5fc61d4f51eb045061e7967cfe3123d622cd500e8868e7c0c592a09fedc4"}, + {file = "multidict-6.7.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29fe6740ebccba4175af1b9b87bf553e9c15cd5868ee967e010efcf94e4fd0f1"}, + {file = "multidict-6.7.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:123e2a72e20537add2f33a79e605f6191fba2afda4cbb876e35c1a7074298a7d"}, + {file = "multidict-6.7.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b284e319754366c1aee2267a2036248b24eeb17ecd5dc16022095e747f2f4304"}, + {file = "multidict-6.7.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:803d685de7be4303b5a657b76e2f6d1240e7e0a8aa2968ad5811fa2285553a12"}, + {file = "multidict-6.7.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c04a328260dfd5db8c39538f999f02779012268f54614902d0afc775d44e0a62"}, + {file = "multidict-6.7.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8a19cdb57cd3df4cd865849d93ee14920fb97224300c88501f16ecfa2604b4e0"}, + {file = "multidict-6.7.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9b2fd74c52accced7e75de26023b7dccee62511a600e62311b918ec5c168fc2a"}, + {file = "multidict-6.7.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3e8bfdd0e487acf992407a140d2589fe598238eaeffa3da8448d63a63cd363f8"}, + {file = "multidict-6.7.0-cp312-cp312-win32.whl", hash = "sha256:dd32a49400a2c3d52088e120ee00c1e3576cbff7e10b98467962c74fdb762ed4"}, + {file = "multidict-6.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:92abb658ef2d7ef22ac9f8bb88e8b6c3e571671534e029359b6d9e845923eb1b"}, + {file = "multidict-6.7.0-cp312-cp312-win_arm64.whl", hash = "sha256:490dab541a6a642ce1a9d61a4781656b346a55c13038f0b1244653828e3a83ec"}, + {file = "multidict-6.7.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:bee7c0588aa0076ce77c0ea5d19a68d76ad81fcd9fe8501003b9a24f9d4000f6"}, + {file = "multidict-6.7.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7ef6b61cad77091056ce0e7ce69814ef72afacb150b7ac6a3e9470def2198159"}, + {file = "multidict-6.7.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9c0359b1ec12b1d6849c59f9d319610b7f20ef990a6d454ab151aa0e3b9f78ca"}, + {file = "multidict-6.7.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cd240939f71c64bd658f186330603aac1a9a81bf6273f523fca63673cb7378a8"}, + {file = "multidict-6.7.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a60a4d75718a5efa473ebd5ab685786ba0c67b8381f781d1be14da49f1a2dc60"}, + {file = "multidict-6.7.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:53a42d364f323275126aff81fb67c5ca1b7a04fda0546245730a55c8c5f24bc4"}, + {file = "multidict-6.7.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3b29b980d0ddbecb736735ee5bef69bb2ddca56eff603c86f3f29a1128299b4f"}, + {file = "multidict-6.7.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f8a93b1c0ed2d04b97a5e9336fd2d33371b9a6e29ab7dd6503d63407c20ffbaf"}, + {file = "multidict-6.7.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9ff96e8815eecacc6645da76c413eb3b3d34cfca256c70b16b286a687d013c32"}, + {file = "multidict-6.7.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7516c579652f6a6be0e266aec0acd0db80829ca305c3d771ed898538804c2036"}, + {file = "multidict-6.7.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:040f393368e63fb0f3330e70c26bfd336656bed925e5cbe17c9da839a6ab13ec"}, + {file = "multidict-6.7.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b3bc26a951007b1057a1c543af845f1c7e3e71cc240ed1ace7bf4484aa99196e"}, + {file = "multidict-6.7.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:7b022717c748dd1992a83e219587aabe45980d88969f01b316e78683e6285f64"}, + {file = "multidict-6.7.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:9600082733859f00d79dee64effc7aef1beb26adb297416a4ad2116fd61374bd"}, + {file = "multidict-6.7.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:94218fcec4d72bc61df51c198d098ce2b378e0ccbac41ddbed5ef44092913288"}, + {file = "multidict-6.7.0-cp313-cp313-win32.whl", hash = "sha256:a37bd74c3fa9d00be2d7b8eca074dc56bd8077ddd2917a839bd989612671ed17"}, + {file = "multidict-6.7.0-cp313-cp313-win_amd64.whl", hash = "sha256:30d193c6cc6d559db42b6bcec8a5d395d34d60c9877a0b71ecd7c204fcf15390"}, + {file = "multidict-6.7.0-cp313-cp313-win_arm64.whl", hash = "sha256:ea3334cabe4d41b7ccd01e4d349828678794edbc2d3ae97fc162a3312095092e"}, + {file = "multidict-6.7.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:ad9ce259f50abd98a1ca0aa6e490b58c316a0fce0617f609723e40804add2c00"}, + {file = "multidict-6.7.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:07f5594ac6d084cbb5de2df218d78baf55ef150b91f0ff8a21cc7a2e3a5a58eb"}, + {file = "multidict-6.7.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:0591b48acf279821a579282444814a2d8d0af624ae0bc600aa4d1b920b6e924b"}, + {file = "multidict-6.7.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:749a72584761531d2b9467cfbdfd29487ee21124c304c4b6cb760d8777b27f9c"}, + {file = "multidict-6.7.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b4c3d199f953acd5b446bf7c0de1fe25d94e09e79086f8dc2f48a11a129cdf1"}, + {file = "multidict-6.7.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:9fb0211dfc3b51efea2f349ec92c114d7754dd62c01f81c3e32b765b70c45c9b"}, + {file = "multidict-6.7.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a027ec240fe73a8d6281872690b988eed307cd7d91b23998ff35ff577ca688b5"}, + {file = "multidict-6.7.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1d964afecdf3a8288789df2f5751dc0a8261138c3768d9af117ed384e538fad"}, + {file = "multidict-6.7.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:caf53b15b1b7df9fbd0709aa01409000a2b4dd03a5f6f5cc548183c7c8f8b63c"}, + {file = "multidict-6.7.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:654030da3197d927f05a536a66186070e98765aa5142794c9904555d3a9d8fb5"}, + {file = "multidict-6.7.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:2090d3718829d1e484706a2f525e50c892237b2bf9b17a79b059cb98cddc2f10"}, + {file = "multidict-6.7.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:2d2cfeec3f6f45651b3d408c4acec0ebf3daa9bc8a112a084206f5db5d05b754"}, + {file = "multidict-6.7.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:4ef089f985b8c194d341eb2c24ae6e7408c9a0e2e5658699c92f497437d88c3c"}, + {file = "multidict-6.7.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e93a0617cd16998784bf4414c7e40f17a35d2350e5c6f0bd900d3a8e02bd3762"}, + {file = "multidict-6.7.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f0feece2ef8ebc42ed9e2e8c78fc4aa3cf455733b507c09ef7406364c94376c6"}, + {file = "multidict-6.7.0-cp313-cp313t-win32.whl", hash = "sha256:19a1d55338ec1be74ef62440ca9e04a2f001a04d0cc49a4983dc320ff0f3212d"}, + {file = "multidict-6.7.0-cp313-cp313t-win_amd64.whl", hash = "sha256:3da4fb467498df97e986af166b12d01f05d2e04f978a9c1c680ea1988e0bc4b6"}, + {file = "multidict-6.7.0-cp313-cp313t-win_arm64.whl", hash = "sha256:b4121773c49a0776461f4a904cdf6264c88e42218aaa8407e803ca8025872792"}, + {file = "multidict-6.7.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3bab1e4aff7adaa34410f93b1f8e57c4b36b9af0426a76003f441ee1d3c7e842"}, + {file = "multidict-6.7.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:b8512bac933afc3e45fb2b18da8e59b78d4f408399a960339598374d4ae3b56b"}, + {file = "multidict-6.7.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:79dcf9e477bc65414ebfea98ffd013cb39552b5ecd62908752e0e413d6d06e38"}, + {file = "multidict-6.7.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:31bae522710064b5cbeddaf2e9f32b1abab70ac6ac91d42572502299e9953128"}, + {file = "multidict-6.7.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a0df7ff02397bb63e2fd22af2c87dfa39e8c7f12947bc524dbdc528282c7e34"}, + {file = "multidict-6.7.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7a0222514e8e4c514660e182d5156a415c13ef0aabbd71682fc714e327b95e99"}, + {file = "multidict-6.7.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2397ab4daaf2698eb51a76721e98db21ce4f52339e535725de03ea962b5a3202"}, + {file = "multidict-6.7.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8891681594162635948a636c9fe0ff21746aeb3dd5463f6e25d9bea3a8a39ca1"}, + {file = "multidict-6.7.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:18706cc31dbf402a7945916dd5cddf160251b6dab8a2c5f3d6d5a55949f676b3"}, + {file = "multidict-6.7.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f844a1bbf1d207dd311a56f383f7eda2d0e134921d45751842d8235e7778965d"}, + {file = "multidict-6.7.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:d4393e3581e84e5645506923816b9cc81f5609a778c7e7534054091acc64d1c6"}, + {file = "multidict-6.7.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:fbd18dc82d7bf274b37aa48d664534330af744e03bccf696d6f4c6042e7d19e7"}, + {file = "multidict-6.7.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:b6234e14f9314731ec45c42fc4554b88133ad53a09092cc48a88e771c125dadb"}, + {file = "multidict-6.7.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:08d4379f9744d8f78d98c8673c06e202ffa88296f009c71bbafe8a6bf847d01f"}, + {file = "multidict-6.7.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:9fe04da3f79387f450fd0061d4dd2e45a72749d31bf634aecc9e27f24fdc4b3f"}, + {file = "multidict-6.7.0-cp314-cp314-win32.whl", hash = "sha256:fbafe31d191dfa7c4c51f7a6149c9fb7e914dcf9ffead27dcfd9f1ae382b3885"}, + {file = "multidict-6.7.0-cp314-cp314-win_amd64.whl", hash = "sha256:2f67396ec0310764b9222a1728ced1ab638f61aadc6226f17a71dd9324f9a99c"}, + {file = "multidict-6.7.0-cp314-cp314-win_arm64.whl", hash = "sha256:ba672b26069957ee369cfa7fc180dde1fc6f176eaf1e6beaf61fbebbd3d9c000"}, + {file = "multidict-6.7.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:c1dcc7524066fa918c6a27d61444d4ee7900ec635779058571f70d042d86ed63"}, + {file = "multidict-6.7.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:27e0b36c2d388dc7b6ced3406671b401e84ad7eb0656b8f3a2f46ed0ce483718"}, + {file = "multidict-6.7.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2a7baa46a22e77f0988e3b23d4ede5513ebec1929e34ee9495be535662c0dfe2"}, + {file = "multidict-6.7.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7bf77f54997a9166a2f5675d1201520586439424c2511723a7312bdb4bcc034e"}, + {file = "multidict-6.7.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e011555abada53f1578d63389610ac8a5400fc70ce71156b0aa30d326f1a5064"}, + {file = "multidict-6.7.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:28b37063541b897fd6a318007373930a75ca6d6ac7c940dbe14731ffdd8d498e"}, + {file = "multidict-6.7.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:05047ada7a2fde2631a0ed706f1fd68b169a681dfe5e4cf0f8e4cb6618bbc2cd"}, + {file = "multidict-6.7.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:716133f7d1d946a4e1b91b1756b23c088881e70ff180c24e864c26192ad7534a"}, + {file = "multidict-6.7.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d1bed1b467ef657f2a0ae62844a607909ef1c6889562de5e1d505f74457d0b96"}, + {file = "multidict-6.7.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:ca43bdfa5d37bd6aee89d85e1d0831fb86e25541be7e9d376ead1b28974f8e5e"}, + {file = "multidict-6.7.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:44b546bd3eb645fd26fb949e43c02a25a2e632e2ca21a35e2e132c8105dc8599"}, + {file = "multidict-6.7.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:a6ef16328011d3f468e7ebc326f24c1445f001ca1dec335b2f8e66bed3006394"}, + {file = "multidict-6.7.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:5aa873cbc8e593d361ae65c68f85faadd755c3295ea2c12040ee146802f23b38"}, + {file = "multidict-6.7.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:3d7b6ccce016e29df4b7ca819659f516f0bc7a4b3efa3bb2012ba06431b044f9"}, + {file = "multidict-6.7.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:171b73bd4ee683d307599b66793ac80981b06f069b62eea1c9e29c9241aa66b0"}, + {file = "multidict-6.7.0-cp314-cp314t-win32.whl", hash = "sha256:b2d7f80c4e1fd010b07cb26820aae86b7e73b681ee4889684fb8d2d4537aab13"}, + {file = "multidict-6.7.0-cp314-cp314t-win_amd64.whl", hash = "sha256:09929cab6fcb68122776d575e03c6cc64ee0b8fca48d17e135474b042ce515cd"}, + {file = "multidict-6.7.0-cp314-cp314t-win_arm64.whl", hash = "sha256:cc41db090ed742f32bd2d2c721861725e6109681eddf835d0a82bd3a5c382827"}, + {file = "multidict-6.7.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:363eb68a0a59bd2303216d2346e6c441ba10d36d1f9969fcb6f1ba700de7bb5c"}, + {file = "multidict-6.7.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d874eb056410ca05fed180b6642e680373688efafc7f077b2a2f61811e873a40"}, + {file = "multidict-6.7.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8b55d5497b51afdfde55925e04a022f1de14d4f4f25cdfd4f5d9b0aa96166851"}, + {file = "multidict-6.7.0-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f8e5c0031b90ca9ce555e2e8fd5c3b02a25f14989cbc310701823832c99eb687"}, + {file = "multidict-6.7.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9cf41880c991716f3c7cec48e2f19ae4045fc9db5fc9cff27347ada24d710bb5"}, + {file = "multidict-6.7.0-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8cfc12a8630a29d601f48d47787bd7eb730e475e83edb5d6c5084317463373eb"}, + {file = "multidict-6.7.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3996b50c3237c4aec17459217c1e7bbdead9a22a0fcd3c365564fbd16439dde6"}, + {file = "multidict-6.7.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7f5170993a0dd3ab871c74f45c0a21a4e2c37a2f2b01b5f722a2ad9c6650469e"}, + {file = "multidict-6.7.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ec81878ddf0e98817def1e77d4f50dae5ef5b0e4fe796fae3bd674304172416e"}, + {file = "multidict-6.7.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:9281bf5b34f59afbc6b1e477a372e9526b66ca446f4bf62592839c195a718b32"}, + {file = "multidict-6.7.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:68af405971779d8b37198726f2b6fe3955db846fee42db7a4286fc542203934c"}, + {file = "multidict-6.7.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3ba3ef510467abb0667421a286dc906e30eb08569365f5cdb131d7aff7c2dd84"}, + {file = "multidict-6.7.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:b61189b29081a20c7e4e0b49b44d5d44bb0dc92be3c6d06a11cc043f81bf9329"}, + {file = "multidict-6.7.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:fb287618b9c7aa3bf8d825f02d9201b2f13078a5ed3b293c8f4d953917d84d5e"}, + {file = "multidict-6.7.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:521f33e377ff64b96c4c556b81c55d0cfffb96a11c194fd0c3f1e56f3d8dd5a4"}, + {file = "multidict-6.7.0-cp39-cp39-win32.whl", hash = "sha256:ce8fdc2dca699f8dbf055a61d73eaa10482569ad20ee3c36ef9641f69afa8c91"}, + {file = "multidict-6.7.0-cp39-cp39-win_amd64.whl", hash = "sha256:7e73299c99939f089dd9b2120a04a516b95cdf8c1cd2b18c53ebf0de80b1f18f"}, + {file = "multidict-6.7.0-cp39-cp39-win_arm64.whl", hash = "sha256:6bdce131e14b04fd34a809b6380dbfd826065c3e2fe8a50dbae659fa0c390546"}, + {file = "multidict-6.7.0-py3-none-any.whl", hash = "sha256:394fc5c42a333c9ffc3e421a4c85e08580d990e08b99f6bf35b4132114c5dcb3"}, + {file = "multidict-6.7.0.tar.gz", hash = "sha256:c6e99d9a65ca282e578dfea819cfa9c0a62b2499d8677392e09feaf305e9e6f5"}, +] + +[[package]] +name = "nemesis-dpapi" +version = "0.1.0" +description = "" +optional = false +python-versions = ">=3.12" +groups = ["main"] +files = [] +develop = true + +[package.dependencies] +asyncpg = ">=0.29.0,<=0.30.0" +cryptography = ">=42.0.0,<43.0.0" +dapr = "1.16.0" +dpapick3 = ">=0.7.1,<0.8.0" +impacket = ">=0.12.0,<0.13.0" +pycryptodome = ">=3.23.0,<4.0.0" +pydantic = ">=2.0.0,<3.0.0" + +[package.source] +type = "directory" +url = "../nemesis_dpapi" + +[[package]] +name = "packaging" +version = "25.0" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"}, + {file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"}, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746"}, + {file = "pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["coverage", "pytest", "pytest-benchmark"] + +[[package]] +name = "propcache" +version = "0.4.1" +description = "Accelerated property cache" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "propcache-0.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c2d1fa3201efaf55d730400d945b5b3ab6e672e100ba0f9a409d950ab25d7db"}, + {file = "propcache-0.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1eb2994229cc8ce7fe9b3db88f5465f5fd8651672840b2e426b88cdb1a30aac8"}, + {file = "propcache-0.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:66c1f011f45a3b33d7bcb22daed4b29c0c9e2224758b6be00686731e1b46f925"}, + {file = "propcache-0.4.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9a52009f2adffe195d0b605c25ec929d26b36ef986ba85244891dee3b294df21"}, + {file = "propcache-0.4.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5d4e2366a9c7b837555cf02fb9be2e3167d333aff716332ef1b7c3a142ec40c5"}, + {file = "propcache-0.4.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:9d2b6caef873b4f09e26ea7e33d65f42b944837563a47a94719cc3544319a0db"}, + {file = "propcache-0.4.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2b16ec437a8c8a965ecf95739448dd938b5c7f56e67ea009f4300d8df05f32b7"}, + {file = "propcache-0.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:296f4c8ed03ca7476813fe666c9ea97869a8d7aec972618671b33a38a5182ef4"}, + {file = "propcache-0.4.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:1f0978529a418ebd1f49dad413a2b68af33f85d5c5ca5c6ca2a3bed375a7ac60"}, + {file = "propcache-0.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fd138803047fb4c062b1c1dd95462f5209456bfab55c734458f15d11da288f8f"}, + {file = "propcache-0.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8c9b3cbe4584636d72ff556d9036e0c9317fa27b3ac1f0f558e7e84d1c9c5900"}, + {file = "propcache-0.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f93243fdc5657247533273ac4f86ae106cc6445a0efacb9a1bfe982fcfefd90c"}, + {file = "propcache-0.4.1-cp310-cp310-win32.whl", hash = "sha256:a0ee98db9c5f80785b266eb805016e36058ac72c51a064040f2bc43b61101cdb"}, + {file = "propcache-0.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:1cdb7988c4e5ac7f6d175a28a9aa0c94cb6f2ebe52756a3c0cda98d2809a9e37"}, + {file = "propcache-0.4.1-cp310-cp310-win_arm64.whl", hash = "sha256:d82ad62b19645419fe79dd63b3f9253e15b30e955c0170e5cebc350c1844e581"}, + {file = "propcache-0.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:60a8fda9644b7dfd5dece8c61d8a85e271cb958075bfc4e01083c148b61a7caf"}, + {file = "propcache-0.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c30b53e7e6bda1d547cabb47c825f3843a0a1a42b0496087bb58d8fedf9f41b5"}, + {file = "propcache-0.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6918ecbd897443087a3b7cd978d56546a812517dcaaca51b49526720571fa93e"}, + {file = "propcache-0.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3d902a36df4e5989763425a8ab9e98cd8ad5c52c823b34ee7ef307fd50582566"}, + {file = "propcache-0.4.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a9695397f85973bb40427dedddf70d8dc4a44b22f1650dd4af9eedf443d45165"}, + {file = "propcache-0.4.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2bb07ffd7eaad486576430c89f9b215f9e4be68c4866a96e97db9e97fead85dc"}, + {file = "propcache-0.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fd6f30fdcf9ae2a70abd34da54f18da086160e4d7d9251f81f3da0ff84fc5a48"}, + {file = "propcache-0.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fc38cba02d1acba4e2869eef1a57a43dfbd3d49a59bf90dda7444ec2be6a5570"}, + {file = "propcache-0.4.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:67fad6162281e80e882fb3ec355398cf72864a54069d060321f6cd0ade95fe85"}, + {file = "propcache-0.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f10207adf04d08bec185bae14d9606a1444715bc99180f9331c9c02093e1959e"}, + {file = "propcache-0.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e9b0d8d0845bbc4cfcdcbcdbf5086886bc8157aa963c31c777ceff7846c77757"}, + {file = "propcache-0.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:981333cb2f4c1896a12f4ab92a9cc8f09ea664e9b7dbdc4eff74627af3a11c0f"}, + {file = "propcache-0.4.1-cp311-cp311-win32.whl", hash = "sha256:f1d2f90aeec838a52f1c1a32fe9a619fefd5e411721a9117fbf82aea638fe8a1"}, + {file = "propcache-0.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:364426a62660f3f699949ac8c621aad6977be7126c5807ce48c0aeb8e7333ea6"}, + {file = "propcache-0.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:e53f3a38d3510c11953f3e6a33f205c6d1b001129f972805ca9b42fc308bc239"}, + {file = "propcache-0.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e153e9cd40cc8945138822807139367f256f89c6810c2634a4f6902b52d3b4e2"}, + {file = "propcache-0.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cd547953428f7abb73c5ad82cbb32109566204260d98e41e5dfdc682eb7f8403"}, + {file = "propcache-0.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f048da1b4f243fc44f205dfd320933a951b8d89e0afd4c7cacc762a8b9165207"}, + {file = "propcache-0.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ec17c65562a827bba85e3872ead335f95405ea1674860d96483a02f5c698fa72"}, + {file = "propcache-0.4.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:405aac25c6394ef275dee4c709be43745d36674b223ba4eb7144bf4d691b7367"}, + {file = "propcache-0.4.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0013cb6f8dde4b2a2f66903b8ba740bdfe378c943c4377a200551ceb27f379e4"}, + {file = "propcache-0.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:15932ab57837c3368b024473a525e25d316d8353016e7cc0e5ba9eb343fbb1cf"}, + {file = "propcache-0.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:031dce78b9dc099f4c29785d9cf5577a3faf9ebf74ecbd3c856a7b92768c3df3"}, + {file = "propcache-0.4.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ab08df6c9a035bee56e31af99be621526bd237bea9f32def431c656b29e41778"}, + {file = "propcache-0.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4d7af63f9f93fe593afbf104c21b3b15868efb2c21d07d8732c0c4287e66b6a6"}, + {file = "propcache-0.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:cfc27c945f422e8b5071b6e93169679e4eb5bf73bbcbf1ba3ae3a83d2f78ebd9"}, + {file = "propcache-0.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:35c3277624a080cc6ec6f847cbbbb5b49affa3598c4535a0a4682a697aaa5c75"}, + {file = "propcache-0.4.1-cp312-cp312-win32.whl", hash = "sha256:671538c2262dadb5ba6395e26c1731e1d52534bfe9ae56d0b5573ce539266aa8"}, + {file = "propcache-0.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:cb2d222e72399fcf5890d1d5cc1060857b9b236adff2792ff48ca2dfd46c81db"}, + {file = "propcache-0.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:204483131fb222bdaaeeea9f9e6c6ed0cac32731f75dfc1d4a567fc1926477c1"}, + {file = "propcache-0.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:43eedf29202c08550aac1d14e0ee619b0430aaef78f85864c1a892294fbc28cf"}, + {file = "propcache-0.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d62cdfcfd89ccb8de04e0eda998535c406bf5e060ffd56be6c586cbcc05b3311"}, + {file = "propcache-0.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cae65ad55793da34db5f54e4029b89d3b9b9490d8abe1b4c7ab5d4b8ec7ebf74"}, + {file = "propcache-0.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:333ddb9031d2704a301ee3e506dc46b1fe5f294ec198ed6435ad5b6a085facfe"}, + {file = "propcache-0.4.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:fd0858c20f078a32cf55f7e81473d96dcf3b93fd2ccdb3d40fdf54b8573df3af"}, + {file = "propcache-0.4.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:678ae89ebc632c5c204c794f8dab2837c5f159aeb59e6ed0539500400577298c"}, + {file = "propcache-0.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d472aeb4fbf9865e0c6d622d7f4d54a4e101a89715d8904282bb5f9a2f476c3f"}, + {file = "propcache-0.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4d3df5fa7e36b3225954fba85589da77a0fe6a53e3976de39caf04a0db4c36f1"}, + {file = "propcache-0.4.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:ee17f18d2498f2673e432faaa71698032b0127ebf23ae5974eeaf806c279df24"}, + {file = "propcache-0.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:580e97762b950f993ae618e167e7be9256b8353c2dcd8b99ec100eb50f5286aa"}, + {file = "propcache-0.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:501d20b891688eb8e7aa903021f0b72d5a55db40ffaab27edefd1027caaafa61"}, + {file = "propcache-0.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a0bd56e5b100aef69bd8562b74b46254e7c8812918d3baa700c8a8009b0af66"}, + {file = "propcache-0.4.1-cp313-cp313-win32.whl", hash = "sha256:bcc9aaa5d80322bc2fb24bb7accb4a30f81e90ab8d6ba187aec0744bc302ad81"}, + {file = "propcache-0.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:381914df18634f5494334d201e98245c0596067504b9372d8cf93f4bb23e025e"}, + {file = "propcache-0.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:8873eb4460fd55333ea49b7d189749ecf6e55bf85080f11b1c4530ed3034cba1"}, + {file = "propcache-0.4.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:92d1935ee1f8d7442da9c0c4fa7ac20d07e94064184811b685f5c4fada64553b"}, + {file = "propcache-0.4.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:473c61b39e1460d386479b9b2f337da492042447c9b685f28be4f74d3529e566"}, + {file = "propcache-0.4.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:c0ef0aaafc66fbd87842a3fe3902fd889825646bc21149eafe47be6072725835"}, + {file = "propcache-0.4.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f95393b4d66bfae908c3ca8d169d5f79cd65636ae15b5e7a4f6e67af675adb0e"}, + {file = "propcache-0.4.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c07fda85708bc48578467e85099645167a955ba093be0a2dcba962195676e859"}, + {file = "propcache-0.4.1-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:af223b406d6d000830c6f65f1e6431783fc3f713ba3e6cc8c024d5ee96170a4b"}, + {file = "propcache-0.4.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a78372c932c90ee474559c5ddfffd718238e8673c340dc21fe45c5b8b54559a0"}, + {file = "propcache-0.4.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:564d9f0d4d9509e1a870c920a89b2fec951b44bf5ba7d537a9e7c1ccec2c18af"}, + {file = "propcache-0.4.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:17612831fda0138059cc5546f4d12a2aacfb9e47068c06af35c400ba58ba7393"}, + {file = "propcache-0.4.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:41a89040cb10bd345b3c1a873b2bf36413d48da1def52f268a055f7398514874"}, + {file = "propcache-0.4.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e35b88984e7fa64aacecea39236cee32dd9bd8c55f57ba8a75cf2399553f9bd7"}, + {file = "propcache-0.4.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6f8b465489f927b0df505cbe26ffbeed4d6d8a2bbc61ce90eb074ff129ef0ab1"}, + {file = "propcache-0.4.1-cp313-cp313t-win32.whl", hash = "sha256:2ad890caa1d928c7c2965b48f3a3815c853180831d0e5503d35cf00c472f4717"}, + {file = "propcache-0.4.1-cp313-cp313t-win_amd64.whl", hash = "sha256:f7ee0e597f495cf415bcbd3da3caa3bd7e816b74d0d52b8145954c5e6fd3ff37"}, + {file = "propcache-0.4.1-cp313-cp313t-win_arm64.whl", hash = "sha256:929d7cbe1f01bb7baffb33dc14eb5691c95831450a26354cd210a8155170c93a"}, + {file = "propcache-0.4.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3f7124c9d820ba5548d431afb4632301acf965db49e666aa21c305cbe8c6de12"}, + {file = "propcache-0.4.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:c0d4b719b7da33599dfe3b22d3db1ef789210a0597bc650b7cee9c77c2be8c5c"}, + {file = "propcache-0.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9f302f4783709a78240ebc311b793f123328716a60911d667e0c036bc5dcbded"}, + {file = "propcache-0.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c80ee5802e3fb9ea37938e7eecc307fb984837091d5fd262bb37238b1ae97641"}, + {file = "propcache-0.4.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ed5a841e8bb29a55fb8159ed526b26adc5bdd7e8bd7bf793ce647cb08656cdf4"}, + {file = "propcache-0.4.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:55c72fd6ea2da4c318e74ffdf93c4fe4e926051133657459131a95c846d16d44"}, + {file = "propcache-0.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8326e144341460402713f91df60ade3c999d601e7eb5ff8f6f7862d54de0610d"}, + {file = "propcache-0.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:060b16ae65bc098da7f6d25bf359f1f31f688384858204fe5d652979e0015e5b"}, + {file = "propcache-0.4.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:89eb3fa9524f7bec9de6e83cf3faed9d79bffa560672c118a96a171a6f55831e"}, + {file = "propcache-0.4.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:dee69d7015dc235f526fe80a9c90d65eb0039103fe565776250881731f06349f"}, + {file = "propcache-0.4.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:5558992a00dfd54ccbc64a32726a3357ec93825a418a401f5cc67df0ac5d9e49"}, + {file = "propcache-0.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c9b822a577f560fbd9554812526831712c1436d2c046cedee4c3796d3543b144"}, + {file = "propcache-0.4.1-cp314-cp314-win32.whl", hash = "sha256:ab4c29b49d560fe48b696cdcb127dd36e0bc2472548f3bf56cc5cb3da2b2984f"}, + {file = "propcache-0.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:5a103c3eb905fcea0ab98be99c3a9a5ab2de60228aa5aceedc614c0281cf6153"}, + {file = "propcache-0.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:74c1fb26515153e482e00177a1ad654721bf9207da8a494a0c05e797ad27b992"}, + {file = "propcache-0.4.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:824e908bce90fb2743bd6b59db36eb4f45cd350a39637c9f73b1c1ea66f5b75f"}, + {file = "propcache-0.4.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:c2b5e7db5328427c57c8e8831abda175421b709672f6cfc3d630c3b7e2146393"}, + {file = "propcache-0.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6f6ff873ed40292cd4969ef5310179afd5db59fdf055897e282485043fc80ad0"}, + {file = "propcache-0.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:49a2dc67c154db2c1463013594c458881a069fcf98940e61a0569016a583020a"}, + {file = "propcache-0.4.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:005f08e6a0529984491e37d8dbc3dd86f84bd78a8ceb5fa9a021f4c48d4984be"}, + {file = "propcache-0.4.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5c3310452e0d31390da9035c348633b43d7e7feb2e37be252be6da45abd1abcc"}, + {file = "propcache-0.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c3c70630930447f9ef1caac7728c8ad1c56bc5015338b20fed0d08ea2480b3a"}, + {file = "propcache-0.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8e57061305815dfc910a3634dcf584f08168a8836e6999983569f51a8544cd89"}, + {file = "propcache-0.4.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:521a463429ef54143092c11a77e04056dd00636f72e8c45b70aaa3140d639726"}, + {file = "propcache-0.4.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:120c964da3fdc75e3731aa392527136d4ad35868cc556fd09bb6d09172d9a367"}, + {file = "propcache-0.4.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:d8f353eb14ee3441ee844ade4277d560cdd68288838673273b978e3d6d2c8f36"}, + {file = "propcache-0.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ab2943be7c652f09638800905ee1bab2c544e537edb57d527997a24c13dc1455"}, + {file = "propcache-0.4.1-cp314-cp314t-win32.whl", hash = "sha256:05674a162469f31358c30bcaa8883cb7829fa3110bf9c0991fe27d7896c42d85"}, + {file = "propcache-0.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:990f6b3e2a27d683cb7602ed6c86f15ee6b43b1194736f9baaeb93d0016633b1"}, + {file = "propcache-0.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:ecef2343af4cc68e05131e45024ba34f6095821988a9d0a02aa7c73fcc448aa9"}, + {file = "propcache-0.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3d233076ccf9e450c8b3bc6720af226b898ef5d051a2d145f7d765e6e9f9bcff"}, + {file = "propcache-0.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:357f5bb5c377a82e105e44bd3d52ba22b616f7b9773714bff93573988ef0a5fb"}, + {file = "propcache-0.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cbc3b6dfc728105b2a57c06791eb07a94229202ea75c59db644d7d496b698cac"}, + {file = "propcache-0.4.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:182b51b421f0501952d938dc0b0eb45246a5b5153c50d42b495ad5fb7517c888"}, + {file = "propcache-0.4.1-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4b536b39c5199b96fc6245eb5fb796c497381d3942f169e44e8e392b29c9ebcc"}, + {file = "propcache-0.4.1-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:db65d2af507bbfbdcedb254a11149f894169d90488dd3e7190f7cdcb2d6cd57a"}, + {file = "propcache-0.4.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fd2dbc472da1f772a4dae4fa24be938a6c544671a912e30529984dd80400cd88"}, + {file = "propcache-0.4.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:daede9cd44e0f8bdd9e6cc9a607fc81feb80fae7a5fc6cecaff0e0bb32e42d00"}, + {file = "propcache-0.4.1-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:71b749281b816793678ae7f3d0d84bd36e694953822eaad408d682efc5ca18e0"}, + {file = "propcache-0.4.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:0002004213ee1f36cfb3f9a42b5066100c44276b9b72b4e1504cddd3d692e86e"}, + {file = "propcache-0.4.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:fe49d0a85038f36ba9e3ffafa1103e61170b28e95b16622e11be0a0ea07c6781"}, + {file = "propcache-0.4.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:99d43339c83aaf4d32bda60928231848eee470c6bda8d02599cc4cebe872d183"}, + {file = "propcache-0.4.1-cp39-cp39-win32.whl", hash = "sha256:a129e76735bc792794d5177069691c3217898b9f5cee2b2661471e52ffe13f19"}, + {file = "propcache-0.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:948dab269721ae9a87fd16c514a0a2c2a1bdb23a9a61b969b0f9d9ee2968546f"}, + {file = "propcache-0.4.1-cp39-cp39-win_arm64.whl", hash = "sha256:5fd37c406dd6dc85aa743e214cef35dc54bbdd1419baac4f6ae5e5b1a2976938"}, + {file = "propcache-0.4.1-py3-none-any.whl", hash = "sha256:af2a6052aeb6cf17d3e46ee169099044fd8224cbaf75c76a2ef596e8163e2237"}, + {file = "propcache-0.4.1.tar.gz", hash = "sha256:f48107a8c637e80362555f37ecf49abe20370e557cc4ab374f04ec4423c97c3d"}, +] + +[[package]] +name = "protobuf" +version = "6.32.1" +description = "" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "protobuf-6.32.1-cp310-abi3-win32.whl", hash = "sha256:a8a32a84bc9f2aad712041b8b366190f71dde248926da517bde9e832e4412085"}, + {file = "protobuf-6.32.1-cp310-abi3-win_amd64.whl", hash = "sha256:b00a7d8c25fa471f16bc8153d0e53d6c9e827f0953f3c09aaa4331c718cae5e1"}, + {file = "protobuf-6.32.1-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:d8c7e6eb619ffdf105ee4ab76af5a68b60a9d0f66da3ea12d1640e6d8dab7281"}, + {file = "protobuf-6.32.1-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:2f5b80a49e1eb7b86d85fcd23fe92df154b9730a725c3b38c4e43b9d77018bf4"}, + {file = "protobuf-6.32.1-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:b1864818300c297265c83a4982fd3169f97122c299f56a56e2445c3698d34710"}, + {file = "protobuf-6.32.1-cp39-cp39-win32.whl", hash = "sha256:68ff170bac18c8178f130d1ccb94700cf72852298e016a2443bdb9502279e5f1"}, + {file = "protobuf-6.32.1-cp39-cp39-win_amd64.whl", hash = "sha256:d0975d0b2f3e6957111aa3935d08a0eb7e006b1505d825f862a1fffc8348e122"}, + {file = "protobuf-6.32.1-py3-none-any.whl", hash = "sha256:2601b779fc7d32a866c6b4404f9d42a3f67c5b9f3f15b4db3cccabe06b95c346"}, + {file = "protobuf-6.32.1.tar.gz", hash = "sha256:ee2469e4a021474ab9baafea6cd070e5bf27c7d29433504ddea1a4ee5850f68d"}, +] + +[[package]] +name = "psycopg" +version = "3.2.10" +description = "PostgreSQL database adapter for Python" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "psycopg-3.2.10-py3-none-any.whl", hash = "sha256:ab5caf09a9ec42e314a21f5216dbcceac528e0e05142e42eea83a3b28b320ac3"}, + {file = "psycopg-3.2.10.tar.gz", hash = "sha256:0bce99269d16ed18401683a8569b2c5abd94f72f8364856d56c0389bcd50972a"}, +] + +[package.dependencies] +psycopg-binary = {version = "3.2.10", optional = true, markers = "implementation_name != \"pypy\" and extra == \"binary\""} +typing-extensions = {version = ">=4.6", markers = "python_version < \"3.13\""} +tzdata = {version = "*", markers = "sys_platform == \"win32\""} + +[package.extras] +binary = ["psycopg-binary (==3.2.10)"] +c = ["psycopg-c (==3.2.10)"] +dev = ["ast-comments (>=1.1.2)", "black (>=24.1.0)", "codespell (>=2.2)", "dnspython (>=2.1)", "flake8 (>=4.0)", "isort-psycopg", "isort[colors] (>=6.0)", "mypy (>=1.14)", "pre-commit (>=4.0.1)", "types-setuptools (>=57.4)", "types-shapely (>=2.0)", "wheel (>=0.37)"] +docs = ["Sphinx (>=5.0)", "furo (==2022.6.21)", "sphinx-autobuild (>=2021.3.14)", "sphinx-autodoc-typehints (>=1.12)"] +pool = ["psycopg-pool"] +test = ["anyio (>=4.0)", "mypy (>=1.14)", "pproxy (>=2.7)", "pytest (>=6.2.5)", "pytest-cov (>=3.0)", "pytest-randomly (>=3.5)"] + +[[package]] +name = "psycopg-binary" +version = "3.2.10" +description = "PostgreSQL database adapter for Python -- C optimisation distribution" +optional = false +python-versions = ">=3.8" +groups = ["main"] +markers = "implementation_name != \"pypy\"" +files = [ + {file = "psycopg_binary-3.2.10-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:037dc92fc7d3f2adae7680e17216934c15b919d6528b908ac2eb52aecc0addcf"}, + {file = "psycopg_binary-3.2.10-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:84f7e8c5e5031db342ae697c2e8fb48cd708ba56990573b33e53ce626445371d"}, + {file = "psycopg_binary-3.2.10-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:a5a81104d88780018005fe17c37fa55b4afbb6dd3c205963cc56c025d5f1cc32"}, + {file = "psycopg_binary-3.2.10-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:0c23e88e048bbc33f32f5a35981707c9418723d469552dd5ac4e956366e58492"}, + {file = "psycopg_binary-3.2.10-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9c9f2728488ac5848acdbf14bb4fde50f8ba783cbf3c19e9abd506741389fa7f"}, + {file = "psycopg_binary-3.2.10-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ab1c6d761c4ee581016823dcc02f29b16ad69177fcbba88a9074c924fc31813e"}, + {file = "psycopg_binary-3.2.10-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:a024b3ee539a475cbc59df877c8ecdd6f8552a1b522b69196935bc26dc6152fb"}, + {file = "psycopg_binary-3.2.10-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:50130c0d1a2a01ec3d41631df86b6c1646c76718be000600a399dc1aad80b813"}, + {file = "psycopg_binary-3.2.10-cp310-cp310-win_amd64.whl", hash = "sha256:7fa1626225a162924d2da0ff4ef77869f7a8501d320355d2732be5bf2dda6138"}, + {file = "psycopg_binary-3.2.10-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:db0eb06a19e4c64a08db0db80875ede44939af6a2afc281762c338fad5d6e547"}, + {file = "psycopg_binary-3.2.10-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d922fdd49ed17c558b6b2f9ae2054c3d0cced2a34e079ce5a41c86904d0203f7"}, + {file = "psycopg_binary-3.2.10-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d557a94cd6d2e775b3af6cc0bd0ff0d9d641820b5cc3060ccf1f5ca2bf971217"}, + {file = "psycopg_binary-3.2.10-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:29b6bb87959515bc8b6abef10d8d23a9a681f03e48e9f0c8adb4b9fb7fa73f11"}, + {file = "psycopg_binary-3.2.10-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1b29285474e3339d0840e1b5079fdb0481914108f92ec62de0c87ae333c60b24"}, + {file = "psycopg_binary-3.2.10-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:62590dd113d10cd9c08251cb80b32e2e8aaf01ece04a700322e776b1d216959f"}, + {file = "psycopg_binary-3.2.10-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:764a5b9b40ad371c55dfdf95374d89e44a82fd62272d4fceebea0adb8930e2fb"}, + {file = "psycopg_binary-3.2.10-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bd3676a04970cf825d2c771b0c147f91182c5a3653e0dbe958e12383668d0f79"}, + {file = "psycopg_binary-3.2.10-cp311-cp311-win_amd64.whl", hash = "sha256:646048f46192c8d23786cc6ef19f35b7488d4110396391e407eca695fdfe9dcd"}, + {file = "psycopg_binary-3.2.10-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:1dee2f4d2adc9adacbfecf8254bd82f6ac95cff707e1b9b99aa721cd1ef16b47"}, + {file = "psycopg_binary-3.2.10-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8b45e65383da9c4a42a56f817973e521e893f4faae897fe9f1a971f9fe799742"}, + {file = "psycopg_binary-3.2.10-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:484d2b1659afe0f8f1cef5ea960bb640e96fa864faf917086f9f833f5c7a8034"}, + {file = "psycopg_binary-3.2.10-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:3bb4046973264ebc8cb7e20a83882d68577c1f26a6f8ad4fe52e4468cd9a8eee"}, + {file = "psycopg_binary-3.2.10-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:14bcbcac0cab465d88b2581e43ec01af4b01c9833e663f1352e05cb41be19e44"}, + {file = "psycopg_binary-3.2.10-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:70bb7f665587dfd79e69f48b34efe226149454d7aab138ed22d5431d703de2f6"}, + {file = "psycopg_binary-3.2.10-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:d2fe9eaa367f6171ab1a21a7dcb335eb2398be7f8bb7e04a20e2260aedc6f782"}, + {file = "psycopg_binary-3.2.10-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:299834cce3eec0c48aae5a5207fc8f0c558fd65f2ceab1a36693329847da956b"}, + {file = "psycopg_binary-3.2.10-cp312-cp312-win_amd64.whl", hash = "sha256:e037aac8dc894d147ef33056fc826ee5072977107a3fdf06122224353a057598"}, + {file = "psycopg_binary-3.2.10-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:55b14f2402be027fe1568bc6c4d75ac34628ff5442a70f74137dadf99f738e3b"}, + {file = "psycopg_binary-3.2.10-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:43d803fb4e108a67c78ba58f3e6855437ca25d56504cae7ebbfbd8fce9b59247"}, + {file = "psycopg_binary-3.2.10-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:470594d303928ab72a1ffd179c9c7bde9d00f76711d6b0c28f8a46ddf56d9807"}, + {file = "psycopg_binary-3.2.10-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:a1d4e4d309049e3cb61269652a3ca56cb598da30ecd7eb8cea561e0d18bc1a43"}, + {file = "psycopg_binary-3.2.10-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a92ff1c2cd79b3966d6a87e26ceb222ecd5581b5ae4b58961f126af806a861ed"}, + {file = "psycopg_binary-3.2.10-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac0365398947879c9827b319217096be727da16c94422e0eb3cf98c930643162"}, + {file = "psycopg_binary-3.2.10-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:42ee399c2613b470a87084ed79b06d9d277f19b0457c10e03a4aef7059097abc"}, + {file = "psycopg_binary-3.2.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2028073fc12cd70ba003309d1439c0c4afab4a7eee7653b8c91213064fffe12b"}, + {file = "psycopg_binary-3.2.10-cp313-cp313-win_amd64.whl", hash = "sha256:8390db6d2010ffcaf7f2b42339a2da620a7125d37029c1f9b72dfb04a8e7be6f"}, + {file = "psycopg_binary-3.2.10-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:b34c278a58aa79562afe7f45e0455b1f4cad5974fc3d5674cc5f1f9f57e97fc5"}, + {file = "psycopg_binary-3.2.10-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:810f65b9ef1fe9dddb5c05937884ea9563aaf4e1a2c3d138205231ed5f439511"}, + {file = "psycopg_binary-3.2.10-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8923487c3898c65e1450847e15d734bb2e6adbd2e79d2d1dd5ad829a1306bdc0"}, + {file = "psycopg_binary-3.2.10-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:7950ff79df7a453ac8a7d7a74694055b6c15905b0a2b6e3c99eb59c51a3f9bf7"}, + {file = "psycopg_binary-3.2.10-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0c2b95e83fda70ed2b0b4fadd8538572e4a4d987b721823981862d1ab56cc760"}, + {file = "psycopg_binary-3.2.10-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:20384985fbc650c09a547a13c6d7f91bb42020d38ceafd2b68b7fc4a48a1f160"}, + {file = "psycopg_binary-3.2.10-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:1f6982609b8ff8fcd67299b67cd5787da1876f3bb28fedd547262cfa8ddedf94"}, + {file = "psycopg_binary-3.2.10-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:bf30dcf6aaaa8d4779a20d2158bdf81cc8e84ce8eee595d748a7671c70c7b890"}, + {file = "psycopg_binary-3.2.10-cp314-cp314-win_amd64.whl", hash = "sha256:d5c6a66a76022af41970bf19f51bc6bf87bd10165783dd1d40484bfd87d6b382"}, + {file = "psycopg_binary-3.2.10-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:901729188b3fd5625970650ca1167786847dee0b92930c2858724d1a5e25dee1"}, + {file = "psycopg_binary-3.2.10-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d7d05174276bb403b8a57e01b857d96b0ac2a6879c5ce06a5cac2d1115763081"}, + {file = "psycopg_binary-3.2.10-cp38-cp38-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:37b42b2f5f58df1f07a5df1b0c2bcc9bd3b9c105e2e988923bfa47aa4ae967da"}, + {file = "psycopg_binary-3.2.10-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6fe450a98a0788b721b1b8302f0ba9be6eca82faf74bf7a86d794cd6484c7e27"}, + {file = "psycopg_binary-3.2.10-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:a28f24a7b68456bd31209b027a5b04304d37eb1d622ef847bf8c47933218a738"}, + {file = "psycopg_binary-3.2.10-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:5369202e0e764193eac311b5a337d8cd58b1e23b822ddb7a559ed9f683d97623"}, + {file = "psycopg_binary-3.2.10-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:8f4ae059c6c9e491cdc3f39f9fc4f09373ef281c6cc381499269dcff21abafc9"}, + {file = "psycopg_binary-3.2.10-cp38-cp38-win_amd64.whl", hash = "sha256:3e115930af2f38f4bbb5f1b61b598ceb802f091c1592c0fe0571c796b714b89a"}, + {file = "psycopg_binary-3.2.10-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0738320a8d405f98743227ff70ed8fac9670870289435f4861dc640cef4a61d3"}, + {file = "psycopg_binary-3.2.10-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:89440355d1b163b11dc661ae64a5667578aab1b80bbf71ced90693d88e9863e1"}, + {file = "psycopg_binary-3.2.10-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3234605839e7d7584bd0a20716395eba34d368a5099dafe7896c943facac98fc"}, + {file = "psycopg_binary-3.2.10-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:725843fd444075cc6c9989f5b25ca83ac68d8d70b58e1f476fbb4096975e43cc"}, + {file = "psycopg_binary-3.2.10-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:447afc326cbc95ed67c0cd27606c0f81fa933b830061e096dbd37e08501cb3de"}, + {file = "psycopg_binary-3.2.10-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5334a61a00ccb722f0b28789e265c7a273cfd10d5a1ed6bf062686fbb71e7032"}, + {file = "psycopg_binary-3.2.10-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:183a59cbdcd7e156669577fd73a9e917b1ee664e620f1e31ae138d24c7714693"}, + {file = "psycopg_binary-3.2.10-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:8fa2efaf5e2f8c289a185c91c80a624a8f97aa17fbedcbc68f373d089b332afd"}, + {file = "psycopg_binary-3.2.10-cp39-cp39-win_amd64.whl", hash = "sha256:6220d6efd6e2df7b67d70ed60d653106cd3b70c5cb8cbe4e9f0a142a5db14015"}, +] + +[[package]] +name = "pyasn1" +version = "0.6.1" +description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629"}, + {file = "pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034"}, +] + +[[package]] +name = "pyasn1-modules" +version = "0.4.2" +description = "A collection of ASN.1-based protocols modules" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "pyasn1_modules-0.4.2-py3-none-any.whl", hash = "sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a"}, + {file = "pyasn1_modules-0.4.2.tar.gz", hash = "sha256:677091de870a80aae844b1ca6134f54652fa2c8c5a52aa396440ac3106e941e6"}, +] + +[package.dependencies] +pyasn1 = ">=0.6.1,<0.7.0" + +[[package]] +name = "pycparser" +version = "2.23" +description = "C parser in Python" +optional = false +python-versions = ">=3.8" +groups = ["main"] +markers = "implementation_name != \"PyPy\"" +files = [ + {file = "pycparser-2.23-py3-none-any.whl", hash = "sha256:e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934"}, + {file = "pycparser-2.23.tar.gz", hash = "sha256:78816d4f24add8f10a06d6f05b4d424ad9e96cfebf68a4ddc99c65c0720d00c2"}, +] + +[[package]] +name = "pycryptodome" +version = "3.23.0" +description = "Cryptographic library for Python" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["main"] +files = [ + {file = "pycryptodome-3.23.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a176b79c49af27d7f6c12e4b178b0824626f40a7b9fed08f712291b6d54bf566"}, + {file = "pycryptodome-3.23.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:573a0b3017e06f2cffd27d92ef22e46aa3be87a2d317a5abf7cc0e84e321bd75"}, + {file = "pycryptodome-3.23.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:63dad881b99ca653302b2c7191998dd677226222a3f2ea79999aa51ce695f720"}, + {file = "pycryptodome-3.23.0-cp27-cp27m-win32.whl", hash = "sha256:b34e8e11d97889df57166eda1e1ddd7676da5fcd4d71a0062a760e75060514b4"}, + {file = "pycryptodome-3.23.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:7ac1080a8da569bde76c0a104589c4f414b8ba296c0b3738cf39a466a9fb1818"}, + {file = "pycryptodome-3.23.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:6fe8258e2039eceb74dfec66b3672552b6b7d2c235b2dfecc05d16b8921649a8"}, + {file = "pycryptodome-3.23.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:0011f7f00cdb74879142011f95133274741778abba114ceca229adbf8e62c3e4"}, + {file = "pycryptodome-3.23.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:90460fc9e088ce095f9ee8356722d4f10f86e5be06e2354230a9880b9c549aae"}, + {file = "pycryptodome-3.23.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4764e64b269fc83b00f682c47443c2e6e85b18273712b98aa43bcb77f8570477"}, + {file = "pycryptodome-3.23.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb8f24adb74984aa0e5d07a2368ad95276cf38051fe2dc6605cbcf482e04f2a7"}, + {file = "pycryptodome-3.23.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d97618c9c6684a97ef7637ba43bdf6663a2e2e77efe0f863cce97a76af396446"}, + {file = "pycryptodome-3.23.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9a53a4fe5cb075075d515797d6ce2f56772ea7e6a1e5e4b96cf78a14bac3d265"}, + {file = "pycryptodome-3.23.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:763d1d74f56f031788e5d307029caef067febf890cd1f8bf61183ae142f1a77b"}, + {file = "pycryptodome-3.23.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:954af0e2bd7cea83ce72243b14e4fb518b18f0c1649b576d114973e2073b273d"}, + {file = "pycryptodome-3.23.0-cp313-cp313t-win32.whl", hash = "sha256:257bb3572c63ad8ba40b89f6fc9d63a2a628e9f9708d31ee26560925ebe0210a"}, + {file = "pycryptodome-3.23.0-cp313-cp313t-win_amd64.whl", hash = "sha256:6501790c5b62a29fcb227bd6b62012181d886a767ce9ed03b303d1f22eb5c625"}, + {file = "pycryptodome-3.23.0-cp313-cp313t-win_arm64.whl", hash = "sha256:9a77627a330ab23ca43b48b130e202582e91cc69619947840ea4d2d1be21eb39"}, + {file = "pycryptodome-3.23.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:187058ab80b3281b1de11c2e6842a357a1f71b42cb1e15bce373f3d238135c27"}, + {file = "pycryptodome-3.23.0-cp37-abi3-macosx_10_9_x86_64.whl", hash = "sha256:cfb5cd445280c5b0a4e6187a7ce8de5a07b5f3f897f235caa11f1f435f182843"}, + {file = "pycryptodome-3.23.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67bd81fcbe34f43ad9422ee8fd4843c8e7198dd88dd3d40e6de42ee65fbe1490"}, + {file = "pycryptodome-3.23.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8987bd3307a39bc03df5c8e0e3d8be0c4c3518b7f044b0f4c15d1aa78f52575"}, + {file = "pycryptodome-3.23.0-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aa0698f65e5b570426fc31b8162ed4603b0c2841cbb9088e2b01641e3065915b"}, + {file = "pycryptodome-3.23.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:53ecbafc2b55353edcebd64bf5da94a2a2cdf5090a6915bcca6eca6cc452585a"}, + {file = "pycryptodome-3.23.0-cp37-abi3-musllinux_1_2_i686.whl", hash = "sha256:156df9667ad9f2ad26255926524e1c136d6664b741547deb0a86a9acf5ea631f"}, + {file = "pycryptodome-3.23.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:dea827b4d55ee390dc89b2afe5927d4308a8b538ae91d9c6f7a5090f397af1aa"}, + {file = "pycryptodome-3.23.0-cp37-abi3-win32.whl", hash = "sha256:507dbead45474b62b2bbe318eb1c4c8ee641077532067fec9c1aa82c31f84886"}, + {file = "pycryptodome-3.23.0-cp37-abi3-win_amd64.whl", hash = "sha256:c75b52aacc6c0c260f204cbdd834f76edc9fb0d8e0da9fbf8352ef58202564e2"}, + {file = "pycryptodome-3.23.0-cp37-abi3-win_arm64.whl", hash = "sha256:11eeeb6917903876f134b56ba11abe95c0b0fd5e3330def218083c7d98bbcb3c"}, + {file = "pycryptodome-3.23.0-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:350ebc1eba1da729b35ab7627a833a1a355ee4e852d8ba0447fafe7b14504d56"}, + {file = "pycryptodome-3.23.0-pp27-pypy_73-win32.whl", hash = "sha256:93837e379a3e5fd2bb00302a47aee9fdf7940d83595be3915752c74033d17ca7"}, + {file = "pycryptodome-3.23.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ddb95b49df036ddd264a0ad246d1be5b672000f12d6961ea2c267083a5e19379"}, + {file = "pycryptodome-3.23.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e95564beb8782abfd9e431c974e14563a794a4944c29d6d3b7b5ea042110b4"}, + {file = "pycryptodome-3.23.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14e15c081e912c4b0d75632acd8382dfce45b258667aa3c67caf7a4d4c13f630"}, + {file = "pycryptodome-3.23.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7fc76bf273353dc7e5207d172b83f569540fc9a28d63171061c42e361d22353"}, + {file = "pycryptodome-3.23.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:45c69ad715ca1a94f778215a11e66b7ff989d792a4d63b68dc586a1da1392ff5"}, + {file = "pycryptodome-3.23.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:865d83c906b0fc6a59b510deceee656b6bc1c4fa0d82176e2b77e97a420a996a"}, + {file = "pycryptodome-3.23.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89d4d56153efc4d81defe8b65fd0821ef8b2d5ddf8ed19df31ba2f00872b8002"}, + {file = "pycryptodome-3.23.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3f2d0aaf8080bda0587d58fc9fe4766e012441e2eed4269a77de6aea981c8be"}, + {file = "pycryptodome-3.23.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:64093fc334c1eccfd3933c134c4457c34eaca235eeae49d69449dc4728079339"}, + {file = "pycryptodome-3.23.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:ce64e84a962b63a47a592690bdc16a7eaf709d2c2697ababf24a0def566899a6"}, + {file = "pycryptodome-3.23.0.tar.gz", hash = "sha256:447700a657182d60338bab09fdb27518f8856aecd80ae4c6bdddb67ff5da44ef"}, +] + +[[package]] +name = "pycryptodomex" +version = "3.23.0" +description = "Cryptographic library for Python" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["main"] +files = [ + {file = "pycryptodomex-3.23.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:add243d204e125f189819db65eed55e6b4713f70a7e9576c043178656529cec7"}, + {file = "pycryptodomex-3.23.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:1c6d919fc8429e5cb228ba8c0d4d03d202a560b421c14867a65f6042990adc8e"}, + {file = "pycryptodomex-3.23.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:1c3a65ad441746b250d781910d26b7ed0a396733c6f2dbc3327bd7051ec8a541"}, + {file = "pycryptodomex-3.23.0-cp27-cp27m-win32.whl", hash = "sha256:47f6d318fe864d02d5e59a20a18834819596c4ed1d3c917801b22b92b3ffa648"}, + {file = "pycryptodomex-3.23.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:d9825410197a97685d6a1fa2a86196430b01877d64458a20e95d4fd00d739a08"}, + {file = "pycryptodomex-3.23.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:267a3038f87a8565bd834317dbf053a02055915acf353bf42ededb9edaf72010"}, + {file = "pycryptodomex-3.23.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:7b37e08e3871efe2187bc1fd9320cc81d87caf19816c648f24443483005ff886"}, + {file = "pycryptodomex-3.23.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:91979028227543010d7b2ba2471cf1d1e398b3f183cb105ac584df0c36dac28d"}, + {file = "pycryptodomex-3.23.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b8962204c47464d5c1c4038abeadd4514a133b28748bcd9fa5b6d62e3cec6fa"}, + {file = "pycryptodomex-3.23.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a33986a0066860f7fcf7c7bd2bc804fa90e434183645595ae7b33d01f3c91ed8"}, + {file = "pycryptodomex-3.23.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7947ab8d589e3178da3d7cdeabe14f841b391e17046954f2fbcd941705762b5"}, + {file = "pycryptodomex-3.23.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c25e30a20e1b426e1f0fa00131c516f16e474204eee1139d1603e132acffc314"}, + {file = "pycryptodomex-3.23.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:da4fa650cef02db88c2b98acc5434461e027dce0ae8c22dd5a69013eaf510006"}, + {file = "pycryptodomex-3.23.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:58b851b9effd0d072d4ca2e4542bf2a4abcf13c82a29fd2c93ce27ee2a2e9462"}, + {file = "pycryptodomex-3.23.0-cp313-cp313t-win32.whl", hash = "sha256:a9d446e844f08299236780f2efa9898c818fe7e02f17263866b8550c7d5fb328"}, + {file = "pycryptodomex-3.23.0-cp313-cp313t-win_amd64.whl", hash = "sha256:bc65bdd9fc8de7a35a74cab1c898cab391a4add33a8fe740bda00f5976ca4708"}, + {file = "pycryptodomex-3.23.0-cp313-cp313t-win_arm64.whl", hash = "sha256:c885da45e70139464f082018ac527fdaad26f1657a99ee13eecdce0f0ca24ab4"}, + {file = "pycryptodomex-3.23.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:06698f957fe1ab229a99ba2defeeae1c09af185baa909a31a5d1f9d42b1aaed6"}, + {file = "pycryptodomex-3.23.0-cp37-abi3-macosx_10_9_x86_64.whl", hash = "sha256:b2c2537863eccef2d41061e82a881dcabb04944c5c06c5aa7110b577cc487545"}, + {file = "pycryptodomex-3.23.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:43c446e2ba8df8889e0e16f02211c25b4934898384c1ec1ec04d7889c0333587"}, + {file = "pycryptodomex-3.23.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f489c4765093fb60e2edafdf223397bc716491b2b69fe74367b70d6999257a5c"}, + {file = "pycryptodomex-3.23.0-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bdc69d0d3d989a1029df0eed67cc5e8e5d968f3724f4519bd03e0ec68df7543c"}, + {file = "pycryptodomex-3.23.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:6bbcb1dd0f646484939e142462d9e532482bc74475cecf9c4903d4e1cd21f003"}, + {file = "pycryptodomex-3.23.0-cp37-abi3-musllinux_1_2_i686.whl", hash = "sha256:8a4fcd42ccb04c31268d1efeecfccfd1249612b4de6374205376b8f280321744"}, + {file = "pycryptodomex-3.23.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:55ccbe27f049743a4caf4f4221b166560d3438d0b1e5ab929e07ae1702a4d6fd"}, + {file = "pycryptodomex-3.23.0-cp37-abi3-win32.whl", hash = "sha256:189afbc87f0b9f158386bf051f720e20fa6145975f1e76369303d0f31d1a8d7c"}, + {file = "pycryptodomex-3.23.0-cp37-abi3-win_amd64.whl", hash = "sha256:52e5ca58c3a0b0bd5e100a9fbc8015059b05cffc6c66ce9d98b4b45e023443b9"}, + {file = "pycryptodomex-3.23.0-cp37-abi3-win_arm64.whl", hash = "sha256:02d87b80778c171445d67e23d1caef279bf4b25c3597050ccd2e13970b57fd51"}, + {file = "pycryptodomex-3.23.0-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:febec69c0291efd056c65691b6d9a339f8b4bc43c6635b8699471248fe897fea"}, + {file = "pycryptodomex-3.23.0-pp27-pypy_73-win32.whl", hash = "sha256:c84b239a1f4ec62e9c789aafe0543f0594f0acd90c8d9e15bcece3efe55eca66"}, + {file = "pycryptodomex-3.23.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ebfff755c360d674306e5891c564a274a47953562b42fb74a5c25b8fc1fb1cb5"}, + {file = "pycryptodomex-3.23.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eca54f4bb349d45afc17e3011ed4264ef1cc9e266699874cdd1349c504e64798"}, + {file = "pycryptodomex-3.23.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2596e643d4365e14d0879dc5aafe6355616c61c2176009270f3048f6d9a61f"}, + {file = "pycryptodomex-3.23.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fdfac7cda115bca3a5abb2f9e43bc2fb66c2b65ab074913643803ca7083a79ea"}, + {file = "pycryptodomex-3.23.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:14c37aaece158d0ace436f76a7bb19093db3b4deade9797abfc39ec6cd6cc2fe"}, + {file = "pycryptodomex-3.23.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7de1e40a41a5d7f1ac42b6569b10bcdded34339950945948529067d8426d2785"}, + {file = "pycryptodomex-3.23.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bffc92138d75664b6d543984db7893a628559b9e78658563b0395e2a5fb47ed9"}, + {file = "pycryptodomex-3.23.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df027262368334552db2c0ce39706b3fb32022d1dce34673d0f9422df004b96a"}, + {file = "pycryptodomex-3.23.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4e79f1aaff5a3a374e92eb462fa9e598585452135012e2945f96874ca6eeb1ff"}, + {file = "pycryptodomex-3.23.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:27e13c80ac9a0a1d050ef0a7e0a18cc04c8850101ec891815b6c5a0375e8a245"}, + {file = "pycryptodomex-3.23.0.tar.gz", hash = "sha256:71909758f010c82bc99b0abf4ea12012c98962fbf0583c2164f8b84533c2e4da"}, +] + +[[package]] +name = "pydantic" +version = "2.12.2" +description = "Data validation using Python type hints" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "pydantic-2.12.2-py3-none-any.whl", hash = "sha256:25ff718ee909acd82f1ff9b1a4acfd781bb23ab3739adaa7144f19a6a4e231ae"}, + {file = "pydantic-2.12.2.tar.gz", hash = "sha256:7b8fa15b831a4bbde9d5b84028641ac3080a4ca2cbd4a621a661687e741624fd"}, +] + +[package.dependencies] +annotated-types = ">=0.6.0" +pydantic-core = "2.41.4" +typing-extensions = ">=4.14.1" +typing-inspection = ">=0.4.2" + +[package.extras] +email = ["email-validator (>=2.0.0)"] +timezone = ["tzdata"] + +[[package]] +name = "pydantic-core" +version = "2.41.4" +description = "Core functionality for Pydantic validation and serialization" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "pydantic_core-2.41.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2442d9a4d38f3411f22eb9dd0912b7cbf4b7d5b6c92c4173b75d3e1ccd84e36e"}, + {file = "pydantic_core-2.41.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:30a9876226dda131a741afeab2702e2d127209bde3c65a2b8133f428bc5d006b"}, + {file = "pydantic_core-2.41.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d55bbac04711e2980645af68b97d445cdbcce70e5216de444a6c4b6943ebcccd"}, + {file = "pydantic_core-2.41.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e1d778fb7849a42d0ee5927ab0f7453bf9f85eef8887a546ec87db5ddb178945"}, + {file = "pydantic_core-2.41.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1b65077a4693a98b90ec5ad8f203ad65802a1b9b6d4a7e48066925a7e1606706"}, + {file = "pydantic_core-2.41.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:62637c769dee16eddb7686bf421be48dfc2fae93832c25e25bc7242e698361ba"}, + {file = "pydantic_core-2.41.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2dfe3aa529c8f501babf6e502936b9e8d4698502b2cfab41e17a028d91b1ac7b"}, + {file = "pydantic_core-2.41.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ca2322da745bf2eeb581fc9ea3bbb31147702163ccbcbf12a3bb630e4bf05e1d"}, + {file = "pydantic_core-2.41.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e8cd3577c796be7231dcf80badcf2e0835a46665eaafd8ace124d886bab4d700"}, + {file = "pydantic_core-2.41.4-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:1cae8851e174c83633f0833e90636832857297900133705ee158cf79d40f03e6"}, + {file = "pydantic_core-2.41.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a26d950449aae348afe1ac8be5525a00ae4235309b729ad4d3399623125b43c9"}, + {file = "pydantic_core-2.41.4-cp310-cp310-win32.whl", hash = "sha256:0cf2a1f599efe57fa0051312774280ee0f650e11152325e41dfd3018ef2c1b57"}, + {file = "pydantic_core-2.41.4-cp310-cp310-win_amd64.whl", hash = "sha256:a8c2e340d7e454dc3340d3d2e8f23558ebe78c98aa8f68851b04dcb7bc37abdc"}, + {file = "pydantic_core-2.41.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:28ff11666443a1a8cf2a044d6a545ebffa8382b5f7973f22c36109205e65dc80"}, + {file = "pydantic_core-2.41.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:61760c3925d4633290292bad462e0f737b840508b4f722247d8729684f6539ae"}, + {file = "pydantic_core-2.41.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eae547b7315d055b0de2ec3965643b0ab82ad0106a7ffd29615ee9f266a02827"}, + {file = "pydantic_core-2.41.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ef9ee5471edd58d1fcce1c80ffc8783a650e3e3a193fe90d52e43bb4d87bff1f"}, + {file = "pydantic_core-2.41.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:15dd504af121caaf2c95cb90c0ebf71603c53de98305621b94da0f967e572def"}, + {file = "pydantic_core-2.41.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3a926768ea49a8af4d36abd6a8968b8790f7f76dd7cbd5a4c180db2b4ac9a3a2"}, + {file = "pydantic_core-2.41.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6916b9b7d134bff5440098a4deb80e4cb623e68974a87883299de9124126c2a8"}, + {file = "pydantic_core-2.41.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5cf90535979089df02e6f17ffd076f07237efa55b7343d98760bde8743c4b265"}, + {file = "pydantic_core-2.41.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:7533c76fa647fade2d7ec75ac5cc079ab3f34879626dae5689b27790a6cf5a5c"}, + {file = "pydantic_core-2.41.4-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:37e516bca9264cbf29612539801ca3cd5d1be465f940417b002905e6ed79d38a"}, + {file = "pydantic_core-2.41.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0c19cb355224037c83642429b8ce261ae108e1c5fbf5c028bac63c77b0f8646e"}, + {file = "pydantic_core-2.41.4-cp311-cp311-win32.whl", hash = "sha256:09c2a60e55b357284b5f31f5ab275ba9f7f70b7525e18a132ec1f9160b4f1f03"}, + {file = "pydantic_core-2.41.4-cp311-cp311-win_amd64.whl", hash = "sha256:711156b6afb5cb1cb7c14a2cc2c4a8b4c717b69046f13c6b332d8a0a8f41ca3e"}, + {file = "pydantic_core-2.41.4-cp311-cp311-win_arm64.whl", hash = "sha256:6cb9cf7e761f4f8a8589a45e49ed3c0d92d1d696a45a6feaee8c904b26efc2db"}, + {file = "pydantic_core-2.41.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:ab06d77e053d660a6faaf04894446df7b0a7e7aba70c2797465a0a1af00fc887"}, + {file = "pydantic_core-2.41.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c53ff33e603a9c1179a9364b0a24694f183717b2e0da2b5ad43c316c956901b2"}, + {file = "pydantic_core-2.41.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:304c54176af2c143bd181d82e77c15c41cbacea8872a2225dd37e6544dce9999"}, + {file = "pydantic_core-2.41.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:025ba34a4cf4fb32f917d5d188ab5e702223d3ba603be4d8aca2f82bede432a4"}, + {file = "pydantic_core-2.41.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b9f5f30c402ed58f90c70e12eff65547d3ab74685ffe8283c719e6bead8ef53f"}, + {file = "pydantic_core-2.41.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd96e5d15385d301733113bcaa324c8bcf111275b7675a9c6e88bfb19fc05e3b"}, + {file = "pydantic_core-2.41.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98f348cbb44fae6e9653c1055db7e29de67ea6a9ca03a5fa2c2e11a47cff0e47"}, + {file = "pydantic_core-2.41.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ec22626a2d14620a83ca583c6f5a4080fa3155282718b6055c2ea48d3ef35970"}, + {file = "pydantic_core-2.41.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3a95d4590b1f1a43bf33ca6d647b990a88f4a3824a8c4572c708f0b45a5290ed"}, + {file = "pydantic_core-2.41.4-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:f9672ab4d398e1b602feadcffcdd3af44d5f5e6ddc15bc7d15d376d47e8e19f8"}, + {file = "pydantic_core-2.41.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:84d8854db5f55fead3b579f04bda9a36461dab0730c5d570e1526483e7bb8431"}, + {file = "pydantic_core-2.41.4-cp312-cp312-win32.whl", hash = "sha256:9be1c01adb2ecc4e464392c36d17f97e9110fbbc906bcbe1c943b5b87a74aabd"}, + {file = "pydantic_core-2.41.4-cp312-cp312-win_amd64.whl", hash = "sha256:d682cf1d22bab22a5be08539dca3d1593488a99998f9f412137bc323179067ff"}, + {file = "pydantic_core-2.41.4-cp312-cp312-win_arm64.whl", hash = "sha256:833eebfd75a26d17470b58768c1834dfc90141b7afc6eb0429c21fc5a21dcfb8"}, + {file = "pydantic_core-2.41.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:85e050ad9e5f6fe1004eec65c914332e52f429bc0ae12d6fa2092407a462c746"}, + {file = "pydantic_core-2.41.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e7393f1d64792763a48924ba31d1e44c2cfbc05e3b1c2c9abb4ceeadd912cced"}, + {file = "pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94dab0940b0d1fb28bcab847adf887c66a27a40291eedf0b473be58761c9799a"}, + {file = "pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:de7c42f897e689ee6f9e93c4bec72b99ae3b32a2ade1c7e4798e690ff5246e02"}, + {file = "pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:664b3199193262277b8b3cd1e754fb07f2c6023289c815a1e1e8fb415cb247b1"}, + {file = "pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d95b253b88f7d308b1c0b417c4624f44553ba4762816f94e6986819b9c273fb2"}, + {file = "pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a1351f5bbdbbabc689727cb91649a00cb9ee7203e0a6e54e9f5ba9e22e384b84"}, + {file = "pydantic_core-2.41.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1affa4798520b148d7182da0615d648e752de4ab1a9566b7471bc803d88a062d"}, + {file = "pydantic_core-2.41.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7b74e18052fea4aa8dea2fb7dbc23d15439695da6cbe6cfc1b694af1115df09d"}, + {file = "pydantic_core-2.41.4-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:285b643d75c0e30abda9dc1077395624f314a37e3c09ca402d4015ef5979f1a2"}, + {file = "pydantic_core-2.41.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:f52679ff4218d713b3b33f88c89ccbf3a5c2c12ba665fb80ccc4192b4608dbab"}, + {file = "pydantic_core-2.41.4-cp313-cp313-win32.whl", hash = "sha256:ecde6dedd6fff127c273c76821bb754d793be1024bc33314a120f83a3c69460c"}, + {file = "pydantic_core-2.41.4-cp313-cp313-win_amd64.whl", hash = "sha256:d081a1f3800f05409ed868ebb2d74ac39dd0c1ff6c035b5162356d76030736d4"}, + {file = "pydantic_core-2.41.4-cp313-cp313-win_arm64.whl", hash = "sha256:f8e49c9c364a7edcbe2a310f12733aad95b022495ef2a8d653f645e5d20c1564"}, + {file = "pydantic_core-2.41.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:ed97fd56a561f5eb5706cebe94f1ad7c13b84d98312a05546f2ad036bafe87f4"}, + {file = "pydantic_core-2.41.4-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a870c307bf1ee91fc58a9a61338ff780d01bfae45922624816878dce784095d2"}, + {file = "pydantic_core-2.41.4-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d25e97bc1f5f8f7985bdc2335ef9e73843bb561eb1fa6831fdfc295c1c2061cf"}, + {file = "pydantic_core-2.41.4-cp313-cp313t-win_amd64.whl", hash = "sha256:d405d14bea042f166512add3091c1af40437c2e7f86988f3915fabd27b1e9cd2"}, + {file = "pydantic_core-2.41.4-cp313-cp313t-win_arm64.whl", hash = "sha256:19f3684868309db5263a11bace3c45d93f6f24afa2ffe75a647583df22a2ff89"}, + {file = "pydantic_core-2.41.4-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:e9205d97ed08a82ebb9a307e92914bb30e18cdf6f6b12ca4bedadb1588a0bfe1"}, + {file = "pydantic_core-2.41.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:82df1f432b37d832709fbcc0e24394bba04a01b6ecf1ee87578145c19cde12ac"}, + {file = "pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc3b4cc4539e055cfa39a3763c939f9d409eb40e85813257dcd761985a108554"}, + {file = "pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b1eb1754fce47c63d2ff57fdb88c351a6c0150995890088b33767a10218eaa4e"}, + {file = "pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e6ab5ab30ef325b443f379ddb575a34969c333004fca5a1daa0133a6ffaad616"}, + {file = "pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:31a41030b1d9ca497634092b46481b937ff9397a86f9f51bd41c4767b6fc04af"}, + {file = "pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a44ac1738591472c3d020f61c6df1e4015180d6262ebd39bf2aeb52571b60f12"}, + {file = "pydantic_core-2.41.4-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d72f2b5e6e82ab8f94ea7d0d42f83c487dc159c5240d8f83beae684472864e2d"}, + {file = "pydantic_core-2.41.4-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:c4d1e854aaf044487d31143f541f7aafe7b482ae72a022c664b2de2e466ed0ad"}, + {file = "pydantic_core-2.41.4-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:b568af94267729d76e6ee5ececda4e283d07bbb28e8148bb17adad93d025d25a"}, + {file = "pydantic_core-2.41.4-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:6d55fb8b1e8929b341cc313a81a26e0d48aa3b519c1dbaadec3a6a2b4fcad025"}, + {file = "pydantic_core-2.41.4-cp314-cp314-win32.whl", hash = "sha256:5b66584e549e2e32a1398df11da2e0a7eff45d5c2d9db9d5667c5e6ac764d77e"}, + {file = "pydantic_core-2.41.4-cp314-cp314-win_amd64.whl", hash = "sha256:557a0aab88664cc552285316809cab897716a372afaf8efdbef756f8b890e894"}, + {file = "pydantic_core-2.41.4-cp314-cp314-win_arm64.whl", hash = "sha256:3f1ea6f48a045745d0d9f325989d8abd3f1eaf47dd00485912d1a3a63c623a8d"}, + {file = "pydantic_core-2.41.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6c1fe4c5404c448b13188dd8bd2ebc2bdd7e6727fa61ff481bcc2cca894018da"}, + {file = "pydantic_core-2.41.4-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:523e7da4d43b113bf8e7b49fa4ec0c35bf4fe66b2230bfc5c13cc498f12c6c3e"}, + {file = "pydantic_core-2.41.4-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5729225de81fb65b70fdb1907fcf08c75d498f4a6f15af005aabb1fdadc19dfa"}, + {file = "pydantic_core-2.41.4-cp314-cp314t-win_amd64.whl", hash = "sha256:de2cfbb09e88f0f795fd90cf955858fc2c691df65b1f21f0aa00b99f3fbc661d"}, + {file = "pydantic_core-2.41.4-cp314-cp314t-win_arm64.whl", hash = "sha256:d34f950ae05a83e0ede899c595f312ca976023ea1db100cd5aa188f7005e3ab0"}, + {file = "pydantic_core-2.41.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:646e76293345954acea6966149683047b7b2ace793011922208c8e9da12b0062"}, + {file = "pydantic_core-2.41.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cc8e85a63085a137d286e2791037f5fdfff0aabb8b899483ca9c496dd5797338"}, + {file = "pydantic_core-2.41.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:692c622c8f859a17c156492783902d8370ac7e121a611bd6fe92cc71acf9ee8d"}, + {file = "pydantic_core-2.41.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d1e2906efb1031a532600679b424ef1d95d9f9fb507f813951f23320903adbd7"}, + {file = "pydantic_core-2.41.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e04e2f7f8916ad3ddd417a7abdd295276a0bf216993d9318a5d61cc058209166"}, + {file = "pydantic_core-2.41.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:df649916b81822543d1c8e0e1d079235f68acdc7d270c911e8425045a8cfc57e"}, + {file = "pydantic_core-2.41.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66c529f862fdba70558061bb936fe00ddbaaa0c647fd26e4a4356ef1d6561891"}, + {file = "pydantic_core-2.41.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fc3b4c5a1fd3a311563ed866c2c9b62da06cb6398bee186484ce95c820db71cb"}, + {file = "pydantic_core-2.41.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:6e0fc40d84448f941df9b3334c4b78fe42f36e3bf631ad54c3047a0cdddc2514"}, + {file = "pydantic_core-2.41.4-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:44e7625332683b6c1c8b980461475cde9595eff94447500e80716db89b0da005"}, + {file = "pydantic_core-2.41.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:170ee6835f6c71081d031ef1c3b4dc4a12b9efa6a9540f93f95b82f3c7571ae8"}, + {file = "pydantic_core-2.41.4-cp39-cp39-win32.whl", hash = "sha256:3adf61415efa6ce977041ba9745183c0e1f637ca849773afa93833e04b163feb"}, + {file = "pydantic_core-2.41.4-cp39-cp39-win_amd64.whl", hash = "sha256:a238dd3feee263eeaeb7dc44aea4ba1364682c4f9f9467e6af5596ba322c2332"}, + {file = "pydantic_core-2.41.4-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:a1b2cfec3879afb742a7b0bcfa53e4f22ba96571c9e54d6a3afe1052d17d843b"}, + {file = "pydantic_core-2.41.4-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:d175600d975b7c244af6eb9c9041f10059f20b8bbffec9e33fdd5ee3f67cdc42"}, + {file = "pydantic_core-2.41.4-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f184d657fa4947ae5ec9c47bd7e917730fa1cbb78195037e32dcbab50aca5ee"}, + {file = "pydantic_core-2.41.4-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ed810568aeffed3edc78910af32af911c835cc39ebbfacd1f0ab5dd53028e5c"}, + {file = "pydantic_core-2.41.4-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:4f5d640aeebb438517150fdeec097739614421900e4a08db4a3ef38898798537"}, + {file = "pydantic_core-2.41.4-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:4a9ab037b71927babc6d9e7fc01aea9e66dc2a4a34dff06ef0724a4049629f94"}, + {file = "pydantic_core-2.41.4-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4dab9484ec605c3016df9ad4fd4f9a390bc5d816a3b10c6550f8424bb80b18c"}, + {file = "pydantic_core-2.41.4-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd8a5028425820731d8c6c098ab642d7b8b999758e24acae03ed38a66eca8335"}, + {file = "pydantic_core-2.41.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:1e5ab4fc177dd41536b3c32b2ea11380dd3d4619a385860621478ac2d25ceb00"}, + {file = "pydantic_core-2.41.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:3d88d0054d3fa11ce936184896bed3c1c5441d6fa483b498fac6a5d0dd6f64a9"}, + {file = "pydantic_core-2.41.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b2a054a8725f05b4b6503357e0ac1c4e8234ad3b0c2ac130d6ffc66f0e170e2"}, + {file = "pydantic_core-2.41.4-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b0d9db5a161c99375a0c68c058e227bee1d89303300802601d76a3d01f74e258"}, + {file = "pydantic_core-2.41.4-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:6273ea2c8ffdac7b7fda2653c49682db815aebf4a89243a6feccf5e36c18c347"}, + {file = "pydantic_core-2.41.4-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:4c973add636efc61de22530b2ef83a65f39b6d6f656df97f678720e20de26caa"}, + {file = "pydantic_core-2.41.4-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:b69d1973354758007f46cf2d44a4f3d0933f10b6dc9bf15cf1356e037f6f731a"}, + {file = "pydantic_core-2.41.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:3619320641fd212aaf5997b6ca505e97540b7e16418f4a241f44cdf108ffb50d"}, + {file = "pydantic_core-2.41.4-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:491535d45cd7ad7e4a2af4a5169b0d07bebf1adfd164b0368da8aa41e19907a5"}, + {file = "pydantic_core-2.41.4-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:54d86c0cada6aba4ec4c047d0e348cbad7063b87ae0f005d9f8c9ad04d4a92a2"}, + {file = "pydantic_core-2.41.4-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eca1124aced216b2500dc2609eade086d718e8249cb9696660ab447d50a758bd"}, + {file = "pydantic_core-2.41.4-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6c9024169becccf0cb470ada03ee578d7348c119a0d42af3dcf9eda96e3a247c"}, + {file = "pydantic_core-2.41.4-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:26895a4268ae5a2849269f4991cdc97236e4b9c010e51137becf25182daac405"}, + {file = "pydantic_core-2.41.4-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:ca4df25762cf71308c446e33c9b1fdca2923a3f13de616e2a949f38bf21ff5a8"}, + {file = "pydantic_core-2.41.4-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:5a28fcedd762349519276c36634e71853b4541079cab4acaaac60c4421827308"}, + {file = "pydantic_core-2.41.4-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:c173ddcd86afd2535e2b695217e82191580663a1d1928239f877f5a1649ef39f"}, + {file = "pydantic_core-2.41.4.tar.gz", hash = "sha256:70e47929a9d4a1905a67e4b687d5946026390568a8e952b92824118063cee4d5"}, +] + +[package.dependencies] +typing-extensions = ">=4.14.1" + +[[package]] +name = "pygments" +version = "2.19.2" +description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b"}, + {file = "pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887"}, +] + +[package.extras] +windows-terminal = ["colorama (>=0.4.6)"] + +[[package]] +name = "pyopenssl" +version = "24.0.0" +description = "Python wrapper module around the OpenSSL library" +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "pyOpenSSL-24.0.0-py3-none-any.whl", hash = "sha256:ba07553fb6fd6a7a2259adb9b84e12302a9a8a75c44046e8bb5d3e5ee887e3c3"}, + {file = "pyOpenSSL-24.0.0.tar.gz", hash = "sha256:6aa33039a93fffa4563e655b61d11364d01264be8ccb49906101e02a334530bf"}, +] + +[package.dependencies] +cryptography = ">=41.0.5,<43" + +[package.extras] +docs = ["sphinx (!=5.2.0,!=5.2.0.post0,!=7.2.5)", "sphinx-rtd-theme"] +test = ["flaky", "pretend", "pytest (>=3.0.1)"] + +[[package]] +name = "pyreadline3" +version = "3.5.4" +description = "A python implementation of GNU readline." +optional = false +python-versions = ">=3.8" +groups = ["main"] +markers = "sys_platform == \"win32\"" +files = [ + {file = "pyreadline3-3.5.4-py3-none-any.whl", hash = "sha256:eaf8e6cc3c49bcccf145fc6067ba8643d1df34d604a1ec0eccbf7a18e6d3fae6"}, + {file = "pyreadline3-3.5.4.tar.gz", hash = "sha256:8d57d53039a1c75adba8e50dd3d992b28143480816187ea5efbd5c78e6c885b7"}, +] + +[package.extras] +dev = ["build", "flake8", "mypy", "pytest", "twine"] + +[[package]] +name = "pytest" +version = "8.4.2" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79"}, + {file = "pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01"}, +] + +[package.dependencies] +colorama = {version = ">=0.4", markers = "sys_platform == \"win32\""} +iniconfig = ">=1" +packaging = ">=20" +pluggy = ">=1.5,<2" +pygments = ">=2.7.2" + +[package.extras] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "pytest-asyncio" +version = "1.2.0" +description = "Pytest support for asyncio" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "pytest_asyncio-1.2.0-py3-none-any.whl", hash = "sha256:8e17ae5e46d8e7efe51ab6494dd2010f4ca8dae51652aa3c8d55acf50bfb2e99"}, + {file = "pytest_asyncio-1.2.0.tar.gz", hash = "sha256:c609a64a2a8768462d0c99811ddb8bd2583c33fd33cf7f21af1c142e824ffb57"}, +] + +[package.dependencies] +pytest = ">=8.2,<9" +typing-extensions = {version = ">=4.12", markers = "python_version < \"3.13\""} + +[package.extras] +docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1)"] +testing = ["coverage (>=6.2)", "hypothesis (>=5.7.1)"] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +description = "Extensions to the standard Python datetime module" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +groups = ["main"] +files = [ + {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, + {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, +] + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "python-registry" +version = "1.3.1" +description = "Read access to Windows Registry files." +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "python-registry-1.3.1.tar.gz", hash = "sha256:99185f67d5601be3e7843e55902d5769aea1740869b0882f34ff1bd4b43b1eb2"}, + {file = "python_registry-1.3.1-py2-none-any.whl", hash = "sha256:59d3b00c04bca0c4e1a12be0404da6ccf76b87537ee3a3ad2d8fc1bccf6f63ca"}, + {file = "python_registry-1.3.1-py3-none-any.whl", hash = "sha256:b5b8ae07c271dce12dacd24e16af8aa8d56167ebdb360112a4f152b6d04a4ca9"}, +] + +[package.dependencies] +enum-compat = "*" +unicodecsv = "*" + +[[package]] +name = "pyyaml" +version = "6.0.3" +description = "YAML parser and emitter for Python" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "PyYAML-6.0.3-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:c2514fceb77bc5e7a2f7adfaa1feb2fb311607c9cb518dbc378688ec73d8292f"}, + {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c57bb8c96f6d1808c030b1687b9b5fb476abaa47f0db9c0101f5e9f394e97f4"}, + {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:efd7b85f94a6f21e4932043973a7ba2613b059c4a000551892ac9f1d11f5baf3"}, + {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22ba7cfcad58ef3ecddc7ed1db3409af68d023b7f940da23c6c2a1890976eda6"}, + {file = "PyYAML-6.0.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:6344df0d5755a2c9a276d4473ae6b90647e216ab4757f8426893b5dd2ac3f369"}, + {file = "PyYAML-6.0.3-cp38-cp38-win32.whl", hash = "sha256:3ff07ec89bae51176c0549bc4c63aa6202991da2d9a6129d7aef7f1407d3f295"}, + {file = "PyYAML-6.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:5cf4e27da7e3fbed4d6c3d8e797387aaad68102272f8f9752883bc32d61cb87b"}, + {file = "pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b"}, + {file = "pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956"}, + {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8"}, + {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198"}, + {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b"}, + {file = "pyyaml-6.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0"}, + {file = "pyyaml-6.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69"}, + {file = "pyyaml-6.0.3-cp310-cp310-win32.whl", hash = "sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e"}, + {file = "pyyaml-6.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c"}, + {file = "pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e"}, + {file = "pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824"}, + {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c"}, + {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00"}, + {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d"}, + {file = "pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a"}, + {file = "pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4"}, + {file = "pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b"}, + {file = "pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf"}, + {file = "pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196"}, + {file = "pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0"}, + {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28"}, + {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c"}, + {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc"}, + {file = "pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e"}, + {file = "pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea"}, + {file = "pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5"}, + {file = "pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b"}, + {file = "pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd"}, + {file = "pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8"}, + {file = "pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1"}, + {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c"}, + {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5"}, + {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6"}, + {file = "pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6"}, + {file = "pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be"}, + {file = "pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26"}, + {file = "pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c"}, + {file = "pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb"}, + {file = "pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac"}, + {file = "pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310"}, + {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7"}, + {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788"}, + {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5"}, + {file = "pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764"}, + {file = "pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35"}, + {file = "pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac"}, + {file = "pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3"}, + {file = "pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3"}, + {file = "pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba"}, + {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c"}, + {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702"}, + {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c"}, + {file = "pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065"}, + {file = "pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65"}, + {file = "pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9"}, + {file = "pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b"}, + {file = "pyyaml-6.0.3-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:b865addae83924361678b652338317d1bd7e79b1f4596f96b96c77a5a34b34da"}, + {file = "pyyaml-6.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c3355370a2c156cffb25e876646f149d5d68f5e0a3ce86a5084dd0b64a994917"}, + {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3c5677e12444c15717b902a5798264fa7909e41153cdf9ef7ad571b704a63dd9"}, + {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5ed875a24292240029e4483f9d4a4b8a1ae08843b9c54f43fcc11e404532a8a5"}, + {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0150219816b6a1fa26fb4699fb7daa9caf09eb1999f3b70fb6e786805e80375a"}, + {file = "pyyaml-6.0.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:fa160448684b4e94d80416c0fa4aac48967a969efe22931448d853ada8baf926"}, + {file = "pyyaml-6.0.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:27c0abcb4a5dac13684a37f76e701e054692a9b2d3064b70f5e4eb54810553d7"}, + {file = "pyyaml-6.0.3-cp39-cp39-win32.whl", hash = "sha256:1ebe39cb5fc479422b83de611d14e2c0d3bb2a18bbcb01f229ab3cfbd8fee7a0"}, + {file = "pyyaml-6.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:2e71d11abed7344e42a8849600193d15b6def118602c4c176f748e4583246007"}, + {file = "pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f"}, +] + +[[package]] +name = "ruff" +version = "0.9.10" +description = "An extremely fast Python linter and code formatter, written in Rust." +optional = false +python-versions = ">=3.7" +groups = ["dev"] +files = [ + {file = "ruff-0.9.10-py3-none-linux_armv6l.whl", hash = "sha256:eb4d25532cfd9fe461acc83498361ec2e2252795b4f40b17e80692814329e42d"}, + {file = "ruff-0.9.10-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:188a6638dab1aa9bb6228a7302387b2c9954e455fb25d6b4470cb0641d16759d"}, + {file = "ruff-0.9.10-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5284dcac6b9dbc2fcb71fdfc26a217b2ca4ede6ccd57476f52a587451ebe450d"}, + {file = "ruff-0.9.10-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47678f39fa2a3da62724851107f438c8229a3470f533894b5568a39b40029c0c"}, + {file = "ruff-0.9.10-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:99713a6e2766b7a17147b309e8c915b32b07a25c9efd12ada79f217c9c778b3e"}, + {file = "ruff-0.9.10-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:524ee184d92f7c7304aa568e2db20f50c32d1d0caa235d8ddf10497566ea1a12"}, + {file = "ruff-0.9.10-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:df92aeac30af821f9acf819fc01b4afc3dfb829d2782884f8739fb52a8119a16"}, + {file = "ruff-0.9.10-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de42e4edc296f520bb84954eb992a07a0ec5a02fecb834498415908469854a52"}, + {file = "ruff-0.9.10-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d257f95b65806104b6b1ffca0ea53f4ef98454036df65b1eda3693534813ecd1"}, + {file = "ruff-0.9.10-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b60dec7201c0b10d6d11be00e8f2dbb6f40ef1828ee75ed739923799513db24c"}, + {file = "ruff-0.9.10-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:d838b60007da7a39c046fcdd317293d10b845001f38bcb55ba766c3875b01e43"}, + {file = "ruff-0.9.10-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:ccaf903108b899beb8e09a63ffae5869057ab649c1e9231c05ae354ebc62066c"}, + {file = "ruff-0.9.10-py3-none-musllinux_1_2_i686.whl", hash = "sha256:f9567d135265d46e59d62dc60c0bfad10e9a6822e231f5b24032dba5a55be6b5"}, + {file = "ruff-0.9.10-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5f202f0d93738c28a89f8ed9eaba01b7be339e5d8d642c994347eaa81c6d75b8"}, + {file = "ruff-0.9.10-py3-none-win32.whl", hash = "sha256:bfb834e87c916521ce46b1788fbb8484966e5113c02df216680102e9eb960029"}, + {file = "ruff-0.9.10-py3-none-win_amd64.whl", hash = "sha256:f2160eeef3031bf4b17df74e307d4c5fb689a6f3a26a2de3f7ef4044e3c484f1"}, + {file = "ruff-0.9.10-py3-none-win_arm64.whl", hash = "sha256:5fd804c0327a5e5ea26615550e706942f348b197d5475ff34c19733aee4b2e69"}, + {file = "ruff-0.9.10.tar.gz", hash = "sha256:9bacb735d7bada9cfb0f2c227d3658fc443d90a727b47f206fb33f52f3c0eac7"}, +] + +[[package]] +name = "setuptools" +version = "80.9.0" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "setuptools-80.9.0-py3-none-any.whl", hash = "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922"}, + {file = "setuptools-80.9.0.tar.gz", hash = "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c"}, +] + +[package.extras] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)", "ruff (>=0.8.0)"] +core = ["importlib_metadata (>=6)", "jaraco.functools (>=4)", "jaraco.text (>=3.7)", "more_itertools", "more_itertools (>=8.8)", "packaging (>=24.2)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] +cover = ["pytest-cov"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"] +enabler = ["pytest-enabler (>=2.2)"] +test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.7.2)", "jaraco.test (>=5.5)", "packaging (>=24.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] +type = ["importlib_metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (==1.14.*)", "pytest-mypy"] + +[[package]] +name = "six" +version = "1.17.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +groups = ["main"] +files = [ + {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, + {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, +] + +[[package]] +name = "sniffio" +version = "1.3.1" +description = "Sniff out which async library your code is running under" +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, + {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, +] + +[[package]] +name = "starlette" +version = "0.46.2" +description = "The little ASGI library that shines." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "starlette-0.46.2-py3-none-any.whl", hash = "sha256:595633ce89f8ffa71a015caed34a5b2dc1c0cdb3f0f1fbd1e69339cf2abeec35"}, + {file = "starlette-0.46.2.tar.gz", hash = "sha256:7f7361f34eed179294600af672f565727419830b54b7b084efe44bb82d2fccd5"}, +] + +[package.dependencies] +anyio = ">=3.6.2,<5" + +[package.extras] +full = ["httpx (>=0.27.0,<0.29.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.18)", "pyyaml"] + +[[package]] +name = "structlog" +version = "25.4.0" +description = "Structured Logging for Python" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "structlog-25.4.0-py3-none-any.whl", hash = "sha256:fe809ff5c27e557d14e613f45ca441aabda051d119ee5a0102aaba6ce40eed2c"}, + {file = "structlog-25.4.0.tar.gz", hash = "sha256:186cd1b0a8ae762e29417095664adf1d6a31702160a46dacb7796ea82f7409e4"}, +] + +[[package]] +name = "typing-extensions" +version = "4.15.0" +description = "Backported and Experimental Type Hints for Python 3.9+" +optional = false +python-versions = ">=3.9" +groups = ["main", "dev"] +files = [ + {file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"}, + {file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"}, +] +markers = {dev = "python_version < \"3.13\""} + +[[package]] +name = "typing-inspection" +version = "0.4.2" +description = "Runtime typing introspection tools" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7"}, + {file = "typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464"}, +] + +[package.dependencies] +typing-extensions = ">=4.12.0" + +[[package]] +name = "tzdata" +version = "2025.2" +description = "Provider of IANA time zone data" +optional = false +python-versions = ">=2" +groups = ["main"] +markers = "sys_platform == \"win32\"" +files = [ + {file = "tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8"}, + {file = "tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9"}, +] + +[[package]] +name = "unicodecsv" +version = "0.14.1" +description = "Python2's stdlib csv module is nice, but it doesn't support unicode. This module is a drop-in replacement which *does*." +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "unicodecsv-0.14.1.tar.gz", hash = "sha256:018c08037d48649a0412063ff4eda26eaa81eff1546dbffa51fa5293276ff7fc"}, +] + +[[package]] +name = "urllib3" +version = "2.5.0" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc"}, + {file = "urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +h2 = ["h2 (>=4,<5)"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] + +[[package]] +name = "werkzeug" +version = "3.1.3" +description = "The comprehensive WSGI web application library." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "werkzeug-3.1.3-py3-none-any.whl", hash = "sha256:54b78bf3716d19a65be4fceccc0d1d7b89e608834989dfae50ea87564639213e"}, + {file = "werkzeug-3.1.3.tar.gz", hash = "sha256:60723ce945c19328679790e3282cc758aa4a6040e4bb330f53d30fa546d44746"}, +] + +[package.dependencies] +MarkupSafe = ">=2.1.1" + +[package.extras] +watchdog = ["watchdog (>=2.3)"] + +[[package]] +name = "yarl" +version = "1.22.0" +description = "Yet another URL library" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "yarl-1.22.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c7bd6683587567e5a49ee6e336e0612bec8329be1b7d4c8af5687dcdeb67ee1e"}, + {file = "yarl-1.22.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5cdac20da754f3a723cceea5b3448e1a2074866406adeb4ef35b469d089adb8f"}, + {file = "yarl-1.22.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:07a524d84df0c10f41e3ee918846e1974aba4ec017f990dc735aad487a0bdfdf"}, + {file = "yarl-1.22.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e1b329cb8146d7b736677a2440e422eadd775d1806a81db2d4cded80a48efc1a"}, + {file = "yarl-1.22.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:75976c6945d85dbb9ee6308cd7ff7b1fb9409380c82d6119bd778d8fcfe2931c"}, + {file = "yarl-1.22.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:80ddf7a5f8c86cb3eb4bc9028b07bbbf1f08a96c5c0bc1244be5e8fefcb94147"}, + {file = "yarl-1.22.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d332fc2e3c94dad927f2112395772a4e4fedbcf8f80efc21ed7cdfae4d574fdb"}, + {file = "yarl-1.22.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0cf71bf877efeac18b38d3930594c0948c82b64547c1cf420ba48722fe5509f6"}, + {file = "yarl-1.22.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:663e1cadaddae26be034a6ab6072449a8426ddb03d500f43daf952b74553bba0"}, + {file = "yarl-1.22.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:6dcbb0829c671f305be48a7227918cfcd11276c2d637a8033a99a02b67bf9eda"}, + {file = "yarl-1.22.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f0d97c18dfd9a9af4490631905a3f131a8e4c9e80a39353919e2cfed8f00aedc"}, + {file = "yarl-1.22.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:437840083abe022c978470b942ff832c3940b2ad3734d424b7eaffcd07f76737"}, + {file = "yarl-1.22.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a899cbd98dce6f5d8de1aad31cb712ec0a530abc0a86bd6edaa47c1090138467"}, + {file = "yarl-1.22.0-cp310-cp310-win32.whl", hash = "sha256:595697f68bd1f0c1c159fcb97b661fc9c3f5db46498043555d04805430e79bea"}, + {file = "yarl-1.22.0-cp310-cp310-win_amd64.whl", hash = "sha256:cb95a9b1adaa48e41815a55ae740cfda005758104049a640a398120bf02515ca"}, + {file = "yarl-1.22.0-cp310-cp310-win_arm64.whl", hash = "sha256:b85b982afde6df99ecc996990d4ad7ccbdbb70e2a4ba4de0aecde5922ba98a0b"}, + {file = "yarl-1.22.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1ab72135b1f2db3fed3997d7e7dc1b80573c67138023852b6efb336a5eae6511"}, + {file = "yarl-1.22.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:669930400e375570189492dc8d8341301578e8493aec04aebc20d4717f899dd6"}, + {file = "yarl-1.22.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:792a2af6d58177ef7c19cbf0097aba92ca1b9cb3ffdd9c7470e156c8f9b5e028"}, + {file = "yarl-1.22.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3ea66b1c11c9150f1372f69afb6b8116f2dd7286f38e14ea71a44eee9ec51b9d"}, + {file = "yarl-1.22.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3e2daa88dc91870215961e96a039ec73e4937da13cf77ce17f9cad0c18df3503"}, + {file = "yarl-1.22.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ba440ae430c00eee41509353628600212112cd5018d5def7e9b05ea7ac34eb65"}, + {file = "yarl-1.22.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e6438cc8f23a9c1478633d216b16104a586b9761db62bfacb6425bac0a36679e"}, + {file = "yarl-1.22.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c52a6e78aef5cf47a98ef8e934755abf53953379b7d53e68b15ff4420e6683d"}, + {file = "yarl-1.22.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3b06bcadaac49c70f4c88af4ffcfbe3dc155aab3163e75777818092478bcbbe7"}, + {file = "yarl-1.22.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:6944b2dc72c4d7f7052683487e3677456050ff77fcf5e6204e98caf785ad1967"}, + {file = "yarl-1.22.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:d5372ca1df0f91a86b047d1277c2aaf1edb32d78bbcefffc81b40ffd18f027ed"}, + {file = "yarl-1.22.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:51af598701f5299012b8416486b40fceef8c26fc87dc6d7d1f6fc30609ea0aa6"}, + {file = "yarl-1.22.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b266bd01fedeffeeac01a79ae181719ff848a5a13ce10075adbefc8f1daee70e"}, + {file = "yarl-1.22.0-cp311-cp311-win32.whl", hash = "sha256:a9b1ba5610a4e20f655258d5a1fdc7ebe3d837bb0e45b581398b99eb98b1f5ca"}, + {file = "yarl-1.22.0-cp311-cp311-win_amd64.whl", hash = "sha256:078278b9b0b11568937d9509b589ee83ef98ed6d561dfe2020e24a9fd08eaa2b"}, + {file = "yarl-1.22.0-cp311-cp311-win_arm64.whl", hash = "sha256:b6a6f620cfe13ccec221fa312139135166e47ae169f8253f72a0abc0dae94376"}, + {file = "yarl-1.22.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e340382d1afa5d32b892b3ff062436d592ec3d692aeea3bef3a5cfe11bbf8c6f"}, + {file = "yarl-1.22.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f1e09112a2c31ffe8d80be1b0988fa6a18c5d5cad92a9ffbb1c04c91bfe52ad2"}, + {file = "yarl-1.22.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:939fe60db294c786f6b7c2d2e121576628468f65453d86b0fe36cb52f987bd74"}, + {file = "yarl-1.22.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e1651bf8e0398574646744c1885a41198eba53dc8a9312b954073f845c90a8df"}, + {file = "yarl-1.22.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b8a0588521a26bf92a57a1705b77b8b59044cdceccac7151bd8d229e66b8dedb"}, + {file = "yarl-1.22.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:42188e6a615c1a75bcaa6e150c3fe8f3e8680471a6b10150c5f7e83f47cc34d2"}, + {file = "yarl-1.22.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f6d2cb59377d99718913ad9a151030d6f83ef420a2b8f521d94609ecc106ee82"}, + {file = "yarl-1.22.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50678a3b71c751d58d7908edc96d332af328839eea883bb554a43f539101277a"}, + {file = "yarl-1.22.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1e8fbaa7cec507aa24ea27a01456e8dd4b6fab829059b69844bd348f2d467124"}, + {file = "yarl-1.22.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:433885ab5431bc3d3d4f2f9bd15bfa1614c522b0f1405d62c4f926ccd69d04fa"}, + {file = "yarl-1.22.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:b790b39c7e9a4192dc2e201a282109ed2985a1ddbd5ac08dc56d0e121400a8f7"}, + {file = "yarl-1.22.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:31f0b53913220599446872d757257be5898019c85e7971599065bc55065dc99d"}, + {file = "yarl-1.22.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a49370e8f711daec68d09b821a34e1167792ee2d24d405cbc2387be4f158b520"}, + {file = "yarl-1.22.0-cp312-cp312-win32.whl", hash = "sha256:70dfd4f241c04bd9239d53b17f11e6ab672b9f1420364af63e8531198e3f5fe8"}, + {file = "yarl-1.22.0-cp312-cp312-win_amd64.whl", hash = "sha256:8884d8b332a5e9b88e23f60bb166890009429391864c685e17bd73a9eda9105c"}, + {file = "yarl-1.22.0-cp312-cp312-win_arm64.whl", hash = "sha256:ea70f61a47f3cc93bdf8b2f368ed359ef02a01ca6393916bc8ff877427181e74"}, + {file = "yarl-1.22.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8dee9c25c74997f6a750cd317b8ca63545169c098faee42c84aa5e506c819b53"}, + {file = "yarl-1.22.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:01e73b85a5434f89fc4fe27dcda2aff08ddf35e4d47bbbea3bdcd25321af538a"}, + {file = "yarl-1.22.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:22965c2af250d20c873cdbee8ff958fb809940aeb2e74ba5f20aaf6b7ac8c70c"}, + {file = "yarl-1.22.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b4f15793aa49793ec8d1c708ab7f9eded1aa72edc5174cae703651555ed1b601"}, + {file = "yarl-1.22.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5542339dcf2747135c5c85f68680353d5cb9ffd741c0f2e8d832d054d41f35a"}, + {file = "yarl-1.22.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5c401e05ad47a75869c3ab3e35137f8468b846770587e70d71e11de797d113df"}, + {file = "yarl-1.22.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:243dda95d901c733f5b59214d28b0120893d91777cb8aa043e6ef059d3cddfe2"}, + {file = "yarl-1.22.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bec03d0d388060058f5d291a813f21c011041938a441c593374da6077fe21b1b"}, + {file = "yarl-1.22.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b0748275abb8c1e1e09301ee3cf90c8a99678a4e92e4373705f2a2570d581273"}, + {file = "yarl-1.22.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:47fdb18187e2a4e18fda2c25c05d8251a9e4a521edaed757fef033e7d8498d9a"}, + {file = "yarl-1.22.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c7044802eec4524fde550afc28edda0dd5784c4c45f0be151a2d3ba017daca7d"}, + {file = "yarl-1.22.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:139718f35149ff544caba20fce6e8a2f71f1e39b92c700d8438a0b1d2a631a02"}, + {file = "yarl-1.22.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e1b51bebd221006d3d2f95fbe124b22b247136647ae5dcc8c7acafba66e5ee67"}, + {file = "yarl-1.22.0-cp313-cp313-win32.whl", hash = "sha256:d3e32536234a95f513bd374e93d717cf6b2231a791758de6c509e3653f234c95"}, + {file = "yarl-1.22.0-cp313-cp313-win_amd64.whl", hash = "sha256:47743b82b76d89a1d20b83e60d5c20314cbd5ba2befc9cda8f28300c4a08ed4d"}, + {file = "yarl-1.22.0-cp313-cp313-win_arm64.whl", hash = "sha256:5d0fcda9608875f7d052eff120c7a5da474a6796fe4d83e152e0e4d42f6d1a9b"}, + {file = "yarl-1.22.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:719ae08b6972befcba4310e49edb1161a88cdd331e3a694b84466bd938a6ab10"}, + {file = "yarl-1.22.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:47d8a5c446df1c4db9d21b49619ffdba90e77c89ec6e283f453856c74b50b9e3"}, + {file = "yarl-1.22.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:cfebc0ac8333520d2d0423cbbe43ae43c8838862ddb898f5ca68565e395516e9"}, + {file = "yarl-1.22.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4398557cbf484207df000309235979c79c4356518fd5c99158c7d38203c4da4f"}, + {file = "yarl-1.22.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2ca6fd72a8cd803be290d42f2dec5cdcd5299eeb93c2d929bf060ad9efaf5de0"}, + {file = "yarl-1.22.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ca1f59c4e1ab6e72f0a23c13fca5430f889634166be85dbf1013683e49e3278e"}, + {file = "yarl-1.22.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6c5010a52015e7c70f86eb967db0f37f3c8bd503a695a49f8d45700144667708"}, + {file = "yarl-1.22.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d7672ecf7557476642c88497c2f8d8542f8e36596e928e9bcba0e42e1e7d71f"}, + {file = "yarl-1.22.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:3b7c88eeef021579d600e50363e0b6ee4f7f6f728cd3486b9d0f3ee7b946398d"}, + {file = "yarl-1.22.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:f4afb5c34f2c6fecdcc182dfcfc6af6cccf1aa923eed4d6a12e9d96904e1a0d8"}, + {file = "yarl-1.22.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:59c189e3e99a59cf8d83cbb31d4db02d66cda5a1a4374e8a012b51255341abf5"}, + {file = "yarl-1.22.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:5a3bf7f62a289fa90f1990422dc8dff5a458469ea71d1624585ec3a4c8d6960f"}, + {file = "yarl-1.22.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:de6b9a04c606978fdfe72666fa216ffcf2d1a9f6a381058d4378f8d7b1e5de62"}, + {file = "yarl-1.22.0-cp313-cp313t-win32.whl", hash = "sha256:1834bb90991cc2999f10f97f5f01317f99b143284766d197e43cd5b45eb18d03"}, + {file = "yarl-1.22.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ff86011bd159a9d2dfc89c34cfd8aff12875980e3bd6a39ff097887520e60249"}, + {file = "yarl-1.22.0-cp313-cp313t-win_arm64.whl", hash = "sha256:7861058d0582b847bc4e3a4a4c46828a410bca738673f35a29ba3ca5db0b473b"}, + {file = "yarl-1.22.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:34b36c2c57124530884d89d50ed2c1478697ad7473efd59cfd479945c95650e4"}, + {file = "yarl-1.22.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:0dd9a702591ca2e543631c2a017e4a547e38a5c0f29eece37d9097e04a7ac683"}, + {file = "yarl-1.22.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:594fcab1032e2d2cc3321bb2e51271e7cd2b516c7d9aee780ece81b07ff8244b"}, + {file = "yarl-1.22.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f3d7a87a78d46a2e3d5b72587ac14b4c16952dd0887dbb051451eceac774411e"}, + {file = "yarl-1.22.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:852863707010316c973162e703bddabec35e8757e67fcb8ad58829de1ebc8590"}, + {file = "yarl-1.22.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:131a085a53bfe839a477c0845acf21efc77457ba2bcf5899618136d64f3303a2"}, + {file = "yarl-1.22.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:078a8aefd263f4d4f923a9677b942b445a2be970ca24548a8102689a3a8ab8da"}, + {file = "yarl-1.22.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bca03b91c323036913993ff5c738d0842fc9c60c4648e5c8d98331526df89784"}, + {file = "yarl-1.22.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:68986a61557d37bb90d3051a45b91fa3d5c516d177dfc6dd6f2f436a07ff2b6b"}, + {file = "yarl-1.22.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:4792b262d585ff0dff6bcb787f8492e40698443ec982a3568c2096433660c694"}, + {file = "yarl-1.22.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:ebd4549b108d732dba1d4ace67614b9545b21ece30937a63a65dd34efa19732d"}, + {file = "yarl-1.22.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f87ac53513d22240c7d59203f25cc3beac1e574c6cd681bbfd321987b69f95fd"}, + {file = "yarl-1.22.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:22b029f2881599e2f1b06f8f1db2ee63bd309e2293ba2d566e008ba12778b8da"}, + {file = "yarl-1.22.0-cp314-cp314-win32.whl", hash = "sha256:6a635ea45ba4ea8238463b4f7d0e721bad669f80878b7bfd1f89266e2ae63da2"}, + {file = "yarl-1.22.0-cp314-cp314-win_amd64.whl", hash = "sha256:0d6e6885777af0f110b0e5d7e5dda8b704efed3894da26220b7f3d887b839a79"}, + {file = "yarl-1.22.0-cp314-cp314-win_arm64.whl", hash = "sha256:8218f4e98d3c10d683584cb40f0424f4b9fd6e95610232dd75e13743b070ee33"}, + {file = "yarl-1.22.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:45c2842ff0e0d1b35a6bf1cd6c690939dacb617a70827f715232b2e0494d55d1"}, + {file = "yarl-1.22.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:d947071e6ebcf2e2bee8fce76e10faca8f7a14808ca36a910263acaacef08eca"}, + {file = "yarl-1.22.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:334b8721303e61b00019474cc103bdac3d7b1f65e91f0bfedeec2d56dfe74b53"}, + {file = "yarl-1.22.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e7ce67c34138a058fd092f67d07a72b8e31ff0c9236e751957465a24b28910c"}, + {file = "yarl-1.22.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d77e1b2c6d04711478cb1c4ab90db07f1609ccf06a287d5607fcd90dc9863acf"}, + {file = "yarl-1.22.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4647674b6150d2cae088fc07de2738a84b8bcedebef29802cf0b0a82ab6face"}, + {file = "yarl-1.22.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:efb07073be061c8f79d03d04139a80ba33cbd390ca8f0297aae9cce6411e4c6b"}, + {file = "yarl-1.22.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e51ac5435758ba97ad69617e13233da53908beccc6cfcd6c34bbed8dcbede486"}, + {file = "yarl-1.22.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:33e32a0dd0c8205efa8e83d04fc9f19313772b78522d1bdc7d9aed706bfd6138"}, + {file = "yarl-1.22.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:bf4a21e58b9cde0e401e683ebd00f6ed30a06d14e93f7c8fd059f8b6e8f87b6a"}, + {file = "yarl-1.22.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:e4b582bab49ac33c8deb97e058cd67c2c50dac0dd134874106d9c774fd272529"}, + {file = "yarl-1.22.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:0b5bcc1a9c4839e7e30b7b30dd47fe5e7e44fb7054ec29b5bb8d526aa1041093"}, + {file = "yarl-1.22.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:c0232bce2170103ec23c454e54a57008a9a72b5d1c3105dc2496750da8cfa47c"}, + {file = "yarl-1.22.0-cp314-cp314t-win32.whl", hash = "sha256:8009b3173bcd637be650922ac455946197d858b3630b6d8787aa9e5c4564533e"}, + {file = "yarl-1.22.0-cp314-cp314t-win_amd64.whl", hash = "sha256:9fb17ea16e972c63d25d4a97f016d235c78dd2344820eb35bc034bc32012ee27"}, + {file = "yarl-1.22.0-cp314-cp314t-win_arm64.whl", hash = "sha256:9f6d73c1436b934e3f01df1e1b21ff765cd1d28c77dfb9ace207f746d4610ee1"}, + {file = "yarl-1.22.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3aa27acb6de7a23785d81557577491f6c38a5209a254d1191519d07d8fe51748"}, + {file = "yarl-1.22.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:af74f05666a5e531289cb1cc9c883d1de2088b8e5b4de48004e5ca8a830ac859"}, + {file = "yarl-1.22.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:62441e55958977b8167b2709c164c91a6363e25da322d87ae6dd9c6019ceecf9"}, + {file = "yarl-1.22.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b580e71cac3f8113d3135888770903eaf2f507e9421e5697d6ee6d8cd1c7f054"}, + {file = "yarl-1.22.0-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e81fda2fb4a07eda1a2252b216aa0df23ebcd4d584894e9612e80999a78fd95b"}, + {file = "yarl-1.22.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:99b6fc1d55782461b78221e95fc357b47ad98b041e8e20f47c1411d0aacddc60"}, + {file = "yarl-1.22.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:088e4e08f033db4be2ccd1f34cf29fe994772fb54cfe004bbf54db320af56890"}, + {file = "yarl-1.22.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e4e1f6f0b4da23e61188676e3ed027ef0baa833a2e633c29ff8530800edccba"}, + {file = "yarl-1.22.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:84fc3ec96fce86ce5aa305eb4aa9358279d1aa644b71fab7b8ed33fe3ba1a7ca"}, + {file = "yarl-1.22.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:5dbeefd6ca588b33576a01b0ad58aa934bc1b41ef89dee505bf2932b22ddffba"}, + {file = "yarl-1.22.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:14291620375b1060613f4aab9ebf21850058b6b1b438f386cc814813d901c60b"}, + {file = "yarl-1.22.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:a4fcfc8eb2c34148c118dfa02e6427ca278bfd0f3df7c5f99e33d2c0e81eae3e"}, + {file = "yarl-1.22.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:029866bde8d7b0878b9c160e72305bbf0a7342bcd20b9999381704ae03308dc8"}, + {file = "yarl-1.22.0-cp39-cp39-win32.whl", hash = "sha256:4dcc74149ccc8bba31ce1944acee24813e93cfdee2acda3c172df844948ddf7b"}, + {file = "yarl-1.22.0-cp39-cp39-win_amd64.whl", hash = "sha256:10619d9fdee46d20edc49d3479e2f8269d0779f1b031e6f7c2aa1c76be04b7ed"}, + {file = "yarl-1.22.0-cp39-cp39-win_arm64.whl", hash = "sha256:dd7afd3f8b0bfb4e0d9fc3c31bfe8a4ec7debe124cfd90619305def3c8ca8cd2"}, + {file = "yarl-1.22.0-py3-none-any.whl", hash = "sha256:1380560bdba02b6b6c90de54133c81c9f2a453dee9912fe58c1dcced1edb7cff"}, + {file = "yarl-1.22.0.tar.gz", hash = "sha256:bebf8557577d4401ba8bd9ff33906f1376c877aa78d1fe216ad01b4d6745af71"}, +] + +[package.dependencies] +idna = ">=2.0" +multidict = ">=4.0" +propcache = ">=0.2.1" + +[metadata] +lock-version = "2.1" +python-versions = ">=3.12,<4.0" +content-hash = "8704106c6a7f30ae16b47f1eae4616a750c23722e696f5e8ded7e48bd5d483d1" diff --git a/projects/dotnet_api/poetry.toml b/libs/chromium/poetry.toml similarity index 100% rename from projects/dotnet_api/poetry.toml rename to libs/chromium/poetry.toml diff --git a/libs/chromium/pyproject.toml b/libs/chromium/pyproject.toml new file mode 100644 index 0000000..afd65a5 --- /dev/null +++ b/libs/chromium/pyproject.toml @@ -0,0 +1,25 @@ +[tool.poetry] +name = "chromium" +version = "0.1.0" +description = "Modules Nemesis uses to handle Chromium files" +authors = ["SpecterOps"] +readme = "README.md" + +[tool.poetry.dependencies] +python = ">=3.12,<4.0" +impacket = ">=0.12.0,<0.13.0" +psycopg = {extras = ["binary"], version = ">=3.0.0,<4.0.0"} +dapr = "1.16.0" +structlog = ">=20.0.0,<30.0.0" +nemesis_dpapi = { path = "../nemesis_dpapi", develop = true } +file_linking = { path = "../file_linking", develop = true } +common = { path = "../../libs/common", develop = true } + +[tool.poetry.group.dev.dependencies] +ruff = "^0.9.2" +pytest = "^8.4.2" +pytest-asyncio = "^1.2.0" + +[build-system] +requires = ["poetry-core>=2.0.0,<3.0.0"] +build-backend = "poetry.core.masonry.api" diff --git a/libs/file_enrichment_modules/file_enrichment_modules/pe/rule.yara b/libs/chromium/tests/__init__.py similarity index 100% rename from libs/file_enrichment_modules/file_enrichment_modules/pe/rule.yara rename to libs/chromium/tests/__init__.py diff --git a/libs/chromium/tests/test_example.py b/libs/chromium/tests/test_example.py new file mode 100644 index 0000000..b497045 --- /dev/null +++ b/libs/chromium/tests/test_example.py @@ -0,0 +1,3 @@ +def test_example(): + """Simple example test to verify pytest is working.""" + assert True diff --git a/libs/common/.vscode/settings.json b/libs/common/.vscode/settings.json index 34bd581..61a8b1d 100644 --- a/libs/common/.vscode/settings.json +++ b/libs/common/.vscode/settings.json @@ -8,13 +8,12 @@ "[python]": { "editor.formatOnSave": true, "editor.codeActionsOnSave": { - "source.fixAll": "explicit", - "source.organizeImports": "explicit" + "source.fixAll.ruff": "explicit", + "source.organizeImports.ruff": "explicit" }, "editor.defaultFormatter": "charliermarsh.ruff" }, "autoDocstring.docstringFormat": "google", - "editor.formatOnSave": true, "files.exclude": { "**/.DS_Store": true, "**/.git": true, @@ -62,4 +61,5 @@ "python.testing.unittestEnabled": false, "python.testing.pytestEnabled": true, "python.analysis.typeCheckingMode": "basic", + "python.defaultInterpreterPath": "${workspaceFolder}/.venv/bin/python" } \ No newline at end of file diff --git a/libs/common/common/db.py b/libs/common/common/db.py new file mode 100644 index 0000000..df9a310 --- /dev/null +++ b/libs/common/common/db.py @@ -0,0 +1,47 @@ +from functools import lru_cache +from urllib.parse import quote_plus + +from dapr.clients import DaprClient + +_DAPR_SECRET_STORE_NAME = "nemesis-secret-store" + + +@lru_cache(maxsize=1) +def get_postgres_connection_str(dapr_client: DaprClient | None = None) -> str: + """Get PostgreSQL connection string from Dapr secrets by building it from individual parameters.""" + + def fetch_secrets(client: DaprClient) -> dict: + """Fetch all required PostgreSQL secrets.""" + secrets = {} + secret_keys = ["POSTGRES_USER", "POSTGRES_PASSWORD", "POSTGRES_HOST", "POSTGRES_PORT", "POSTGRES_DB", "POSTGRES_PARAMETERS"] + + for key in secret_keys: + try: + secret = client.get_secret(store_name=_DAPR_SECRET_STORE_NAME, key=key) + secrets[key] = secret.secret[key] + except Exception as e: + raise ValueError(f"Failed to fetch {key} from Dapr secret store: {e}") from e + + return secrets + + if dapr_client: + secrets = fetch_secrets(dapr_client) + else: + with DaprClient() as client: + secrets = fetch_secrets(client) + + # Build the connection string from individual parameters + # URL-encode user and password to handle special characters like @, :, /, etc. + user = quote_plus(secrets["POSTGRES_USER"]) + password = quote_plus(secrets["POSTGRES_PASSWORD"]) + host = secrets["POSTGRES_HOST"] + port = secrets["POSTGRES_PORT"] + db = secrets["POSTGRES_DB"] + parameters = secrets["POSTGRES_PARAMETERS"] + + output = f"postgresql://{user}:{password}@{host}:{port}/{db}?{parameters}" + + if not output.startswith("postgresql://"): + raise ValueError("Constructed POSTGRES connection string must start with 'postgresql://'") + + return output diff --git a/libs/common/common/dependency_checks.py b/libs/common/common/dependency_checks.py index 886e81b..be76aa7 100644 --- a/libs/common/common/dependency_checks.py +++ b/libs/common/common/dependency_checks.py @@ -3,7 +3,6 @@ import os import shutil from collections.abc import Sequence from pathlib import Path -from typing import Optional logger = logging.getLogger(__name__) logger.addHandler(logging.NullHandler()) @@ -22,7 +21,7 @@ def find_missing_path_dependencies( commands: Sequence[str], *, raise_error: bool = True, - search_paths: Optional[Sequence[Path]] = None, + search_paths: Sequence[Path] | None = None, ) -> list[str]: """Checks if specified commands are available in system PATH. diff --git a/libs/common/common/helpers.py b/libs/common/common/helpers.py index 5dc583b..d524511 100644 --- a/libs/common/common/helpers.py +++ b/libs/common/common/helpers.py @@ -1,10 +1,9 @@ import hashlib +import io +import posixpath import re import subprocess - -import structlog - -logger = structlog.get_logger(module=__name__) +from typing import BinaryIO def calculate_file_hash(file_path: str, hash_type: str) -> str: @@ -250,6 +249,100 @@ def sanitize_file_path(file_path: str, num_chars=4): return f"{sanitized_base}.{extension[0]}" if extension else sanitized_base +def get_file_extension(filepath): + # Get just the final filename component of the path + base_name = posixpath.basename(filepath) + + # Split on the last dot, but only if the dot isn't the first character + if base_name.startswith(".") or "." not in base_name: + return "" + + name_parts = base_name.split(".") + if len(name_parts) > 1: + return "." + name_parts[-1] + return "" + +def get_drive_from_path(path: str) -> str | None: + """ + Extract Windows drive letter from a file path. + + Supports two path formats: + 1. POSIX-style with leading slash: "/C:/Users/..." or "/D:/Data/..." + 2. Windows-style without leading slash: "C:/Users/..." or "D:/Data/..." + + Args: + path: File path string to parse + + Returns: + str | None: Drive letter with colon, or None if no valid drive found + - For POSIX-style paths: Returns with leading slash (e.g., "/C:", "/D:") + - For Windows-style paths: Returns without leading slash (e.g., "C:", "D:") + + Examples: + >>> get_drive_from_path("/C:/Users/john/file.txt") + '/C:' + >>> get_drive_from_path("C:/Users/john/file.txt") + 'C:' + >>> get_drive_from_path("/D:/Data/files") + '/D:' + >>> get_drive_from_path("invalid/path") + None + + Supported drive letters: A-Z (case-insensitive) + """ + parts = path.split("/") + + # Handle paths without leading slash (e.g., "C:/Users/...") + if len(parts) >= 1 and parts[0]: + drive_part = parts[0] + + # Validate drive part + if not drive_part: + return None + + # Must be exactly 2 characters: letter + colon + if len(drive_part) != 2: + return None + + # Second character must be ':' + if drive_part[1] != ":": + return None + + # First character must be a letter (A-Z or a-z) + if not drive_part[0].isalpha(): + return None + + # Return drive without trailing slash for paths without leading slash + return drive_part + + # For paths like "/C:/Users/...", parts[0] is empty and parts[1] contains the drive + if len(parts) >= 2: + drive_part = parts[1] + + # Validate drive part + if not drive_part: + return None + + # Must be exactly 2 characters: letter + colon + if len(drive_part) != 2: + return None + + # Second character must be ':' + if drive_part[1] != ":": + return None + + # First character must be a letter (A-Z or a-z) + if not drive_part[0].isalpha(): + return None + + # Return "/" + drive letter with colon + # e.g., "C:" -> "/C:" + return f"/{drive_part}" + + return None + + + def extract_all_strings(filename: str, min_len: int = 5): """ Returns a combined list of all single-byte ASCII strings @@ -274,6 +367,22 @@ def extract_all_strings(filename: str, min_len: int = 5): return all_strings +def create_text_reader(binary_file: BinaryIO) -> io.TextIOWrapper: + """Creates a text reader that handles BOMs and mixed content""" + + bom_check = binary_file.read(4) + binary_file.seek(0) # Reset to start + + if bom_check.startswith(b"\xff\xfe"): + return io.TextIOWrapper(binary_file, encoding="utf-16le") + elif bom_check.startswith(b"\xfe\xff"): + return io.TextIOWrapper(binary_file, encoding="utf-16be") + elif bom_check.startswith(b"\xef\xbb\xbf"): + return io.TextIOWrapper(binary_file, encoding="utf-8-sig") + else: + return io.TextIOWrapper(binary_file, encoding="utf-8", errors="replace") + + def escape_markdown(text): """ Escapes markdown control characters in text by adding backslashes. diff --git a/libs/common/common/logger.py b/libs/common/common/logger.py new file mode 100644 index 0000000..ca7286d --- /dev/null +++ b/libs/common/common/logger.py @@ -0,0 +1,67 @@ +import logging +import os +import sys + +import structlog +from structlog.stdlib import ProcessorFormatter + +LOG_LEVEL = os.getenv("LOG_LEVEL", "INFO").upper() +NUMERIC_LEVEL = getattr(logging, LOG_LEVEL, logging.INFO) + +WORKFLOW_RUNTIME_LOG_LEVEL = os.getenv("WORKFLOW_RUNTIME_LOG_LEVEL", "WARNING") +WORKFLOW_CLIENT_LOG_LEVEL = os.getenv("WORKFLOW_CLIENT_LOG_LEVEL", "WARNING") + + +def add_callsite_from_record(_logger: logging.Logger, _method_name: str, event_dict: dict) -> dict: + record = event_dict.get("_record") + if record is not None: + event_dict.setdefault("logger", record.name) + event_dict.setdefault("module", record.module) + event_dict.setdefault("func", record.funcName) + event_dict.setdefault("line", record.lineno) + return event_dict + + +foreign_pre_chain = [ + add_callsite_from_record, + structlog.stdlib.add_log_level, + structlog.processors.StackInfoRenderer(), + structlog.processors.format_exc_info, +] + +formatter = ProcessorFormatter( + processor=structlog.dev.ConsoleRenderer(colors=True), + foreign_pre_chain=foreign_pre_chain, +) + +handler = logging.StreamHandler(stream=sys.stdout) +handler.setFormatter(formatter) + +root = logging.getLogger() +root.handlers[:] = [handler] +root.setLevel("WARNING") +logging.captureWarnings(True) + +# structlog -> hand off to ProcessorFormatter (no direct rendering here) +structlog.configure( + processors=[ + structlog.stdlib.filter_by_level, + structlog.stdlib.add_logger_name, # adds "logger" for your own logs + structlog.stdlib.add_log_level, + structlog.stdlib.PositionalArgumentsFormatter(), + structlog.processors.TimeStamper(fmt="iso"), + structlog.processors.StackInfoRenderer(), + structlog.processors.format_exc_info, + structlog.processors.UnicodeDecoder(), + ProcessorFormatter.wrap_for_formatter, # defer final render + ], + context_class=dict, + logger_factory=structlog.stdlib.LoggerFactory(), + wrapper_class=structlog.stdlib.BoundLogger, + cache_logger_on_first_use=True, +) + + +def get_logger(name: str | None = None) -> structlog.stdlib.BoundLogger: + logging.getLogger(name).setLevel(LOG_LEVEL) + return structlog.get_logger(name) diff --git a/libs/common/common/models.py b/libs/common/common/models.py index a72716a..65343a4 100644 --- a/libs/common/common/models.py +++ b/libs/common/common/models.py @@ -1,12 +1,16 @@ # src/common/models.py from datetime import datetime from enum import Enum -from typing import Any, Generic, TypeVar +from typing import TYPE_CHECKING, Any -import structlog -from pydantic import BaseModel, Field +from pydantic import BaseModel, ConfigDict, Field, field_serializer, field_validator -logger = structlog.get_logger(module=__name__) +from .logger import get_logger + +if TYPE_CHECKING: + from .models2.api import FileMetadata + +logger = get_logger(__name__) ########################################## @@ -28,6 +32,7 @@ class FindingCategory(str, Enum): YARA_MATCH = "yara_match" PII = "pii" MISC = "misc" + INFORMATIONAL = "informational" class FindingOrigin(str, Enum): @@ -59,6 +64,59 @@ class Alert(BaseModel): service: str | None = None # service that sent the message (optional) +########################################## +# +# .NET +# +########################################## + + +class DotNetInput(BaseModel): + object_id: str + + +class DotNetMethodInfo(BaseModel): + MethodName: str + FilterLevel: str | None = None + + +class DotNetAssemblyAnalysis(BaseModel): + AssemblyName: str + Error: str | None = None + RemotingChannels: list[str] = [] + IsWCFServer: bool = False + IsWCFClient: bool = False + SerializationGadgetCalls: dict[str, list[DotNetMethodInfo]] = {} + WcfServerCalls: dict[str, list[DotNetMethodInfo]] = {} + ClientCalls: dict[str, list[DotNetMethodInfo]] = {} + RemotingCalls: dict[str, list[DotNetMethodInfo]] = {} + ExecutionCalls: dict[str, list[DotNetMethodInfo]] = {} + + +class DotNetOutput(BaseModel): + model_config = ConfigDict(populate_by_name=True) + + object_id: str = Field(alias="objectId") + decompilation: str | None = None + analysis: DotNetAssemblyAnalysis | None = None + + @field_validator("analysis", mode="before") + @classmethod + def parse_analysis_json(cls, v): + """Parse analysis from JSON string if needed""" + if v is None: + return None + if isinstance(v, str): + import json + + try: + return json.loads(v) + except Exception as e: + logger.warning(f"Failed to parse DotNet analysis JSON: {e}") + return None + return v + + ########################################## # # Special case for NoseyParker @@ -70,6 +128,14 @@ class NoseyParkerInput(BaseModel): object_id: str +class GitCommitInfo(BaseModel): + commit_id: str + author: str + author_email: str + commit_date: str + message: str + + class MatchLocation(BaseModel): line: int column: int @@ -81,6 +147,8 @@ class MatchInfo(BaseModel): matched_content: str location: MatchLocation snippet: str + file_path: str | None = None + git_commit: GitCommitInfo | None = None class ScanStats(BaseModel): @@ -90,31 +158,21 @@ class ScanStats(BaseModel): bytes_scanned: int matches_found: int - # Allow aliases for field names - class Config: - populate_by_name = True - extra = "ignore" # Ignore extra fields - class ScanResults(BaseModel): scan_duration_ms: int bytes_scanned: int - matches: list[MatchInfo] = [] # Default to empty list + matches: list[MatchInfo] stats: ScanStats - - class Config: - populate_by_name = True - extra = "ignore" # Ignore extra fields + scan_type: str = "regular" # "regular", "zip", "git_repo" class NoseyParkerOutput(BaseModel): + model_config = ConfigDict(populate_by_name=True, extra="ignore") + object_id: str scan_result: ScanResults - class Config: - populate_by_name = True - extra = "ignore" # Ignore extra fields - # Add a factory method to handle flexible parsing @classmethod def from_dict(cls, data: dict): @@ -137,32 +195,80 @@ class NoseyParkerOutput(BaseModel): bytes_scanned=data.get("scan_result", {}).get("stats", {}).get("bytes_scanned", 0), matches_found=data.get("scan_result", {}).get("stats", {}).get("matches_found", 0), ), + scan_type=data.get("scan_result", {}).get("scan_type", "regular"), ), ) +########################################## +# +# Bulk Enrichment +# +########################################## + + +class BulkEnrichmentEvent(BaseModel): + enrichment_name: str + object_id: str + + +class SingleEnrichmentWorkflowInput(BaseModel): + """Input model for single enrichment workflows (bulk operations).""" + + enrichment_name: str + object_id: str + + ########################################## # # Model for files submitted to the API # ########################################## class File(BaseModel): + model_config = ConfigDict( + exclude_none=True, + exclude_unset=True, + ) + object_id: str agent_id: str + source: str | None = None project: str timestamp: datetime expiration: datetime - path: str | None = None + path: str # | None = None originating_object_id: str | None = None + originating_container_id: str | None = None nesting_level: int | None = None creation_time: str | None = None access_time: str | None = None modification_time: str | None = None - class Config: - exclude_none = True - exclude_unset = True - json_encoders = {datetime: lambda dt: dt.isoformat()} + @field_serializer("timestamp", "expiration") + def serialize_datetime(self, dt: datetime, _info): + return dt.isoformat() + + @classmethod + def from_file_metadata(cls, metadata: "FileMetadata", object_id: str) -> "File": + """ + Create a File instance from FileMetadata and object_id. + + Args: + metadata: FileMetadata object containing upload metadata + object_id: The object ID of the uploaded file + + Returns: + File instance ready for submission + """ + return cls( + object_id=object_id, + agent_id=metadata.agent_id, + source=metadata.source, + project=metadata.project, + timestamp=metadata.timestamp, + expiration=metadata.expiration, + path=metadata.path, + ) ########################################## @@ -199,9 +305,6 @@ class FileEnriched(File): is_plaintext: bool is_container: bool - class Config: - json_encoders = {datetime: lambda dt: dt.isoformat()} - ########################################## # @@ -218,10 +321,7 @@ class WorkflowStatus(BaseModel): result: dict | None = None -T = TypeVar("T") - - -class CloudEvent(BaseModel, Generic[T]): +class CloudEvent[T](BaseModel): """Cloud event schema used in Dapr pub/sub""" data: T diff --git a/libs/common/common/models2/api.py b/libs/common/common/models2/api.py index df49f84..fcd57bd 100644 --- a/libs/common/common/models2/api.py +++ b/libs/common/common/models2/api.py @@ -1,9 +1,10 @@ import logging +import re from datetime import UTC, datetime -from typing import Annotated, Union +from typing import Annotated, Literal from uuid import UUID -from pydantic import BaseModel, BeforeValidator, Field +from pydantic import BaseModel, BeforeValidator, Field, field_serializer, field_validator logger = logging.getLogger(__name__) @@ -12,14 +13,6 @@ class ErrorResponse(BaseModel): detail: str = Field(..., description="Error message details") -class ValidationError(BaseModel): - """Model representing a validation error""" - - loc: list[Union[str, int]] - msg: str - type: str - - class FileWithMetadataResponse(BaseModel): """Response for combined file and metadata uploads""" @@ -67,28 +60,107 @@ def ensure_utc_datetime(value) -> datetime: UTCDatetime = Annotated[datetime, BeforeValidator(ensure_utc_datetime)] +class FileFilters(BaseModel): + """File filtering configuration for container extraction""" + + include: list[str] | None = Field( + default=None, description="Patterns for files to include. If empty/None, all files are included by default." + ) + exclude: list[str] | None = Field( + default=None, description="Patterns for files to exclude. Takes precedence over include patterns." + ) + pattern_type: Literal["glob", "regex"] = Field( + default="glob", + description="Type of patterns to use: 'glob' for shell-style wildcards, 'regex' for regular expressions", + ) + + @field_validator("include", "exclude") + @classmethod + def validate_patterns(cls, v, info): + """Validate that patterns can be compiled if regex type""" + if v is None: + return v + + # Access other field values through info.data + pattern_type = info.data.get("pattern_type", "glob") + + if pattern_type == "regex": + # Validate that all regex patterns compile + for pattern in v: + try: + re.compile(pattern, re.IGNORECASE) + except re.error as e: + raise ValueError(f"Invalid regex pattern '{pattern}': {e}") from e + + return v + + class FileMetadata(BaseModel): """Metadata model for file uploads""" agent_id: str + source: str | None = None project: str - timestamp: datetime = Field(description="ISO 8601 formatted timestamp") - expiration: datetime = Field(description="ISO 8601 formatted expiration date") + timestamp: datetime | None = Field( + default=None, description="ISO 8601 formatted timestamp of when the data was collected" + ) + expiration: datetime | None = Field( + default=None, description="ISO 8601 formatted expiration date (when the data should be deleted)" + ) path: str + file_filters: FileFilters | None = Field( + default=None, description="Optional file filtering configuration for container extraction" + ) model_config = { "json_schema_extra": { "example": { "agent_id": "beacon123", + "source": "host://192.168.1.1", "project": "assess-test", "timestamp": "2025-01-06T23:48:46.925656Z", "expiration": "2026-01-06T23:48:46.925656Z", "path": "/path/to/file", + "file_filters": { + "include": ["*.exe", "*/Users/**/*"], + "exclude": ["*/Windows/**/*", "*.tmp"], + "pattern_type": "glob", + }, } } } - model_config = {"json_encoders": {datetime: lambda dt: dt.isoformat()}} + @field_serializer("timestamp", "expiration", when_used="unless-none") + def serialize_datetime(self, dt: datetime, _info): + return dt.isoformat() + + +class ContainerFromMountRequest(BaseModel): + """Request model for processing container files from mounted folder""" + + filename: str = Field(description="Name of the container file in the mounted folder") + metadata: FileMetadata = Field(description="File metadata for processing") + + model_config = { + "json_schema_extra": { + "example": { + "filename": "large_archive.zip", + "metadata": { + "agent_id": "beacon123", + "source": "host://192.168.1.1", + "project": "assess-test", + "timestamp": "2025-01-06T23:48:46.925656Z", + "expiration": "2026-01-06T23:48:46.925656Z", + "path": "/mounted/large_archive.zip", + "file_filters": { + "include": ["*.exe", "*/Users/**/*"], + "exclude": ["*/Windows/**/*", "*.tmp"], + "pattern_type": "glob", + }, + }, + } + } + } #################### diff --git a/libs/common/common/models2/dpapi.py b/libs/common/common/models2/dpapi.py new file mode 100644 index 0000000..98518a6 --- /dev/null +++ b/libs/common/common/models2/dpapi.py @@ -0,0 +1,171 @@ +"""DPAPI credential models for API requests.""" + +import re +from typing import Annotated, Literal, Union +from uuid import UUID + +from common.logger import get_logger +from nemesis_dpapi.types import Sid +from pydantic import BaseModel, Discriminator, Field, Tag, field_serializer, field_validator + +logger = get_logger(__name__) + + +class PasswordCredentialKey(BaseModel): + """Use a plaintext password to derive a DPAPI credential key.""" + + model_config = {"frozen": True, "extra": "forbid"} + + type: Literal["password"] + value: str # Plain text password + user_sid: Sid # Required for to derive MK encryption keys + + +class NtlmHashCredentialKey(BaseModel): + """Use an NTLM hash to derive a DPAPI credential key.""" + + model_config = {"frozen": True, "extra": "forbid"} + + type: Literal["cred_key_ntlm"] + value: str # Hex string representation of NTLM hash (16 bytes) + user_sid: Sid # Required for to derive MK encryption keys + + @field_validator("value") + @classmethod + def validate_ntlm_hash_length(cls, v): + """Validate that value is exactly 32 hex characters (16 bytes).""" + if len(v) != 32: + raise ValueError(f"NTLM hash value must be exactly 32 hex characters (16 bytes), got {len(v)} characters") + if not re.match(r"^[0-9a-fA-F]+$", v): + raise ValueError("NTLM hash value must contain only hex characters (0-9, a-f, A-F)") + return v + + +class Sha1CredentialKey(BaseModel): + """Use a SHA1 hash to derive a DPAPI credential key.""" + + model_config = {"frozen": True, "extra": "forbid"} + + type: Literal["cred_key_sha1"] + value: str # Hex string representation of credential key (20 bytes) + user_sid: Sid # Required for to derive MK encryption keys + + @field_validator("value") + @classmethod + def validate_cred_key_length(cls, v): + """Validate that value is either 40 hex characters (20 bytes).""" + if len(v) != 40: + raise ValueError(f"SHA1 credential key value must be 40 hex characters (20 bytes), got {len(v)} characters") + if not re.match(r"^[0-9a-fA-F]+$", v): + raise ValueError("SHA1 Credential key value must contain only hex characters (0-9, a-f, A-F)") + return v + + +class Pbkdf2StrongCredentialKey(BaseModel): + """Credential key object.""" + + model_config = {"frozen": True, "extra": "forbid"} + + type: Literal["cred_key_pbkdf2"] + value: str # Hex string representation of credential key (16 bytes) + user_sid: Sid # Required to derive MK encryption keys + + @field_validator("value") + @classmethod + def validate_cred_key_length(cls, v): + """Validate that value is either 32 hex characters (16 bytes).""" + if len(v) != 32: + raise ValueError( + f"Secure credential key (PBKDF2) value must be exactly 32 hex characters (16 bytes), got {len(v)} characters" + ) + if not re.match(r"^[0-9a-fA-F]+$", v): + raise ValueError("Secure credential key (PBKDF2) value must contain only hex characters (0-9, a-f, A-F)") + return v + + +class DomainBackupKeyCredential(BaseModel): + """Domain backup key credential (PVK format).""" + + model_config = {"frozen": True, "extra": "forbid"} + + type: Literal["domain_backup_key"] + value: str # Base64 encoded PVK data + guid: str # Domain backup key GUID (UUID format) + domain_controller: str | None = None # Optional domain controller + + @field_validator("guid") + @classmethod + def validate_guid_format(cls, v): + """Validate that guid is a valid UUID format.""" + try: + UUID(v) # This will raise ValueError if not valid UUID format + return v + except ValueError as e: + raise ValueError(f"guid must be a valid UUID format, got: {v}") from e + + +class MasterKeyGuidPair(BaseModel): + """Strongly typed master key data.""" + + model_config = {"frozen": True, "extra": "forbid"} + + guid: UUID = Field(description="Master key GUID") + key_hex: str = Field(description="Hex-encoded master key bytes", pattern=r"^[0-9a-fA-F]+$") + + @field_serializer("guid") + def serialize_guid(self, value: UUID) -> str: + return str(value) + + +class MasterKeyGuidPairList(BaseModel): + """Decrypted master key/GUID pairs.""" + + model_config = {"frozen": True, "extra": "forbid"} + + type: Literal["master_key_guid_pair"] + value: list[MasterKeyGuidPair] + + +class DpapiSystemCredentialRequest(BaseModel): + """DPAPI_SYSTEM LSA Secret credential sent in an API request.""" + + model_config = {"frozen": True, "extra": "forbid"} + + type: Literal["dpapi_system"] + value: str = Field( + description="Hex-encoded DPAPI_SYSTEM LSA secret (40 bytes)", + pattern=r"^[0-9a-fA-F]{80}$", + ) + + @field_validator("value") + @classmethod + def validate_hex_length(cls, v): + """Validate that value is exactly 80 hex characters (40 bytes).""" + if len(v) != 80: + raise ValueError( + f"DPAPI_SYSTEM value must be exactly 80 hex characters (40 bytes), got {len(v)} characters" + ) + if not re.match(r"^[0-9a-fA-F]+$", v): + raise ValueError("DPAPI_SYSTEM value must contain only hex characters (0-9, a-f, A-F)") + return v + + +def get_credential_type(v): + """Discriminator function to determine credential type from 'type' field.""" + if isinstance(v, dict): + return v.get("type") + return getattr(v, "type", None) + + +type DpapiCredentialRequest = Annotated[ + Union[ + Annotated[PasswordCredentialKey, Tag("password")], + Annotated[NtlmHashCredentialKey, Tag("cred_key_ntlm")], + Annotated[Sha1CredentialKey, Tag("cred_key_sha1")], + Annotated[Pbkdf2StrongCredentialKey, Tag("cred_key_pbkdf2")], + Annotated[DomainBackupKeyCredential, Tag("domain_backup_key")], + Annotated[MasterKeyGuidPairList, Tag("master_key_guid_pair")], + Annotated[DpapiSystemCredentialRequest, Tag("dpapi_system")], + ], + Field(discriminator=Discriminator(get_credential_type)), +] diff --git a/libs/common/common/models2/enrichments.py b/libs/common/common/models2/enrichments.py new file mode 100644 index 0000000..03c2c04 --- /dev/null +++ b/libs/common/common/models2/enrichments.py @@ -0,0 +1,18 @@ +"""API models for the /enrichments route.""" + +from pydantic import BaseModel + + +class EnrichmentRequest(BaseModel): + object_id: str + + +class EnrichmentResponse(BaseModel): + status: str + message: str + object_id: str + instance_id: str + + +class ModulesListResponse(BaseModel): + modules: list[str] diff --git a/libs/common/common/state_helpers.py b/libs/common/common/state_helpers.py index 2da3ede..4eaba8f 100644 --- a/libs/common/common/state_helpers.py +++ b/libs/common/common/state_helpers.py @@ -1,72 +1,113 @@ import json +import asyncpg import psycopg -import structlog -from dapr.clients import DaprClient - +from common.db import get_postgres_connection_str from common.models import FileEnriched -logger = structlog.get_logger(module=__name__) +from .logger import get_logger + +logger = get_logger(__name__) + +# Single source of truth for file_enriched query (using psycopg style with %s placeholders) +_FILE_ENRICHED_SELECT_QUERY = """ + SELECT + object_id, agent_id, source, project, timestamp, expiration, + path, file_name, extension, size, magic_type, mime_type, + is_plaintext, is_container, originating_object_id, + nesting_level, file_creation_time, file_access_time, + file_modification_time, security_info, hashes + FROM files_enriched +""" + +_FILE_ENRICHED_SELECT_QUERY_PSYCHOPG = f""" +{_FILE_ENRICHED_SELECT_QUERY} +WHERE object_id = %s +""" + +_FILE_ENRICHED_SELECT_QUERY_ASYNCPG = f""" +{_FILE_ENRICHED_SELECT_QUERY} +WHERE object_id = $1 +""" -with DaprClient() as client: - secret = client.get_secret(store_name="nemesis-secret-store", key="POSTGRES_CONNECTION_STRING") - postgres_connection_string = secret.secret["POSTGRES_CONNECTION_STRING"] +def _transform_file_enriched_data(file_data: dict) -> dict: + """ + Transform raw database data into format suitable for FileEnriched model. + + Handles: + - UUID to string conversion + - Datetime to ISO format conversion + - JSON field parsing + - None value removal + + Args: + file_data: Dictionary of raw database values + + Returns: + Transformed dictionary ready for FileEnriched.model_validate() + """ + # Convert UUID to string + if "object_id" in file_data and file_data["object_id"]: + file_data["object_id"] = str(file_data["object_id"]) + if "originating_object_id" in file_data and file_data["originating_object_id"]: + file_data["originating_object_id"] = str(file_data["originating_object_id"]) + + # Convert datetime objects to ISO format strings + datetime_fields = [ + "timestamp", + "expiration", + "file_creation_time", + "file_access_time", + "file_modification_time", + ] + for field in datetime_fields: + if field in file_data and file_data[field]: + file_data[field] = file_data[field].isoformat() + + # Handle JSON fields (parse if string, keep as-is if dict) + if "security_info" in file_data and isinstance(file_data["security_info"], str): + file_data["security_info"] = json.loads(file_data["security_info"]) + if "hashes" in file_data and isinstance(file_data["hashes"], str): + file_data["hashes"] = json.loads(file_data["hashes"]) + + # Remove None values + file_data = {k: v for k, v in file_data.items() if v is not None} + + return file_data def get_file_enriched(object_id: str) -> FileEnriched: - """Retrieve a file_enriched record from PostgreSQL and parse it into a FileEnriched object.""" + """ + Retrieve a file_enriched record from PostgreSQL (synchronous). + + Args: + object_id: The object_id to query + + Returns: + FileEnriched model instance + + Raises: + ValueError: If no record found for object_id + Exception: For database or parsing errors + """ try: - with psycopg.connect(postgres_connection_string) as conn: + with psycopg.connect(get_postgres_connection_str()) as conn: with conn.cursor() as cur: - # Query remains the same - cur.execute( - """ - SELECT - object_id, agent_id, project, timestamp, expiration, path, - file_name, extension, size, magic_type, mime_type, - is_plaintext, is_container, originating_object_id, - nesting_level, file_creation_time, file_access_time, - file_modification_time, security_info, hashes - FROM files_enriched - WHERE object_id = %s - """, - (object_id,), - ) + cur.execute(_FILE_ENRICHED_SELECT_QUERY_PSYCHOPG, (object_id,)) result = cur.fetchone() if not result: raise ValueError(f"No file_enriched record found for object_id {object_id}") + if not cur.description: + raise RuntimeError("Query returned no column descriptions") + columns = [desc[0] for desc in cur.description] file_data = dict(zip(columns, result)) - # Convert UUID to string - if "object_id" in file_data and file_data["object_id"]: - file_data["object_id"] = str(file_data["object_id"]) - if "originating_object_id" in file_data and file_data["originating_object_id"]: - file_data["originating_object_id"] = str(file_data["originating_object_id"]) - - # Convert datetime objects to ISO format strings - datetime_fields = [ - "timestamp", - "expiration", - "file_creation_time", - "file_access_time", - "file_modification_time", - ] - for field in datetime_fields: - if field in file_data and file_data[field]: - file_data[field] = file_data[field].isoformat() - - # Handle JSON fields - if "security_info" in file_data and isinstance(file_data["security_info"], str): - file_data["security_info"] = json.loads(file_data["security_info"]) - if "hashes" in file_data and isinstance(file_data["hashes"], str): - file_data["hashes"] = json.loads(file_data["hashes"]) - - # Remove None values - file_data = {k: v for k, v in file_data.items() if v is not None} + # Transform data using shared helper + file_data = _transform_file_enriched_data(file_data) return FileEnriched.model_validate(file_data) @@ -76,3 +117,51 @@ def get_file_enriched(object_id: str) -> FileEnriched: except Exception as e: logger.exception(e, message="Error retrieving file_enriched from PostgreSQL") raise + + +async def get_file_enriched_async(object_id: str, connection: str | asyncpg.Pool | None = None) -> FileEnriched: + """ + Retrieve a file_enriched record from PostgreSQL (asynchronous using asyncpg). + + Args: + object_id: The object_id to query + connection: Optional connection string or asyncpg.Pool. If not provided, uses get_postgres_connection_str() + + Returns: + FileEnriched model instance + + Raises: + ValueError: If no record found for object_id + Exception: For database or parsing errors + """ + try: + # Determine if we're using a pool or creating a new connection + if isinstance(connection, asyncpg.Pool): + # Use the provided pool + row = await connection.fetchrow(_FILE_ENRICHED_SELECT_QUERY_ASYNCPG, object_id) + else: + # Use connection string (provided or default) + connection_string = connection if connection is not None else get_postgres_connection_str() + conn = await asyncpg.connect(connection_string) + try: + row = await conn.fetchrow(_FILE_ENRICHED_SELECT_QUERY_ASYNCPG, object_id) + finally: + await conn.close() + + if not row: + raise ValueError(f"No file_enriched record found for object_id {object_id}") + + # Convert asyncpg.Record to dict + file_data = dict(row) + + # Transform data using shared helper + file_data = _transform_file_enriched_data(file_data) + + return FileEnriched.model_validate(file_data) + + except ValueError as e: + logger.error(f"File not found: {str(e)}") + raise + except Exception as e: + logger.exception(e, message="Error retrieving file_enriched from PostgreSQL (async)") + raise diff --git a/libs/common/common/storage.py b/libs/common/common/storage.py index ef89bd2..bbcaf70 100644 --- a/libs/common/common/storage.py +++ b/libs/common/common/storage.py @@ -2,14 +2,15 @@ import tempfile import uuid from io import BytesIO -import structlog from dapr.clients import DaprClient from fastapi import UploadFile from minio import Minio from minio.error import S3Error from urllib3 import PoolManager, Retry -logger = structlog.get_logger(module=__name__) +from .logger import get_logger + +logger = get_logger(__name__) class StorageMinio: @@ -58,11 +59,12 @@ class StorageMinio: logger.exception(e, message="Failed to download file") raise finally: - logger.info("Downloaded file", file_uuid=file_uuid) + logger.debug("Downloaded file", file_uuid=file_uuid) return temp_file except Exception as e: logger.exception(e, file_uuid=file_uuid, bucket_name=self.bucket_name) + raise def download_bytes(self, file_uuid: str, offset: int = 0, length: int = 0) -> bytes: try: @@ -75,7 +77,7 @@ class StorageMinio: file_data = response.read() response.close() - logger.info("Successfully downloaded file", file_uuid=file_uuid) + logger.debug("Successfully downloaded file", file_uuid=file_uuid) return file_data except BaseException as e: @@ -115,7 +117,7 @@ class StorageMinio: yield chunk response.close() - logger.info("Successfully streamed file", file_uuid=file_uuid) + logger.debug("Successfully streamed file", file_uuid=file_uuid) except BaseException as e: logger.exception(e, message="Failed to stream file") @@ -131,10 +133,7 @@ class StorageMinio: return self.minio_client.stat_object(self.bucket_name, object_name) except Exception as e: logger.exception(e, "Error pulling object stats", object_name=object_name) - - def check_bucket_exists(self): - """Returns True if the bucket exists, false if it doesn't.""" - return self.minio_client.bucket_exists(self.bucket_name) + raise def check_file_exists(self, object_name): """Check if a file exists.""" @@ -149,41 +148,10 @@ class StorageMinio: # For other errors, raise the exception raise - def ensure_bucket_exists(self): - """Checks if the bucket exists and creates it w/ the LifecycleConfig if not.""" - try: - if not self.check_bucket_exists(): - logger.info("Creating Minio bucket", bucket=self.bucket_name) - self.minio_client.make_bucket(f"{self.bucket_name}") - - # # since this is the only place that creates the bucket, we can set - # # the auto-expiration policy here - # # # NOTE: this is now handled by the Housekeeping service - - # config = LifecycleConfig( - # [ - # Rule( - # ENABLED, - # rule_filter=Filter(prefix=""), - # rule_id=f"expire-{self.storage_expiration_days}-days", - # expiration=Expiration(days=self.storage_expiration_days), - # ), - # ], - # ) - # logger.info( - # f"Setting Minio bucket files to expire in {self.storage_expiration_days} days", - # bucket=self.bucket_name, - # ) - # self.minio_client.set_bucket_lifecycle(self.bucket_name, config) - except Exception as e: - logger.exception(e, bucket_name=self.bucket_name) - def upload_uploadfile(self, file: UploadFile) -> uuid.UUID: # uploads an UploadFile post directly from FastAPI try: - self.ensure_bucket_exists() - - logger.info(f"Uploading UploadFile {file.filename} to storage") + logger.debug(f"Uploading UploadFile {file.filename} to storage") # Get file size using SpooledTemporaryFile instead of async read file_size = 0 @@ -204,16 +172,16 @@ class StorageMinio: file.file, # Use file.file directly length=file_size, # Provide the calculated size ) - logger.debug("Object upload compelted", file_name=file.filename) + logger.debug("Object upload completed", file_name=file.filename) return file_uuid except Exception as e: logger.exception(e, bucket_name=self.bucket_name) + raise def upload_file(self, file_path: str) -> uuid.UUID: try: - self.ensure_bucket_exists() logger.debug("Uploading file to storage", file_path=file_path) file_uuid = f"{uuid.uuid4()}" self.minio_client.fput_object( @@ -224,10 +192,10 @@ class StorageMinio: return file_uuid except Exception as e: logger.exception(e, file_path=file_path, bucket_name=self.bucket_name) + raise def upload(self, data: bytes) -> uuid.UUID: try: - self.ensure_bucket_exists() logger.debug(f"Uploading {len(data)} bytes to storage") file_uuid = f"{uuid.uuid4()}" self.minio_client.put_object( @@ -239,6 +207,7 @@ class StorageMinio: return file_uuid except Exception as e: logger.exception(e, bucket_name=self.bucket_name) + raise def delete_object(self, object_id: str) -> bool: """Delete a single object from Minio storage. @@ -281,7 +250,7 @@ class StorageMinio: upload creates everything correctly. """ - logger.info("Deleting all files from bucket", bucket_name=self.bucket_name) + logger.debug("Deleting all files from bucket", bucket_name=self.bucket_name) try: files = self.minio_client.list_objects(self.bucket_name, recursive=True) diff --git a/libs/common/common/workflows/setup.py b/libs/common/common/workflows/setup.py new file mode 100644 index 0000000..d376c6b --- /dev/null +++ b/libs/common/common/workflows/setup.py @@ -0,0 +1,63 @@ +import asyncio +from collections.abc import Callable +from functools import wraps +from typing import Any + +import dapr.ext.workflow as wf +from common.logger import WORKFLOW_RUNTIME_LOG_LEVEL +from dapr.ext.workflow.logger.options import LoggerOptions + +wf_runtime: wf.WorkflowRuntime = wf.WorkflowRuntime( + logger_options=LoggerOptions( + log_level=WORKFLOW_RUNTIME_LOG_LEVEL, + ) +) + + +def set_fastapi_loop(loop: asyncio.AbstractEventLoop) -> None: + global fastapi_loop + fastapi_loop = loop + + +def workflow_activity(fn: Callable | None = None, *, name: str | None = None) -> Callable: + """ + Decorator to mark an async function as a workflow activity. + The default @wf_runtime.activity decorator does not support async functions. + + Can be used with or without parentheses: + @workflow_activity + async def my_activity(): ... + + @workflow_activity() + async def my_activity(): ... + + @workflow_activity(name="custom_name") + async def my_activity(): ... + """ + + def decorator(func: Callable) -> Callable: + # To facilitate unit-testing, avoid using the @wf_runtime.activity decorator + # and simply return the function as is. + # if settings.pytest_running: + # return func + + @wf_runtime.activity(name=name) + @wraps(func) + def wrapped_fn(*args, **kwargs) -> Any: # type: ignore + result = func(*args, **kwargs) + if not asyncio.iscoroutine(result): + # If the result is not a coroutine, just return it as is. + return result + + if fastapi_loop is None: + raise RuntimeError("FastAPI event loop is not set.") + return asyncio.run_coroutine_threadsafe(result, fastapi_loop).result() + + return wrapped_fn + + # If called without parentheses, fn will be the function + if fn is not None: + return decorator(fn) + + # If called with parentheses (with or without arguments), return the decorator + return decorator diff --git a/libs/common/poetry.lock b/libs/common/poetry.lock index c98a75b..498c8ef 100644 --- a/libs/common/poetry.lock +++ b/libs/common/poetry.lock @@ -14,103 +14,137 @@ files = [ [[package]] name = "aiohttp" -version = "3.12.13" +version = "3.13.0" description = "Async http client/server framework (asyncio)" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "aiohttp-3.12.13-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5421af8f22a98f640261ee48aae3a37f0c41371e99412d55eaf2f8a46d5dad29"}, - {file = "aiohttp-3.12.13-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0fcda86f6cb318ba36ed8f1396a6a4a3fd8f856f84d426584392083d10da4de0"}, - {file = "aiohttp-3.12.13-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4cd71c9fb92aceb5a23c4c39d8ecc80389c178eba9feab77f19274843eb9412d"}, - {file = "aiohttp-3.12.13-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34ebf1aca12845066c963016655dac897651e1544f22a34c9b461ac3b4b1d3aa"}, - {file = "aiohttp-3.12.13-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:893a4639694c5b7edd4bdd8141be296042b6806e27cc1d794e585c43010cc294"}, - {file = "aiohttp-3.12.13-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:663d8ee3ffb3494502ebcccb49078faddbb84c1d870f9c1dd5a29e85d1f747ce"}, - {file = "aiohttp-3.12.13-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f0f8f6a85a0006ae2709aa4ce05749ba2cdcb4b43d6c21a16c8517c16593aabe"}, - {file = "aiohttp-3.12.13-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1582745eb63df267c92d8b61ca655a0ce62105ef62542c00a74590f306be8cb5"}, - {file = "aiohttp-3.12.13-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d59227776ee2aa64226f7e086638baa645f4b044f2947dbf85c76ab11dcba073"}, - {file = "aiohttp-3.12.13-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:06b07c418bde1c8e737d8fa67741072bd3f5b0fb66cf8c0655172188c17e5fa6"}, - {file = "aiohttp-3.12.13-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:9445c1842680efac0f81d272fd8db7163acfcc2b1436e3f420f4c9a9c5a50795"}, - {file = "aiohttp-3.12.13-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:09c4767af0b0b98c724f5d47f2bf33395c8986995b0a9dab0575ca81a554a8c0"}, - {file = "aiohttp-3.12.13-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f3854fbde7a465318ad8d3fc5bef8f059e6d0a87e71a0d3360bb56c0bf87b18a"}, - {file = "aiohttp-3.12.13-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2332b4c361c05ecd381edb99e2a33733f3db906739a83a483974b3df70a51b40"}, - {file = "aiohttp-3.12.13-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:1561db63fa1b658cd94325d303933553ea7d89ae09ff21cc3bcd41b8521fbbb6"}, - {file = "aiohttp-3.12.13-cp310-cp310-win32.whl", hash = "sha256:a0be857f0b35177ba09d7c472825d1b711d11c6d0e8a2052804e3b93166de1ad"}, - {file = "aiohttp-3.12.13-cp310-cp310-win_amd64.whl", hash = "sha256:fcc30ad4fb5cb41a33953292d45f54ef4066746d625992aeac33b8c681173178"}, - {file = "aiohttp-3.12.13-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7c229b1437aa2576b99384e4be668af1db84b31a45305d02f61f5497cfa6f60c"}, - {file = "aiohttp-3.12.13-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:04076d8c63471e51e3689c93940775dc3d12d855c0c80d18ac5a1c68f0904358"}, - {file = "aiohttp-3.12.13-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:55683615813ce3601640cfaa1041174dc956d28ba0511c8cbd75273eb0587014"}, - {file = "aiohttp-3.12.13-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:921bc91e602d7506d37643e77819cb0b840d4ebb5f8d6408423af3d3bf79a7b7"}, - {file = "aiohttp-3.12.13-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e72d17fe0974ddeae8ed86db297e23dba39c7ac36d84acdbb53df2e18505a013"}, - {file = "aiohttp-3.12.13-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0653d15587909a52e024a261943cf1c5bdc69acb71f411b0dd5966d065a51a47"}, - {file = "aiohttp-3.12.13-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a77b48997c66722c65e157c06c74332cdf9c7ad00494b85ec43f324e5c5a9b9a"}, - {file = "aiohttp-3.12.13-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d6946bae55fd36cfb8e4092c921075cde029c71c7cb571d72f1079d1e4e013bc"}, - {file = "aiohttp-3.12.13-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f95db8c8b219bcf294a53742c7bda49b80ceb9d577c8e7aa075612b7f39ffb7"}, - {file = "aiohttp-3.12.13-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:03d5eb3cfb4949ab4c74822fb3326cd9655c2b9fe22e4257e2100d44215b2e2b"}, - {file = "aiohttp-3.12.13-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:6383dd0ffa15515283c26cbf41ac8e6705aab54b4cbb77bdb8935a713a89bee9"}, - {file = "aiohttp-3.12.13-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:6548a411bc8219b45ba2577716493aa63b12803d1e5dc70508c539d0db8dbf5a"}, - {file = "aiohttp-3.12.13-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:81b0fcbfe59a4ca41dc8f635c2a4a71e63f75168cc91026c61be665945739e2d"}, - {file = "aiohttp-3.12.13-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:6a83797a0174e7995e5edce9dcecc517c642eb43bc3cba296d4512edf346eee2"}, - {file = "aiohttp-3.12.13-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a5734d8469a5633a4e9ffdf9983ff7cdb512524645c7a3d4bc8a3de45b935ac3"}, - {file = "aiohttp-3.12.13-cp311-cp311-win32.whl", hash = "sha256:fef8d50dfa482925bb6b4c208b40d8e9fa54cecba923dc65b825a72eed9a5dbd"}, - {file = "aiohttp-3.12.13-cp311-cp311-win_amd64.whl", hash = "sha256:9a27da9c3b5ed9d04c36ad2df65b38a96a37e9cfba6f1381b842d05d98e6afe9"}, - {file = "aiohttp-3.12.13-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0aa580cf80558557285b49452151b9c69f2fa3ad94c5c9e76e684719a8791b73"}, - {file = "aiohttp-3.12.13-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b103a7e414b57e6939cc4dece8e282cfb22043efd0c7298044f6594cf83ab347"}, - {file = "aiohttp-3.12.13-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:78f64e748e9e741d2eccff9597d09fb3cd962210e5b5716047cbb646dc8fe06f"}, - {file = "aiohttp-3.12.13-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29c955989bf4c696d2ededc6b0ccb85a73623ae6e112439398935362bacfaaf6"}, - {file = "aiohttp-3.12.13-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d640191016763fab76072c87d8854a19e8e65d7a6fcfcbf017926bdbbb30a7e5"}, - {file = "aiohttp-3.12.13-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4dc507481266b410dede95dd9f26c8d6f5a14315372cc48a6e43eac652237d9b"}, - {file = "aiohttp-3.12.13-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8a94daa873465d518db073bd95d75f14302e0208a08e8c942b2f3f1c07288a75"}, - {file = "aiohttp-3.12.13-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:177f52420cde4ce0bb9425a375d95577fe082cb5721ecb61da3049b55189e4e6"}, - {file = "aiohttp-3.12.13-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0f7df1f620ec40f1a7fbcb99ea17d7326ea6996715e78f71a1c9a021e31b96b8"}, - {file = "aiohttp-3.12.13-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3062d4ad53b36e17796dce1c0d6da0ad27a015c321e663657ba1cc7659cfc710"}, - {file = "aiohttp-3.12.13-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:8605e22d2a86b8e51ffb5253d9045ea73683d92d47c0b1438e11a359bdb94462"}, - {file = "aiohttp-3.12.13-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:54fbbe6beafc2820de71ece2198458a711e224e116efefa01b7969f3e2b3ddae"}, - {file = "aiohttp-3.12.13-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:050bd277dfc3768b606fd4eae79dd58ceda67d8b0b3c565656a89ae34525d15e"}, - {file = "aiohttp-3.12.13-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:2637a60910b58f50f22379b6797466c3aa6ae28a6ab6404e09175ce4955b4e6a"}, - {file = "aiohttp-3.12.13-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e986067357550d1aaa21cfe9897fa19e680110551518a5a7cf44e6c5638cb8b5"}, - {file = "aiohttp-3.12.13-cp312-cp312-win32.whl", hash = "sha256:ac941a80aeea2aaae2875c9500861a3ba356f9ff17b9cb2dbfb5cbf91baaf5bf"}, - {file = "aiohttp-3.12.13-cp312-cp312-win_amd64.whl", hash = "sha256:671f41e6146a749b6c81cb7fd07f5a8356d46febdaaaf07b0e774ff04830461e"}, - {file = "aiohttp-3.12.13-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d4a18e61f271127465bdb0e8ff36e8f02ac4a32a80d8927aa52371e93cd87938"}, - {file = "aiohttp-3.12.13-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:532542cb48691179455fab429cdb0d558b5e5290b033b87478f2aa6af5d20ace"}, - {file = "aiohttp-3.12.13-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d7eea18b52f23c050ae9db5d01f3d264ab08f09e7356d6f68e3f3ac2de9dfabb"}, - {file = "aiohttp-3.12.13-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad7c8e5c25f2a26842a7c239de3f7b6bfb92304593ef997c04ac49fb703ff4d7"}, - {file = "aiohttp-3.12.13-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6af355b483e3fe9d7336d84539fef460120c2f6e50e06c658fe2907c69262d6b"}, - {file = "aiohttp-3.12.13-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a95cf9f097498f35c88e3609f55bb47b28a5ef67f6888f4390b3d73e2bac6177"}, - {file = "aiohttp-3.12.13-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b8ed8c38a1c584fe99a475a8f60eefc0b682ea413a84c6ce769bb19a7ff1c5ef"}, - {file = "aiohttp-3.12.13-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a0b9170d5d800126b5bc89d3053a2363406d6e327afb6afaeda2d19ee8bb103"}, - {file = "aiohttp-3.12.13-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:372feeace612ef8eb41f05ae014a92121a512bd5067db8f25101dd88a8db11da"}, - {file = "aiohttp-3.12.13-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a946d3702f7965d81f7af7ea8fb03bb33fe53d311df48a46eeca17e9e0beed2d"}, - {file = "aiohttp-3.12.13-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:a0c4725fae86555bbb1d4082129e21de7264f4ab14baf735278c974785cd2041"}, - {file = "aiohttp-3.12.13-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:9b28ea2f708234f0a5c44eb6c7d9eb63a148ce3252ba0140d050b091b6e842d1"}, - {file = "aiohttp-3.12.13-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d4f5becd2a5791829f79608c6f3dc745388162376f310eb9c142c985f9441cc1"}, - {file = "aiohttp-3.12.13-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:60f2ce6b944e97649051d5f5cc0f439360690b73909230e107fd45a359d3e911"}, - {file = "aiohttp-3.12.13-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:69fc1909857401b67bf599c793f2183fbc4804717388b0b888f27f9929aa41f3"}, - {file = "aiohttp-3.12.13-cp313-cp313-win32.whl", hash = "sha256:7d7e68787a2046b0e44ba5587aa723ce05d711e3a3665b6b7545328ac8e3c0dd"}, - {file = "aiohttp-3.12.13-cp313-cp313-win_amd64.whl", hash = "sha256:5a178390ca90419bfd41419a809688c368e63c86bd725e1186dd97f6b89c2706"}, - {file = "aiohttp-3.12.13-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:36f6c973e003dc9b0bb4e8492a643641ea8ef0e97ff7aaa5c0f53d68839357b4"}, - {file = "aiohttp-3.12.13-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6cbfc73179bd67c229eb171e2e3745d2afd5c711ccd1e40a68b90427f282eab1"}, - {file = "aiohttp-3.12.13-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1e8b27b2d414f7e3205aa23bb4a692e935ef877e3a71f40d1884f6e04fd7fa74"}, - {file = "aiohttp-3.12.13-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eabded0c2b2ef56243289112c48556c395d70150ce4220d9008e6b4b3dd15690"}, - {file = "aiohttp-3.12.13-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:003038e83f1a3ff97409999995ec02fe3008a1d675478949643281141f54751d"}, - {file = "aiohttp-3.12.13-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1b6f46613031dbc92bdcaad9c4c22c7209236ec501f9c0c5f5f0b6a689bf50f3"}, - {file = "aiohttp-3.12.13-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c332c6bb04650d59fb94ed96491f43812549a3ba6e7a16a218e612f99f04145e"}, - {file = "aiohttp-3.12.13-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3fea41a2c931fb582cb15dc86a3037329e7b941df52b487a9f8b5aa960153cbd"}, - {file = "aiohttp-3.12.13-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:846104f45d18fb390efd9b422b27d8f3cf8853f1218c537f36e71a385758c896"}, - {file = "aiohttp-3.12.13-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5d6c85ac7dd350f8da2520bac8205ce99df4435b399fa7f4dc4a70407073e390"}, - {file = "aiohttp-3.12.13-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:5a1ecce0ed281bec7da8550da052a6b89552db14d0a0a45554156f085a912f48"}, - {file = "aiohttp-3.12.13-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:5304d74867028cca8f64f1cc1215eb365388033c5a691ea7aa6b0dc47412f495"}, - {file = "aiohttp-3.12.13-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:64d1f24ee95a2d1e094a4cd7a9b7d34d08db1bbcb8aa9fb717046b0a884ac294"}, - {file = "aiohttp-3.12.13-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:119c79922a7001ca6a9e253228eb39b793ea994fd2eccb79481c64b5f9d2a055"}, - {file = "aiohttp-3.12.13-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:bb18f00396d22e2f10cd8825d671d9f9a3ba968d708a559c02a627536b36d91c"}, - {file = "aiohttp-3.12.13-cp39-cp39-win32.whl", hash = "sha256:0022de47ef63fd06b065d430ac79c6b0bd24cdae7feaf0e8c6bac23b805a23a8"}, - {file = "aiohttp-3.12.13-cp39-cp39-win_amd64.whl", hash = "sha256:29e08111ccf81b2734ae03f1ad1cb03b9615e7d8f616764f22f71209c094f122"}, - {file = "aiohttp-3.12.13.tar.gz", hash = "sha256:47e2da578528264a12e4e3dd8dd72a7289e5f812758fe086473fab037a10fcce"}, + {file = "aiohttp-3.13.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ca69ec38adf5cadcc21d0b25e2144f6a25b7db7bea7e730bac25075bc305eff0"}, + {file = "aiohttp-3.13.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:240f99f88a9a6beb53ebadac79a2e3417247aa756202ed234b1dbae13d248092"}, + {file = "aiohttp-3.13.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a4676b978a9711531e7cea499d4cdc0794c617a1c0579310ab46c9fdf5877702"}, + {file = "aiohttp-3.13.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:48fcdd5bc771cbbab8ccc9588b8b6447f6a30f9fe00898b1a5107098e00d6793"}, + {file = "aiohttp-3.13.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:eeea0cdd2f687e210c8f605f322d7b0300ba55145014a5dbe98bd4be6fff1f6c"}, + {file = "aiohttp-3.13.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:10b3f01d5aeb632adaaf39c5e93f040a550464a768d54c514050c635adcbb9d0"}, + {file = "aiohttp-3.13.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a4dc0b83e25267f42ef065ea57653de4365b56d7bc4e4cfc94fabe56998f8ee6"}, + {file = "aiohttp-3.13.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:72714919ed9b90f030f761c20670e529c4af96c31bd000917dd0c9afd1afb731"}, + {file = "aiohttp-3.13.0-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:564be41e85318403fdb176e9e5b3e852d528392f42f2c1d1efcbeeed481126d7"}, + {file = "aiohttp-3.13.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:84912962071087286333f70569362e10793f73f45c48854e6859df11001eb2d3"}, + {file = "aiohttp-3.13.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:90b570f1a146181c3d6ae8f755de66227ded49d30d050479b5ae07710f7894c5"}, + {file = "aiohttp-3.13.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:2d71ca30257ce756e37a6078b1dff2d9475fee13609ad831eac9a6531bea903b"}, + {file = "aiohttp-3.13.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:cd45eb70eca63f41bb156b7dffbe1a7760153b69892d923bdb79a74099e2ed90"}, + {file = "aiohttp-3.13.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:5ae3a19949a27982c7425a7a5a963c1268fdbabf0be15ab59448cbcf0f992519"}, + {file = "aiohttp-3.13.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ea6df292013c9f050cbf3f93eee9953d6e5acd9e64a0bf4ca16404bfd7aa9bcc"}, + {file = "aiohttp-3.13.0-cp310-cp310-win32.whl", hash = "sha256:3b64f22fbb6dcd5663de5ef2d847a5638646ef99112503e6f7704bdecb0d1c4d"}, + {file = "aiohttp-3.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:f8d877aa60d80715b2afc565f0f1aea66565824c229a2d065b31670e09fed6d7"}, + {file = "aiohttp-3.13.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:99eb94e97a42367fef5fc11e28cb2362809d3e70837f6e60557816c7106e2e20"}, + {file = "aiohttp-3.13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4696665b2713021c6eba3e2b882a86013763b442577fe5d2056a42111e732eca"}, + {file = "aiohttp-3.13.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3e6a38366f7f0d0f6ed7a1198055150c52fda552b107dad4785c0852ad7685d1"}, + {file = "aiohttp-3.13.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aab715b1a0c37f7f11f9f1f579c6fbaa51ef569e47e3c0a4644fba46077a9409"}, + {file = "aiohttp-3.13.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7972c82bed87d7bd8e374b60a6b6e816d75ba4f7c2627c2d14eed216e62738e1"}, + {file = "aiohttp-3.13.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ca8313cb852af788c78d5afdea24c40172cbfff8b35e58b407467732fde20390"}, + {file = "aiohttp-3.13.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6c333a2385d2a6298265f4b3e960590f787311b87f6b5e6e21bb8375914ef504"}, + {file = "aiohttp-3.13.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cc6d5fc5edbfb8041d9607f6a417997fa4d02de78284d386bea7ab767b5ea4f3"}, + {file = "aiohttp-3.13.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7ddedba3d0043349edc79df3dc2da49c72b06d59a45a42c1c8d987e6b8d175b8"}, + {file = "aiohttp-3.13.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:23ca762140159417a6bbc959ca1927f6949711851e56f2181ddfe8d63512b5ad"}, + {file = "aiohttp-3.13.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:bfe824d6707a5dc3c5676685f624bc0c63c40d79dc0239a7fd6c034b98c25ebe"}, + {file = "aiohttp-3.13.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:3c11fa5dd2ef773a8a5a6daa40243d83b450915992eab021789498dc87acc114"}, + {file = "aiohttp-3.13.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:00fdfe370cffede3163ba9d3f190b32c0cfc8c774f6f67395683d7b0e48cdb8a"}, + {file = "aiohttp-3.13.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:6475e42ef92717a678bfbf50885a682bb360a6f9c8819fb1a388d98198fdcb80"}, + {file = "aiohttp-3.13.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:77da5305a410910218b99f2a963092f4277d8a9c1f429c1ff1b026d1826bd0b6"}, + {file = "aiohttp-3.13.0-cp311-cp311-win32.whl", hash = "sha256:2f9d9ea547618d907f2ee6670c9a951f059c5994e4b6de8dcf7d9747b420c820"}, + {file = "aiohttp-3.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:0f19f7798996d4458c669bd770504f710014926e9970f4729cf55853ae200469"}, + {file = "aiohttp-3.13.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1c272a9a18a5ecc48a7101882230046b83023bb2a662050ecb9bfcb28d9ab53a"}, + {file = "aiohttp-3.13.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:97891a23d7fd4e1afe9c2f4473e04595e4acb18e4733b910b6577b74e7e21985"}, + {file = "aiohttp-3.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:475bd56492ce5f4cffe32b5533c6533ee0c406d1d0e6924879f83adcf51da0ae"}, + {file = "aiohttp-3.13.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c32ada0abb4bc94c30be2b681c42f058ab104d048da6f0148280a51ce98add8c"}, + {file = "aiohttp-3.13.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:4af1f8877ca46ecdd0bc0d4a6b66d4b2bddc84a79e2e8366bc0d5308e76bceb8"}, + {file = "aiohttp-3.13.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e04ab827ec4f775817736b20cdc8350f40327f9b598dec4e18c9ffdcbea88a93"}, + {file = "aiohttp-3.13.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a6d9487b9471ec36b0faedf52228cd732e89be0a2bbd649af890b5e2ce422353"}, + {file = "aiohttp-3.13.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e66c57416352f36bf98f6641ddadd47c93740a22af7150d3e9a1ef6e983f9a8"}, + {file = "aiohttp-3.13.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:469167d5372f5bb3aedff4fc53035d593884fff2617a75317740e885acd48b04"}, + {file = "aiohttp-3.13.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a9f3546b503975a69b547c9fd1582cad10ede1ce6f3e313a2f547c73a3d7814f"}, + {file = "aiohttp-3.13.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:6b4174fcec98601f0cfdf308ee29a6ae53c55f14359e848dab4e94009112ee7d"}, + {file = "aiohttp-3.13.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a533873a7a4ec2270fb362ee5a0d3b98752e4e1dc9042b257cd54545a96bd8ed"}, + {file = "aiohttp-3.13.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:ce887c5e54411d607ee0959cac15bb31d506d86a9bcaddf0b7e9d63325a7a802"}, + {file = "aiohttp-3.13.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:d871f6a30d43e32fc9252dc7b9febe1a042b3ff3908aa83868d7cf7c9579a59b"}, + {file = "aiohttp-3.13.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:222c828243b4789d79a706a876910f656fad4381661691220ba57b2ab4547865"}, + {file = "aiohttp-3.13.0-cp312-cp312-win32.whl", hash = "sha256:682d2e434ff2f1108314ff7f056ce44e457f12dbed0249b24e106e385cf154b9"}, + {file = "aiohttp-3.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:0a2be20eb23888df130214b91c262a90e2de1553d6fb7de9e9010cec994c0ff2"}, + {file = "aiohttp-3.13.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:00243e51f16f6ec0fb021659d4af92f675f3cf9f9b39efd142aa3ad641d8d1e6"}, + {file = "aiohttp-3.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:059978d2fddc462e9211362cbc8446747ecd930537fa559d3d25c256f032ff54"}, + {file = "aiohttp-3.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:564b36512a7da3b386143c611867e3f7cfb249300a1bf60889bd9985da67ab77"}, + {file = "aiohttp-3.13.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4aa995b9156ae499393d949a456a7ab0b994a8241a96db73a3b73c7a090eff6a"}, + {file = "aiohttp-3.13.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:55ca0e95a3905f62f00900255ed807c580775174252999286f283e646d675a49"}, + {file = "aiohttp-3.13.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:49ce7525853a981fc35d380aa2353536a01a9ec1b30979ea4e35966316cace7e"}, + {file = "aiohttp-3.13.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2117be9883501eaf95503bd313eb4c7a23d567edd44014ba15835a1e9ec6d852"}, + {file = "aiohttp-3.13.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d169c47e40c911f728439da853b6fd06da83761012e6e76f11cb62cddae7282b"}, + {file = "aiohttp-3.13.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:703ad3f742fc81e543638a7bebddd35acadaa0004a5e00535e795f4b6f2c25ca"}, + {file = "aiohttp-3.13.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5bf635c3476f4119b940cc8d94ad454cbe0c377e61b4527f0192aabeac1e9370"}, + {file = "aiohttp-3.13.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:cfe6285ef99e7ee51cef20609be2bc1dd0e8446462b71c9db8bb296ba632810a"}, + {file = "aiohttp-3.13.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:34d8af6391c5f2e69749d7f037b614b8c5c42093c251f336bdbfa4b03c57d6c4"}, + {file = "aiohttp-3.13.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:12f5d820fadc5848d4559ea838aef733cf37ed2a1103bba148ac2f5547c14c29"}, + {file = "aiohttp-3.13.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:0f1338b61ea66f4757a0544ed8a02ccbf60e38d9cfb3225888888dd4475ebb96"}, + {file = "aiohttp-3.13.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:582770f82513419512da096e8df21ca44f86a2e56e25dc93c5ab4df0fe065bf0"}, + {file = "aiohttp-3.13.0-cp313-cp313-win32.whl", hash = "sha256:3194b8cab8dbc882f37c13ef1262e0a3d62064fa97533d3aa124771f7bf1ecee"}, + {file = "aiohttp-3.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:7897298b3eedc790257fef8a6ec582ca04e9dbe568ba4a9a890913b925b8ea21"}, + {file = "aiohttp-3.13.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:c417f8c2e1137775569297c584a8a7144e5d1237789eae56af4faf1894a0b861"}, + {file = "aiohttp-3.13.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:f84b53326abf8e56ebc28a35cebf4a0f396a13a76300f500ab11fe0573bf0b52"}, + {file = "aiohttp-3.13.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:990a53b9d6a30b2878789e490758e568b12b4a7fb2527d0c89deb9650b0e5813"}, + {file = "aiohttp-3.13.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c811612711e01b901e18964b3e5dec0d35525150f5f3f85d0aee2935f059910a"}, + {file = "aiohttp-3.13.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:ee433e594d7948e760b5c2a78cc06ac219df33b0848793cf9513d486a9f90a52"}, + {file = "aiohttp-3.13.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:19bb08e56f57c215e9572cd65cb6f8097804412c54081d933997ddde3e5ac579"}, + {file = "aiohttp-3.13.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f27b7488144eb5dd9151cf839b195edd1569629d90ace4c5b6b18e4e75d1e63a"}, + {file = "aiohttp-3.13.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d812838c109757a11354a161c95708ae4199c4fd4d82b90959b20914c1d097f6"}, + {file = "aiohttp-3.13.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7c20db99da682f9180fa5195c90b80b159632fb611e8dbccdd99ba0be0970620"}, + {file = "aiohttp-3.13.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:cf8b0870047900eb1f17f453b4b3953b8ffbf203ef56c2f346780ff930a4d430"}, + {file = "aiohttp-3.13.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:5b8a5557d5af3f4e3add52a58c4cf2b8e6e59fc56b261768866f5337872d596d"}, + {file = "aiohttp-3.13.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:052bcdd80c1c54b8a18a9ea0cd5e36f473dc8e38d51b804cea34841f677a9971"}, + {file = "aiohttp-3.13.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:76484ba17b2832776581b7ab466d094e48eba74cb65a60aea20154dae485e8bd"}, + {file = "aiohttp-3.13.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:62d8a0adcdaf62ee56bfb37737153251ac8e4b27845b3ca065862fb01d99e247"}, + {file = "aiohttp-3.13.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5004d727499ecb95f7c9147dd0bfc5b5670f71d355f0bd26d7af2d3af8e07d2f"}, + {file = "aiohttp-3.13.0-cp314-cp314-win32.whl", hash = "sha256:a1c20c26af48aea984f63f96e5d7af7567c32cb527e33b60a0ef0a6313cf8b03"}, + {file = "aiohttp-3.13.0-cp314-cp314-win_amd64.whl", hash = "sha256:56f7d230ec66e799fbfd8350e9544f8a45a4353f1cf40c1fea74c1780f555b8f"}, + {file = "aiohttp-3.13.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:2fd35177dc483ae702f07b86c782f4f4b100a8ce4e7c5778cea016979023d9fd"}, + {file = "aiohttp-3.13.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:4df1984c8804ed336089e88ac81a9417b1fd0db7c6f867c50a9264488797e778"}, + {file = "aiohttp-3.13.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:e68c0076052dd911a81d3acc4ef2911cc4ef65bf7cadbfbc8ae762da24da858f"}, + {file = "aiohttp-3.13.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bc95c49853cd29613e4fe4ff96d73068ff89b89d61e53988442e127e8da8e7ba"}, + {file = "aiohttp-3.13.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3b3bdc89413117b40cc39baae08fd09cbdeb839d421c4e7dce6a34f6b54b3ac1"}, + {file = "aiohttp-3.13.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3e77a729df23be2116acc4e9de2767d8e92445fbca68886dd991dc912f473755"}, + {file = "aiohttp-3.13.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e88ab34826d6eeb6c67e6e92400b9ec653faf5092a35f07465f44c9f1c429f82"}, + {file = "aiohttp-3.13.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:019dbef24fe28ce2301419dd63a2b97250d9760ca63ee2976c2da2e3f182f82e"}, + {file = "aiohttp-3.13.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:2c4aeaedd20771b7b4bcdf0ae791904445df6d856c02fc51d809d12d17cffdc7"}, + {file = "aiohttp-3.13.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:b3a8e6a2058a0240cfde542b641d0e78b594311bc1a710cbcb2e1841417d5cb3"}, + {file = "aiohttp-3.13.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:f8e38d55ca36c15f36d814ea414ecb2401d860de177c49f84a327a25b3ee752b"}, + {file = "aiohttp-3.13.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:a921edbe971aade1bf45bcbb3494e30ba6863a5c78f28be992c42de980fd9108"}, + {file = "aiohttp-3.13.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:474cade59a447cb4019c0dce9f0434bf835fb558ea932f62c686fe07fe6db6a1"}, + {file = "aiohttp-3.13.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:99a303ad960747c33b65b1cb65d01a62ac73fa39b72f08a2e1efa832529b01ed"}, + {file = "aiohttp-3.13.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:bb34001fc1f05f6b323e02c278090c07a47645caae3aa77ed7ed8a3ce6abcce9"}, + {file = "aiohttp-3.13.0-cp314-cp314t-win32.whl", hash = "sha256:dea698b64235d053def7d2f08af9302a69fcd760d1c7bd9988fd5d3b6157e657"}, + {file = "aiohttp-3.13.0-cp314-cp314t-win_amd64.whl", hash = "sha256:1f164699a060c0b3616459d13c1464a981fddf36f892f0a5027cbd45121fb14b"}, + {file = "aiohttp-3.13.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:fcc425fb6fd2a00c6d91c85d084c6b75a61bc8bc12159d08e17c5711df6c5ba4"}, + {file = "aiohttp-3.13.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7c2c4c9ce834801651f81d6760d0a51035b8b239f58f298de25162fcf6f8bb64"}, + {file = "aiohttp-3.13.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f91e8f9053a07177868e813656ec57599cd2a63238844393cd01bd69c2e40147"}, + {file = "aiohttp-3.13.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:df46d9a3d78ec19b495b1107bf26e4fcf97c900279901f4f4819ac5bb2a02a4c"}, + {file = "aiohttp-3.13.0-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3b1eb9871cbe43b6ca6fac3544682971539d8a1d229e6babe43446279679609d"}, + {file = "aiohttp-3.13.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:62a3cddf8d9a2eae1f79585fa81d32e13d0c509bb9e7ad47d33c83b45a944df7"}, + {file = "aiohttp-3.13.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0f735e680c323ee7e9ef8e2ea26425c7dbc2ede0086fa83ce9d7ccab8a089f26"}, + {file = "aiohttp-3.13.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6a51839f778b0e283b43cd82bb17f1835ee2cc1bf1101765e90ae886e53e751c"}, + {file = "aiohttp-3.13.0-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ac90cfab65bc281d6752f22db5fa90419e33220af4b4fa53b51f5948f414c0e7"}, + {file = "aiohttp-3.13.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:62fd54f3e6f17976962ba67f911d62723c760a69d54f5d7b74c3ceb1a4e9ef8d"}, + {file = "aiohttp-3.13.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:cf2b60b65df05b6b2fa0d887f2189991a0dbf44a0dd18359001dc8fcdb7f1163"}, + {file = "aiohttp-3.13.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:1ccedfe280e804d9a9d7fe8b8c4309d28e364b77f40309c86596baa754af50b1"}, + {file = "aiohttp-3.13.0-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:ea01ffbe23df53ece0c8732d1585b3d6079bb8c9ee14f3745daf000051415a31"}, + {file = "aiohttp-3.13.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:19ba8625fa69523627b67f7e9901b587a4952470f68814d79cdc5bc460e9b885"}, + {file = "aiohttp-3.13.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:4b14bfae90598d331b5061fd15a7c290ea0c15b34aeb1cf620464bb5ec02a602"}, + {file = "aiohttp-3.13.0-cp39-cp39-win32.whl", hash = "sha256:cf7a4b976da219e726d0043fc94ae8169c0dba1d3a059b3c1e2c964bafc5a77d"}, + {file = "aiohttp-3.13.0-cp39-cp39-win_amd64.whl", hash = "sha256:6b9697d15231aeaed4786f090c9c8bc3ab5f0e0a6da1e76c135a310def271020"}, + {file = "aiohttp-3.13.0.tar.gz", hash = "sha256:378dbc57dd8cf341ce243f13fa1fa5394d68e2e02c15cd5f28eae35a70ec7f67"}, ] [package.dependencies] aiohappyeyeballs = ">=2.5.0" -aiosignal = ">=1.1.2" +aiosignal = ">=1.4.0" attrs = ">=17.3.0" frozenlist = ">=1.1.1" multidict = ">=4.5,<7.0" @@ -118,22 +152,23 @@ propcache = ">=0.2.0" yarl = ">=1.17.0,<2.0" [package.extras] -speedups = ["Brotli", "aiodns (>=3.3.0)", "brotlicffi"] +speedups = ["Brotli", "aiodns (>=3.3.0)", "brotlicffi", "zstandard"] [[package]] name = "aiosignal" -version = "1.3.2" +version = "1.4.0" description = "aiosignal: a list of registered asynchronous callbacks" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "aiosignal-1.3.2-py2.py3-none-any.whl", hash = "sha256:45cde58e409a301715980c2b01d0c28bdde3770d8290b5eb2173759d9acb31a5"}, - {file = "aiosignal-1.3.2.tar.gz", hash = "sha256:a8c255c66fafb1e499c9351d0bf32ff2d8a0321595ebac3b93713656d2436f54"}, + {file = "aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e"}, + {file = "aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7"}, ] [package.dependencies] frozenlist = ">=1.1.0" +typing-extensions = {version = ">=4.2", markers = "python_version < \"3.13\""} [[package]] name = "annotated-types" @@ -149,14 +184,14 @@ files = [ [[package]] name = "anyio" -version = "4.8.0" -description = "High level compatibility layer for multiple asynchronous event loop implementations" +version = "4.11.0" +description = "High-level concurrency and networking framework on top of asyncio or Trio" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "anyio-4.8.0-py3-none-any.whl", hash = "sha256:b5011f270ab5eb0abf13385f851315585cc37ef330dd88e27ec3d34d651fd47a"}, - {file = "anyio-4.8.0.tar.gz", hash = "sha256:1d9fe889df5212298c0c0723fa20479d1b94883a2df44bd3897aa91083316f7a"}, + {file = "anyio-4.11.0-py3-none-any.whl", hash = "sha256:0287e96f4d26d4149305414d4e3bc32f0dcd0862365a4bddea19d7a1ec38c4fc"}, + {file = "anyio-4.11.0.tar.gz", hash = "sha256:82a8d0b81e318cc5ce71a5f1f8b5c4e63619620b63141ef8c995fa0db95a57c4"}, ] [package.dependencies] @@ -165,191 +200,285 @@ sniffio = ">=1.1" typing_extensions = {version = ">=4.5", markers = "python_version < \"3.13\""} [package.extras] -doc = ["Sphinx (>=7.4,<8.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx_rtd_theme"] -test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "trustme", "truststore (>=0.9.1)", "uvloop (>=0.21)"] -trio = ["trio (>=0.26.1)"] +trio = ["trio (>=0.31.0)"] [[package]] name = "argon2-cffi" -version = "23.1.0" +version = "25.1.0" description = "Argon2 for Python" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" groups = ["main"] files = [ - {file = "argon2_cffi-23.1.0-py3-none-any.whl", hash = "sha256:c670642b78ba29641818ab2e68bd4e6a78ba53b7eff7b4c3815ae16abf91c7ea"}, - {file = "argon2_cffi-23.1.0.tar.gz", hash = "sha256:879c3e79a2729ce768ebb7d36d4609e3a78a4ca2ec3a9f12286ca057e3d0db08"}, + {file = "argon2_cffi-25.1.0-py3-none-any.whl", hash = "sha256:fdc8b074db390fccb6eb4a3604ae7231f219aa669a2652e0f20e16ba513d5741"}, + {file = "argon2_cffi-25.1.0.tar.gz", hash = "sha256:694ae5cc8a42f4c4e2bf2ca0e64e51e23a040c6a517a85074683d3959e1346c1"}, ] [package.dependencies] argon2-cffi-bindings = "*" -[package.extras] -dev = ["argon2-cffi[tests,typing]", "tox (>4)"] -docs = ["furo", "myst-parser", "sphinx", "sphinx-copybutton", "sphinx-notfound-page"] -tests = ["hypothesis", "pytest"] -typing = ["mypy"] - [[package]] name = "argon2-cffi-bindings" -version = "21.2.0" +version = "25.1.0" description = "Low-level CFFI bindings for Argon2" optional = false -python-versions = ">=3.6" +python-versions = ">=3.9" groups = ["main"] files = [ - {file = "argon2-cffi-bindings-21.2.0.tar.gz", hash = "sha256:bb89ceffa6c791807d1305ceb77dbfacc5aa499891d2c55661c6459651fc39e3"}, - {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ccb949252cb2ab3a08c02024acb77cfb179492d5701c7cbdbfd776124d4d2367"}, - {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9524464572e12979364b7d600abf96181d3541da11e23ddf565a32e70bd4dc0d"}, - {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b746dba803a79238e925d9046a63aa26bf86ab2a2fe74ce6b009a1c3f5c8f2ae"}, - {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:58ed19212051f49a523abb1dbe954337dc82d947fb6e5a0da60f7c8471a8476c"}, - {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:bd46088725ef7f58b5a1ef7ca06647ebaf0eb4baff7d1d0d177c6cc8744abd86"}, - {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_i686.whl", hash = "sha256:8cd69c07dd875537a824deec19f978e0f2078fdda07fd5c42ac29668dda5f40f"}, - {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:f1152ac548bd5b8bcecfb0b0371f082037e47128653df2e8ba6e914d384f3c3e"}, - {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-win32.whl", hash = "sha256:603ca0aba86b1349b147cab91ae970c63118a0f30444d4bc80355937c950c082"}, - {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-win_amd64.whl", hash = "sha256:b2ef1c30440dbbcba7a5dc3e319408b59676e2e039e2ae11a8775ecf482b192f"}, - {file = "argon2_cffi_bindings-21.2.0-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:e415e3f62c8d124ee16018e491a009937f8cf7ebf5eb430ffc5de21b900dad93"}, - {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3e385d1c39c520c08b53d63300c3ecc28622f076f4c2b0e6d7e796e9f6502194"}, - {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c3e3cc67fdb7d82c4718f19b4e7a87123caf8a93fde7e23cf66ac0337d3cb3f"}, - {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6a22ad9800121b71099d0fb0a65323810a15f2e292f2ba450810a7316e128ee5"}, - {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f9f8b450ed0547e3d473fdc8612083fd08dd2120d6ac8f73828df9b7d45bb351"}, - {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:93f9bf70084f97245ba10ee36575f0c3f1e7d7724d67d8e5b08e61787c320ed7"}, - {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3b9ef65804859d335dc6b31582cad2c5166f0c3e7975f324d9ffaa34ee7e6583"}, - {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4966ef5848d820776f5f562a7d45fdd70c2f330c961d0d745b784034bd9f48d"}, - {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20ef543a89dee4db46a1a6e206cd015360e5a75822f76df533845c3cbaf72670"}, - {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed2937d286e2ad0cc79a7087d3c272832865f779430e0cc2b4f3718d3159b0cb"}, - {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:5e00316dabdaea0b2dd82d141cc66889ced0cdcbfa599e8b471cf22c620c329a"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:3d3f05610594151994ca9ccb3c771115bdb4daef161976a266f0dd8aa9996b8f"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:8b8efee945193e667a396cbc7b4fb7d357297d6234d30a489905d96caabde56b"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:3c6702abc36bf3ccba3f802b799505def420a1b7039862014a65db3205967f5a"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a1c70058c6ab1e352304ac7e3b52554daadacd8d453c1752e547c76e9c99ac44"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e2fd3bfbff3c5d74fef31a722f729bf93500910db650c925c2d6ef879a7e51cb"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c4f9665de60b1b0e99bcd6be4f17d90339698ce954cfd8d9cf4f91c995165a92"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ba92837e4a9aa6a508c8d2d7883ed5a8f6c308c89a4790e1e447a220deb79a85"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-win32.whl", hash = "sha256:84a461d4d84ae1295871329b346a97f68eade8c53b6ed9a7ca2d7467f3c8ff6f"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b55aec3565b65f56455eebc9b9f34130440404f27fe21c3b375bf1ea4d8fbae6"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:87c33a52407e4c41f3b70a9c2d3f6056d88b10dad7695be708c5021673f55623"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:aecba1723ae35330a008418a91ea6cfcedf6d31e5fbaa056a166462ff066d500"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:2630b6240b495dfab90aebe159ff784d08ea999aa4b0d17efa734055a07d2f44"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:7aef0c91e2c0fbca6fc68e7555aa60ef7008a739cbe045541e438373bc54d2b0"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e021e87faa76ae0d413b619fe2b65ab9a037f24c60a1e6cc43457ae20de6dc6"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d3e924cfc503018a714f94a49a149fdc0b644eaead5d1f089330399134fa028a"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:c87b72589133f0346a1cb8d5ecca4b933e3c9b64656c9d175270a000e73b288d"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1db89609c06afa1a214a69a462ea741cf735b29a57530478c06eb81dd403de99"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-win32.whl", hash = "sha256:473bcb5f82924b1becbb637b63303ec8d10e84c8d241119419897a26116515d2"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-win_amd64.whl", hash = "sha256:a98cd7d17e9f7ce244c0803cad3c23a7d379c301ba618a5fa76a67d116618b98"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-win_arm64.whl", hash = "sha256:b0fdbcf513833809c882823f98dc2f931cf659d9a1429616ac3adebb49f5db94"}, + {file = "argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:6dca33a9859abf613e22733131fc9194091c1fa7cb3e131c143056b4856aa47e"}, + {file = "argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:21378b40e1b8d1655dd5310c84a40fc19a9aa5e6366e835ceb8576bf0fea716d"}, + {file = "argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d588dec224e2a83edbdc785a5e6f3c6cd736f46bfd4b441bbb5aa1f5085e584"}, + {file = "argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5acb4e41090d53f17ca1110c3427f0a130f944b896fc8c83973219c97f57b690"}, + {file = "argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:da0c79c23a63723aa5d782250fbf51b768abca630285262fb5144ba5ae01e520"}, + {file = "argon2_cffi_bindings-25.1.0.tar.gz", hash = "sha256:b957f3e6ea4d55d820e40ff76f450952807013d361a65d7f28acc0acbf29229d"}, ] [package.dependencies] -cffi = ">=1.0.1" - -[package.extras] -dev = ["cogapp", "pre-commit", "pytest", "wheel"] -tests = ["pytest"] +cffi = [ + {version = ">=1.0.1", markers = "python_version < \"3.14\""}, + {version = ">=2.0.0b1", markers = "python_version >= \"3.14\""}, +] [[package]] -name = "attrs" -version = "24.3.0" -description = "Classes Without Boilerplate" +name = "asyncio" +version = "4.0.0" +description = "Deprecated backport of asyncio; use the stdlib package instead" optional = false -python-versions = ">=3.8" +python-versions = ">=3.4" groups = ["main"] files = [ - {file = "attrs-24.3.0-py3-none-any.whl", hash = "sha256:ac96cd038792094f438ad1f6ff80837353805ac950cd2aa0e0625ef19850c308"}, - {file = "attrs-24.3.0.tar.gz", hash = "sha256:8f5c07333d543103541ba7be0e2ce16eeee8130cb0b3f9238ab904ce1e85baff"}, + {file = "asyncio-4.0.0-py3-none-any.whl", hash = "sha256:c1eddb0659231837046809e68103969b2bef8b0400d59cfa6363f6b5ed8cc88b"}, + {file = "asyncio-4.0.0.tar.gz", hash = "sha256:570cd9e50db83bc1629152d4d0b7558d6451bb1bfd5dfc2e935d96fc2f40329b"}, +] + +[[package]] +name = "asyncpg" +version = "0.30.0" +description = "An asyncio PostgreSQL driver" +optional = false +python-versions = ">=3.8.0" +groups = ["main"] +files = [ + {file = "asyncpg-0.30.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bfb4dd5ae0699bad2b233672c8fc5ccbd9ad24b89afded02341786887e37927e"}, + {file = "asyncpg-0.30.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dc1f62c792752a49f88b7e6f774c26077091b44caceb1983509edc18a2222ec0"}, + {file = "asyncpg-0.30.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3152fef2e265c9c24eec4ee3d22b4f4d2703d30614b0b6753e9ed4115c8a146f"}, + {file = "asyncpg-0.30.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c7255812ac85099a0e1ffb81b10dc477b9973345793776b128a23e60148dd1af"}, + {file = "asyncpg-0.30.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:578445f09f45d1ad7abddbff2a3c7f7c291738fdae0abffbeb737d3fc3ab8b75"}, + {file = "asyncpg-0.30.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c42f6bb65a277ce4d93f3fba46b91a265631c8df7250592dd4f11f8b0152150f"}, + {file = "asyncpg-0.30.0-cp310-cp310-win32.whl", hash = "sha256:aa403147d3e07a267ada2ae34dfc9324e67ccc4cdca35261c8c22792ba2b10cf"}, + {file = "asyncpg-0.30.0-cp310-cp310-win_amd64.whl", hash = "sha256:fb622c94db4e13137c4c7f98834185049cc50ee01d8f657ef898b6407c7b9c50"}, + {file = "asyncpg-0.30.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5e0511ad3dec5f6b4f7a9e063591d407eee66b88c14e2ea636f187da1dcfff6a"}, + {file = "asyncpg-0.30.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:915aeb9f79316b43c3207363af12d0e6fd10776641a7de8a01212afd95bdf0ed"}, + {file = "asyncpg-0.30.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c198a00cce9506fcd0bf219a799f38ac7a237745e1d27f0e1f66d3707c84a5a"}, + {file = "asyncpg-0.30.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3326e6d7381799e9735ca2ec9fd7be4d5fef5dcbc3cb555d8a463d8460607956"}, + {file = "asyncpg-0.30.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:51da377487e249e35bd0859661f6ee2b81db11ad1f4fc036194bc9cb2ead5056"}, + {file = "asyncpg-0.30.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bc6d84136f9c4d24d358f3b02be4b6ba358abd09f80737d1ac7c444f36108454"}, + {file = "asyncpg-0.30.0-cp311-cp311-win32.whl", hash = "sha256:574156480df14f64c2d76450a3f3aaaf26105869cad3865041156b38459e935d"}, + {file = "asyncpg-0.30.0-cp311-cp311-win_amd64.whl", hash = "sha256:3356637f0bd830407b5597317b3cb3571387ae52ddc3bca6233682be88bbbc1f"}, + {file = "asyncpg-0.30.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c902a60b52e506d38d7e80e0dd5399f657220f24635fee368117b8b5fce1142e"}, + {file = "asyncpg-0.30.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:aca1548e43bbb9f0f627a04666fedaca23db0a31a84136ad1f868cb15deb6e3a"}, + {file = "asyncpg-0.30.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c2a2ef565400234a633da0eafdce27e843836256d40705d83ab7ec42074efb3"}, + {file = "asyncpg-0.30.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1292b84ee06ac8a2ad8e51c7475aa309245874b61333d97411aab835c4a2f737"}, + {file = "asyncpg-0.30.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0f5712350388d0cd0615caec629ad53c81e506b1abaaf8d14c93f54b35e3595a"}, + {file = "asyncpg-0.30.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:db9891e2d76e6f425746c5d2da01921e9a16b5a71a1c905b13f30e12a257c4af"}, + {file = "asyncpg-0.30.0-cp312-cp312-win32.whl", hash = "sha256:68d71a1be3d83d0570049cd1654a9bdfe506e794ecc98ad0873304a9f35e411e"}, + {file = "asyncpg-0.30.0-cp312-cp312-win_amd64.whl", hash = "sha256:9a0292c6af5c500523949155ec17b7fe01a00ace33b68a476d6b5059f9630305"}, + {file = "asyncpg-0.30.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:05b185ebb8083c8568ea8a40e896d5f7af4b8554b64d7719c0eaa1eb5a5c3a70"}, + {file = "asyncpg-0.30.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c47806b1a8cbb0a0db896f4cd34d89942effe353a5035c62734ab13b9f938da3"}, + {file = "asyncpg-0.30.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b6fde867a74e8c76c71e2f64f80c64c0f3163e687f1763cfaf21633ec24ec33"}, + {file = "asyncpg-0.30.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46973045b567972128a27d40001124fbc821c87a6cade040cfcd4fa8a30bcdc4"}, + {file = "asyncpg-0.30.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9110df111cabc2ed81aad2f35394a00cadf4f2e0635603db6ebbd0fc896f46a4"}, + {file = "asyncpg-0.30.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:04ff0785ae7eed6cc138e73fc67b8e51d54ee7a3ce9b63666ce55a0bf095f7ba"}, + {file = "asyncpg-0.30.0-cp313-cp313-win32.whl", hash = "sha256:ae374585f51c2b444510cdf3595b97ece4f233fde739aa14b50e0d64e8a7a590"}, + {file = "asyncpg-0.30.0-cp313-cp313-win_amd64.whl", hash = "sha256:f59b430b8e27557c3fb9869222559f7417ced18688375825f8f12302c34e915e"}, + {file = "asyncpg-0.30.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:29ff1fc8b5bf724273782ff8b4f57b0f8220a1b2324184846b39d1ab4122031d"}, + {file = "asyncpg-0.30.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:64e899bce0600871b55368b8483e5e3e7f1860c9482e7f12e0a771e747988168"}, + {file = "asyncpg-0.30.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b290f4726a887f75dcd1b3006f484252db37602313f806e9ffc4e5996cfe5cb"}, + {file = "asyncpg-0.30.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f86b0e2cd3f1249d6fe6fd6cfe0cd4538ba994e2d8249c0491925629b9104d0f"}, + {file = "asyncpg-0.30.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:393af4e3214c8fa4c7b86da6364384c0d1b3298d45803375572f415b6f673f38"}, + {file = "asyncpg-0.30.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:fd4406d09208d5b4a14db9a9dbb311b6d7aeeab57bded7ed2f8ea41aeef39b34"}, + {file = "asyncpg-0.30.0-cp38-cp38-win32.whl", hash = "sha256:0b448f0150e1c3b96cb0438a0d0aa4871f1472e58de14a3ec320dbb2798fb0d4"}, + {file = "asyncpg-0.30.0-cp38-cp38-win_amd64.whl", hash = "sha256:f23b836dd90bea21104f69547923a02b167d999ce053f3d502081acea2fba15b"}, + {file = "asyncpg-0.30.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6f4e83f067b35ab5e6371f8a4c93296e0439857b4569850b178a01385e82e9ad"}, + {file = "asyncpg-0.30.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5df69d55add4efcd25ea2a3b02025b669a285b767bfbf06e356d68dbce4234ff"}, + {file = "asyncpg-0.30.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3479a0d9a852c7c84e822c073622baca862d1217b10a02dd57ee4a7a081f708"}, + {file = "asyncpg-0.30.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26683d3b9a62836fad771a18ecf4659a30f348a561279d6227dab96182f46144"}, + {file = "asyncpg-0.30.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1b982daf2441a0ed314bd10817f1606f1c28b1136abd9e4f11335358c2c631cb"}, + {file = "asyncpg-0.30.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1c06a3a50d014b303e5f6fc1e5f95eb28d2cee89cf58384b700da621e5d5e547"}, + {file = "asyncpg-0.30.0-cp39-cp39-win32.whl", hash = "sha256:1b11a555a198b08f5c4baa8f8231c74a366d190755aa4f99aacec5970afe929a"}, + {file = "asyncpg-0.30.0-cp39-cp39-win_amd64.whl", hash = "sha256:8b684a3c858a83cd876f05958823b68e8d14ec01bb0c0d14a6704c5bf9711773"}, + {file = "asyncpg-0.30.0.tar.gz", hash = "sha256:c551e9928ab6707602f44811817f82ba3c446e018bfe1d3abecc8ba5f3eac851"}, ] [package.extras] -benchmark = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -cov = ["cloudpickle", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier (<24.7)"] -tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"] +docs = ["Sphinx (>=8.1.3,<8.2.0)", "sphinx-rtd-theme (>=1.2.2)"] +gssauth = ["gssapi", "sspilib"] +test = ["distro (>=1.9.0,<1.10.0)", "flake8 (>=6.1,<7.0)", "flake8-pyi (>=24.1.0,<24.2.0)", "gssapi", "k5test", "mypy (>=1.8.0,<1.9.0)", "sspilib", "uvloop (>=0.15.3)"] + +[[package]] +name = "attrs" +version = "25.4.0" +description = "Classes Without Boilerplate" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373"}, + {file = "attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11"}, +] [[package]] name = "certifi" -version = "2024.12.14" +version = "2025.10.5" description = "Python package for providing Mozilla's CA Bundle." optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" groups = ["main"] files = [ - {file = "certifi-2024.12.14-py3-none-any.whl", hash = "sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56"}, - {file = "certifi-2024.12.14.tar.gz", hash = "sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db"}, + {file = "certifi-2025.10.5-py3-none-any.whl", hash = "sha256:0f212c2744a9bb6de0c56639a6f68afe01ecd92d91f14ae897c4fe7bbeeef0de"}, + {file = "certifi-2025.10.5.tar.gz", hash = "sha256:47c09d31ccf2acf0be3f701ea53595ee7e0b8fa08801c6624be771df09ae7b43"}, ] [[package]] name = "cffi" -version = "1.17.1" +version = "2.0.0" description = "Foreign Function Interface for Python calling C code." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["main"] files = [ - {file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"}, - {file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"}, - {file = "cffi-1.17.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382"}, - {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702"}, - {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3"}, - {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6"}, - {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17"}, - {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8"}, - {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e"}, - {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be"}, - {file = "cffi-1.17.1-cp310-cp310-win32.whl", hash = "sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c"}, - {file = "cffi-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15"}, - {file = "cffi-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401"}, - {file = "cffi-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf"}, - {file = "cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4"}, - {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41"}, - {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1"}, - {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6"}, - {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d"}, - {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6"}, - {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f"}, - {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b"}, - {file = "cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655"}, - {file = "cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0"}, - {file = "cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4"}, - {file = "cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c"}, - {file = "cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36"}, - {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5"}, - {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff"}, - {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99"}, - {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93"}, - {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3"}, - {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8"}, - {file = "cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65"}, - {file = "cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903"}, - {file = "cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e"}, - {file = "cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2"}, - {file = "cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3"}, - {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683"}, - {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5"}, - {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4"}, - {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd"}, - {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed"}, - {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9"}, - {file = "cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d"}, - {file = "cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a"}, - {file = "cffi-1.17.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:636062ea65bd0195bc012fea9321aca499c0504409f413dc88af450b57ffd03b"}, - {file = "cffi-1.17.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7eac2ef9b63c79431bc4b25f1cd649d7f061a28808cbc6c47b534bd789ef964"}, - {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e221cf152cff04059d011ee126477f0d9588303eb57e88923578ace7baad17f9"}, - {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:31000ec67d4221a71bd3f67df918b1f88f676f1c3b535a7eb473255fdc0b83fc"}, - {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f17be4345073b0a7b8ea599688f692ac3ef23ce28e5df79c04de519dbc4912c"}, - {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2b1fac190ae3ebfe37b979cc1ce69c81f4e4fe5746bb401dca63a9062cdaf1"}, - {file = "cffi-1.17.1-cp38-cp38-win32.whl", hash = "sha256:7596d6620d3fa590f677e9ee430df2958d2d6d6de2feeae5b20e82c00b76fbf8"}, - {file = "cffi-1.17.1-cp38-cp38-win_amd64.whl", hash = "sha256:78122be759c3f8a014ce010908ae03364d00a1f81ab5c7f4a7a5120607ea56e1"}, - {file = "cffi-1.17.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b2ab587605f4ba0bf81dc0cb08a41bd1c0a5906bd59243d56bad7668a6fc6c16"}, - {file = "cffi-1.17.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:28b16024becceed8c6dfbc75629e27788d8a3f9030691a1dbf9821a128b22c36"}, - {file = "cffi-1.17.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d599671f396c4723d016dbddb72fe8e0397082b0a77a4fab8028923bec050e8"}, - {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca74b8dbe6e8e8263c0ffd60277de77dcee6c837a3d0881d8c1ead7268c9e576"}, - {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87"}, - {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98e3969bcff97cae1b2def8ba499ea3d6f31ddfdb7635374834cf89a1a08ecf0"}, - {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdf5ce3acdfd1661132f2a9c19cac174758dc2352bfe37d98aa7512c6b7178b3"}, - {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9755e4345d1ec879e3849e62222a18c7174d65a6a92d5b346b1863912168b595"}, - {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f1e22e8c4419538cb197e4dd60acc919d7696e5ef98ee4da4e01d3f8cfa4cc5a"}, - {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c03e868a0b3bc35839ba98e74211ed2b05d2119be4e8a0f224fba9384f1fe02e"}, - {file = "cffi-1.17.1-cp39-cp39-win32.whl", hash = "sha256:e31ae45bc2e29f6b2abd0de1cc3b9d5205aa847cafaecb8af1476a609a2f6eb7"}, - {file = "cffi-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662"}, - {file = "cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824"}, + {file = "cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44"}, + {file = "cffi-2.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49"}, + {file = "cffi-2.0.0-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c"}, + {file = "cffi-2.0.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb"}, + {file = "cffi-2.0.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0"}, + {file = "cffi-2.0.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4"}, + {file = "cffi-2.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453"}, + {file = "cffi-2.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495"}, + {file = "cffi-2.0.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5"}, + {file = "cffi-2.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb"}, + {file = "cffi-2.0.0-cp310-cp310-win32.whl", hash = "sha256:1f72fb8906754ac8a2cc3f9f5aaa298070652a0ffae577e0ea9bd480dc3c931a"}, + {file = "cffi-2.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:b18a3ed7d5b3bd8d9ef7a8cb226502c6bf8308df1525e1cc676c3680e7176739"}, + {file = "cffi-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe"}, + {file = "cffi-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c"}, + {file = "cffi-2.0.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92"}, + {file = "cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93"}, + {file = "cffi-2.0.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5"}, + {file = "cffi-2.0.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664"}, + {file = "cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26"}, + {file = "cffi-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9"}, + {file = "cffi-2.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414"}, + {file = "cffi-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743"}, + {file = "cffi-2.0.0-cp311-cp311-win32.whl", hash = "sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5"}, + {file = "cffi-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5"}, + {file = "cffi-2.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d"}, + {file = "cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d"}, + {file = "cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c"}, + {file = "cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe"}, + {file = "cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062"}, + {file = "cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e"}, + {file = "cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037"}, + {file = "cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba"}, + {file = "cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94"}, + {file = "cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187"}, + {file = "cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18"}, + {file = "cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5"}, + {file = "cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6"}, + {file = "cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb"}, + {file = "cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca"}, + {file = "cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b"}, + {file = "cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b"}, + {file = "cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2"}, + {file = "cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3"}, + {file = "cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26"}, + {file = "cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c"}, + {file = "cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b"}, + {file = "cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27"}, + {file = "cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75"}, + {file = "cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91"}, + {file = "cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5"}, + {file = "cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13"}, + {file = "cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b"}, + {file = "cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c"}, + {file = "cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef"}, + {file = "cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775"}, + {file = "cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205"}, + {file = "cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1"}, + {file = "cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f"}, + {file = "cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25"}, + {file = "cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad"}, + {file = "cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9"}, + {file = "cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d"}, + {file = "cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c"}, + {file = "cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8"}, + {file = "cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc"}, + {file = "cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592"}, + {file = "cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512"}, + {file = "cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4"}, + {file = "cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e"}, + {file = "cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6"}, + {file = "cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9"}, + {file = "cffi-2.0.0-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:fe562eb1a64e67dd297ccc4f5addea2501664954f2692b69a76449ec7913ecbf"}, + {file = "cffi-2.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:de8dad4425a6ca6e4e5e297b27b5c824ecc7581910bf9aee86cb6835e6812aa7"}, + {file = "cffi-2.0.0-cp39-cp39-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:4647afc2f90d1ddd33441e5b0e85b16b12ddec4fca55f0d9671fef036ecca27c"}, + {file = "cffi-2.0.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3f4d46d8b35698056ec29bca21546e1551a205058ae1a181d871e278b0b28165"}, + {file = "cffi-2.0.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:e6e73b9e02893c764e7e8d5bb5ce277f1a009cd5243f8228f75f842bf937c534"}, + {file = "cffi-2.0.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:cb527a79772e5ef98fb1d700678fe031e353e765d1ca2d409c92263c6d43e09f"}, + {file = "cffi-2.0.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:61d028e90346df14fedc3d1e5441df818d095f3b87d286825dfcbd6459b7ef63"}, + {file = "cffi-2.0.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0f6084a0ea23d05d20c3edcda20c3d006f9b6f3fefeac38f59262e10cef47ee2"}, + {file = "cffi-2.0.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:1cd13c99ce269b3ed80b417dcd591415d3372bcac067009b6e0f59c7d4015e65"}, + {file = "cffi-2.0.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:89472c9762729b5ae1ad974b777416bfda4ac5642423fa93bd57a09204712322"}, + {file = "cffi-2.0.0-cp39-cp39-win32.whl", hash = "sha256:2081580ebb843f759b9f617314a24ed5738c51d2aee65d31e02f6f7a2b97707a"}, + {file = "cffi-2.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:b882b3df248017dba09d6b16defe9b5c407fe32fc7c65a9c69798e6175601be9"}, + {file = "cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529"}, ] [package.dependencies] -pycparser = "*" +pycparser = {version = "*", markers = "implementation_name != \"PyPy\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["dev"] +markers = "sys_platform == \"win32\"" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] [[package]] name = "dapr" -version = "1.14.0" +version = "1.16.0" description = "The official release of Dapr Python SDK." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["main"] files = [ - {file = "dapr-1.14.0-py3-none-any.whl", hash = "sha256:31bfa9587b58d410a575dd46e568cd731e790e235d7b61b18cb17420977e9c84"}, - {file = "dapr-1.14.0.tar.gz", hash = "sha256:d901b787a5154f4b4e448e439825693f3352dda374889ef541281dd2727b8d61"}, + {file = "dapr-1.16.0-py3-none-any.whl", hash = "sha256:076dd559a0b450eae24b1c2ae779c9299ed3e06a05c1f72719a6613af8d19ced"}, + {file = "dapr-1.16.0.tar.gz", hash = "sha256:c7e3d005552a598d07608d0d502b2bc432e86678f94b2beccc13a096b9198684"}, ] [package.dependencies] @@ -360,127 +489,198 @@ protobuf = ">=4.22" python-dateutil = ">=2.8.1" typing-extensions = ">=4.4.0" +[[package]] +name = "dapr-ext-workflow" +version = "1.16.0" +description = "The official release of Dapr Python SDK Workflow Authoring Extension." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "dapr-ext-workflow-1.16.0.tar.gz", hash = "sha256:7487d174394d305e668784f4bac2dcecc757a1e0a8ddf6e5e1cb32c0a887be78"}, + {file = "dapr_ext_workflow-1.16.0-py3-none-any.whl", hash = "sha256:028f6b3a340a5a8f0b061eacdef60de1ce52de2340f9636f517f799f73437ee8"}, +] + +[package.dependencies] +dapr = ">=1.16.0" +durabletask-dapr = ">=0.2.0a8" + +[[package]] +name = "durabletask-dapr" +version = "0.2.0a9" +description = "A Durable Task Client SDK for Python" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "durabletask_dapr-0.2.0a9-py3-none-any.whl", hash = "sha256:48c401c30c05a6122bdd1ee245e9b65c56dabb2dde3dfe6fe1d4ee47085e4a2e"}, + {file = "durabletask_dapr-0.2.0a9.tar.gz", hash = "sha256:ec481840a043a9d15f67628386b0694e60a04de8015f8f56883f55e490ebbb56"}, +] + +[package.dependencies] +asyncio = "*" +grpcio = "*" +protobuf = "*" + [[package]] name = "fastapi" -version = "0.115.6" +version = "0.115.14" description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" optional = false python-versions = ">=3.8" groups = ["main"] files = [ - {file = "fastapi-0.115.6-py3-none-any.whl", hash = "sha256:e9240b29e36fa8f4bb7290316988e90c381e5092e0cbe84e7818cc3713bcf305"}, - {file = "fastapi-0.115.6.tar.gz", hash = "sha256:9ec46f7addc14ea472958a96aae5b5de65f39721a46aaf5705c480d9a8b76654"}, + {file = "fastapi-0.115.14-py3-none-any.whl", hash = "sha256:6c0c8bf9420bd58f565e585036d971872472b4f7d3f6c73b698e10cffdefb3ca"}, + {file = "fastapi-0.115.14.tar.gz", hash = "sha256:b1de15cdc1c499a4da47914db35d0e4ef8f1ce62b624e94e0e5824421df99739"}, ] [package.dependencies] pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0" -starlette = ">=0.40.0,<0.42.0" +starlette = ">=0.40.0,<0.47.0" typing-extensions = ">=4.8.0" [package.extras] -all = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.7)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"] -standard = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "jinja2 (>=2.11.2)", "python-multipart (>=0.0.7)", "uvicorn[standard] (>=0.12.0)"] +all = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=3.1.5)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.18)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"] +standard = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "jinja2 (>=3.1.5)", "python-multipart (>=0.0.18)", "uvicorn[standard] (>=0.12.0)"] [[package]] name = "frozenlist" -version = "1.5.0" +version = "1.8.0" description = "A list-like structure which implements collections.abc.MutableSequence" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["main"] files = [ - {file = "frozenlist-1.5.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5b6a66c18b5b9dd261ca98dffcb826a525334b2f29e7caa54e182255c5f6a65a"}, - {file = "frozenlist-1.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d1b3eb7b05ea246510b43a7e53ed1653e55c2121019a97e60cad7efb881a97bb"}, - {file = "frozenlist-1.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:15538c0cbf0e4fa11d1e3a71f823524b0c46299aed6e10ebb4c2089abd8c3bec"}, - {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e79225373c317ff1e35f210dd5f1344ff31066ba8067c307ab60254cd3a78ad5"}, - {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9272fa73ca71266702c4c3e2d4a28553ea03418e591e377a03b8e3659d94fa76"}, - {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:498524025a5b8ba81695761d78c8dd7382ac0b052f34e66939c42df860b8ff17"}, - {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:92b5278ed9d50fe610185ecd23c55d8b307d75ca18e94c0e7de328089ac5dcba"}, - {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f3c8c1dacd037df16e85227bac13cca58c30da836c6f936ba1df0c05d046d8d"}, - {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f2ac49a9bedb996086057b75bf93538240538c6d9b38e57c82d51f75a73409d2"}, - {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e66cc454f97053b79c2ab09c17fbe3c825ea6b4de20baf1be28919460dd7877f"}, - {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:5a3ba5f9a0dfed20337d3e966dc359784c9f96503674c2faf015f7fe8e96798c"}, - {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6321899477db90bdeb9299ac3627a6a53c7399c8cd58d25da094007402b039ab"}, - {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:76e4753701248476e6286f2ef492af900ea67d9706a0155335a40ea21bf3b2f5"}, - {file = "frozenlist-1.5.0-cp310-cp310-win32.whl", hash = "sha256:977701c081c0241d0955c9586ffdd9ce44f7a7795df39b9151cd9a6fd0ce4cfb"}, - {file = "frozenlist-1.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:189f03b53e64144f90990d29a27ec4f7997d91ed3d01b51fa39d2dbe77540fd4"}, - {file = "frozenlist-1.5.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:fd74520371c3c4175142d02a976aee0b4cb4a7cc912a60586ffd8d5929979b30"}, - {file = "frozenlist-1.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2f3f7a0fbc219fb4455264cae4d9f01ad41ae6ee8524500f381de64ffaa077d5"}, - {file = "frozenlist-1.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f47c9c9028f55a04ac254346e92977bf0f166c483c74b4232bee19a6697e4778"}, - {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0996c66760924da6e88922756d99b47512a71cfd45215f3570bf1e0b694c206a"}, - {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a2fe128eb4edeabe11896cb6af88fca5346059f6c8d807e3b910069f39157869"}, - {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1a8ea951bbb6cacd492e3948b8da8c502a3f814f5d20935aae74b5df2b19cf3d"}, - {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:de537c11e4aa01d37db0d403b57bd6f0546e71a82347a97c6a9f0dcc532b3a45"}, - {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c2623347b933fcb9095841f1cc5d4ff0b278addd743e0e966cb3d460278840d"}, - {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cee6798eaf8b1416ef6909b06f7dc04b60755206bddc599f52232606e18179d3"}, - {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f5f9da7f5dbc00a604fe74aa02ae7c98bcede8a3b8b9666f9f86fc13993bc71a"}, - {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:90646abbc7a5d5c7c19461d2e3eeb76eb0b204919e6ece342feb6032c9325ae9"}, - {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:bdac3c7d9b705d253b2ce370fde941836a5f8b3c5c2b8fd70940a3ea3af7f4f2"}, - {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:03d33c2ddbc1816237a67f66336616416e2bbb6beb306e5f890f2eb22b959cdf"}, - {file = "frozenlist-1.5.0-cp311-cp311-win32.whl", hash = "sha256:237f6b23ee0f44066219dae14c70ae38a63f0440ce6750f868ee08775073f942"}, - {file = "frozenlist-1.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:0cc974cc93d32c42e7b0f6cf242a6bd941c57c61b618e78b6c0a96cb72788c1d"}, - {file = "frozenlist-1.5.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:31115ba75889723431aa9a4e77d5f398f5cf976eea3bdf61749731f62d4a4a21"}, - {file = "frozenlist-1.5.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7437601c4d89d070eac8323f121fcf25f88674627505334654fd027b091db09d"}, - {file = "frozenlist-1.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7948140d9f8ece1745be806f2bfdf390127cf1a763b925c4a805c603df5e697e"}, - {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:feeb64bc9bcc6b45c6311c9e9b99406660a9c05ca8a5b30d14a78555088b0b3a"}, - {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:683173d371daad49cffb8309779e886e59c2f369430ad28fe715f66d08d4ab1a"}, - {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7d57d8f702221405a9d9b40f9da8ac2e4a1a8b5285aac6100f3393675f0a85ee"}, - {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30c72000fbcc35b129cb09956836c7d7abf78ab5416595e4857d1cae8d6251a6"}, - {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:000a77d6034fbad9b6bb880f7ec073027908f1b40254b5d6f26210d2dab1240e"}, - {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5d7f5a50342475962eb18b740f3beecc685a15b52c91f7d975257e13e029eca9"}, - {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:87f724d055eb4785d9be84e9ebf0f24e392ddfad00b3fe036e43f489fafc9039"}, - {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:6e9080bb2fb195a046e5177f10d9d82b8a204c0736a97a153c2466127de87784"}, - {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9b93d7aaa36c966fa42efcaf716e6b3900438632a626fb09c049f6a2f09fc631"}, - {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:52ef692a4bc60a6dd57f507429636c2af8b6046db8b31b18dac02cbc8f507f7f"}, - {file = "frozenlist-1.5.0-cp312-cp312-win32.whl", hash = "sha256:29d94c256679247b33a3dc96cce0f93cbc69c23bf75ff715919332fdbb6a32b8"}, - {file = "frozenlist-1.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:8969190d709e7c48ea386db202d708eb94bdb29207a1f269bab1196ce0dcca1f"}, - {file = "frozenlist-1.5.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7a1a048f9215c90973402e26c01d1cff8a209e1f1b53f72b95c13db61b00f953"}, - {file = "frozenlist-1.5.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:dd47a5181ce5fcb463b5d9e17ecfdb02b678cca31280639255ce9d0e5aa67af0"}, - {file = "frozenlist-1.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1431d60b36d15cda188ea222033eec8e0eab488f39a272461f2e6d9e1a8e63c2"}, - {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6482a5851f5d72767fbd0e507e80737f9c8646ae7fd303def99bfe813f76cf7f"}, - {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:44c49271a937625619e862baacbd037a7ef86dd1ee215afc298a417ff3270608"}, - {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:12f78f98c2f1c2429d42e6a485f433722b0061d5c0b0139efa64f396efb5886b"}, - {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ce3aa154c452d2467487765e3adc730a8c153af77ad84096bc19ce19a2400840"}, - {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9b7dc0c4338e6b8b091e8faf0db3168a37101943e687f373dce00959583f7439"}, - {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:45e0896250900b5aa25180f9aec243e84e92ac84bd4a74d9ad4138ef3f5c97de"}, - {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:561eb1c9579d495fddb6da8959fd2a1fca2c6d060d4113f5844b433fc02f2641"}, - {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:df6e2f325bfee1f49f81aaac97d2aa757c7646534a06f8f577ce184afe2f0a9e"}, - {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:140228863501b44b809fb39ec56b5d4071f4d0aa6d216c19cbb08b8c5a7eadb9"}, - {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7707a25d6a77f5d27ea7dc7d1fc608aa0a478193823f88511ef5e6b8a48f9d03"}, - {file = "frozenlist-1.5.0-cp313-cp313-win32.whl", hash = "sha256:31a9ac2b38ab9b5a8933b693db4939764ad3f299fcaa931a3e605bc3460e693c"}, - {file = "frozenlist-1.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:11aabdd62b8b9c4b84081a3c246506d1cddd2dd93ff0ad53ede5defec7886b28"}, - {file = "frozenlist-1.5.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:dd94994fc91a6177bfaafd7d9fd951bc8689b0a98168aa26b5f543868548d3ca"}, - {file = "frozenlist-1.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2d0da8bbec082bf6bf18345b180958775363588678f64998c2b7609e34719b10"}, - {file = "frozenlist-1.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:73f2e31ea8dd7df61a359b731716018c2be196e5bb3b74ddba107f694fbd7604"}, - {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:828afae9f17e6de596825cf4228ff28fbdf6065974e5ac1410cecc22f699d2b3"}, - {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f1577515d35ed5649d52ab4319db757bb881ce3b2b796d7283e6634d99ace307"}, - {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2150cc6305a2c2ab33299453e2968611dacb970d2283a14955923062c8d00b10"}, - {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a72b7a6e3cd2725eff67cd64c8f13335ee18fc3c7befc05aed043d24c7b9ccb9"}, - {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c16d2fa63e0800723139137d667e1056bee1a1cf7965153d2d104b62855e9b99"}, - {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:17dcc32fc7bda7ce5875435003220a457bcfa34ab7924a49a1c19f55b6ee185c"}, - {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:97160e245ea33d8609cd2b8fd997c850b56db147a304a262abc2b3be021a9171"}, - {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:f1e6540b7fa044eee0bb5111ada694cf3dc15f2b0347ca125ee9ca984d5e9e6e"}, - {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:91d6c171862df0a6c61479d9724f22efb6109111017c87567cfeb7b5d1449fdf"}, - {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:c1fac3e2ace2eb1052e9f7c7db480818371134410e1f5c55d65e8f3ac6d1407e"}, - {file = "frozenlist-1.5.0-cp38-cp38-win32.whl", hash = "sha256:b97f7b575ab4a8af9b7bc1d2ef7f29d3afee2226bd03ca3875c16451ad5a7723"}, - {file = "frozenlist-1.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:374ca2dabdccad8e2a76d40b1d037f5bd16824933bf7bcea3e59c891fd4a0923"}, - {file = "frozenlist-1.5.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:9bbcdfaf4af7ce002694a4e10a0159d5a8d20056a12b05b45cea944a4953f972"}, - {file = "frozenlist-1.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1893f948bf6681733aaccf36c5232c231e3b5166d607c5fa77773611df6dc336"}, - {file = "frozenlist-1.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2b5e23253bb709ef57a8e95e6ae48daa9ac5f265637529e4ce6b003a37b2621f"}, - {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f253985bb515ecd89629db13cb58d702035ecd8cfbca7d7a7e29a0e6d39af5f"}, - {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:04a5c6babd5e8fb7d3c871dc8b321166b80e41b637c31a995ed844a6139942b6"}, - {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9fe0f1c29ba24ba6ff6abf688cb0b7cf1efab6b6aa6adc55441773c252f7411"}, - {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:226d72559fa19babe2ccd920273e767c96a49b9d3d38badd7c91a0fdeda8ea08"}, - {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15b731db116ab3aedec558573c1a5eec78822b32292fe4f2f0345b7f697745c2"}, - {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:366d8f93e3edfe5a918c874702f78faac300209a4d5bf38352b2c1bdc07a766d"}, - {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:1b96af8c582b94d381a1c1f51ffaedeb77c821c690ea5f01da3d70a487dd0a9b"}, - {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:c03eff4a41bd4e38415cbed054bbaff4a075b093e2394b6915dca34a40d1e38b"}, - {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:50cf5e7ee9b98f22bdecbabf3800ae78ddcc26e4a435515fc72d97903e8488e0"}, - {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1e76bfbc72353269c44e0bc2cfe171900fbf7f722ad74c9a7b638052afe6a00c"}, - {file = "frozenlist-1.5.0-cp39-cp39-win32.whl", hash = "sha256:666534d15ba8f0fda3f53969117383d5dc021266b3c1a42c9ec4855e4b58b9d3"}, - {file = "frozenlist-1.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:5c28f4b5dbef8a0d8aad0d4de24d1e9e981728628afaf4ea0792f5d0939372f0"}, - {file = "frozenlist-1.5.0-py3-none-any.whl", hash = "sha256:d994863bba198a4a518b467bb971c56e1db3f180a25c6cf7bb1949c267f748c3"}, - {file = "frozenlist-1.5.0.tar.gz", hash = "sha256:81d5af29e61b9c8348e876d442253723928dce6433e0e76cd925cd83f1b4b817"}, + {file = "frozenlist-1.8.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b37f6d31b3dcea7deb5e9696e529a6aa4a898adc33db82da12e4c60a7c4d2011"}, + {file = "frozenlist-1.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ef2b7b394f208233e471abc541cc6991f907ffd47dc72584acee3147899d6565"}, + {file = "frozenlist-1.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a88f062f072d1589b7b46e951698950e7da00442fc1cacbe17e19e025dc327ad"}, + {file = "frozenlist-1.8.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f57fb59d9f385710aa7060e89410aeb5058b99e62f4d16b08b91986b9a2140c2"}, + {file = "frozenlist-1.8.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:799345ab092bee59f01a915620b5d014698547afd011e691a208637312db9186"}, + {file = "frozenlist-1.8.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c23c3ff005322a6e16f71bf8692fcf4d5a304aaafe1e262c98c6d4adc7be863e"}, + {file = "frozenlist-1.8.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8a76ea0f0b9dfa06f254ee06053d93a600865b3274358ca48a352ce4f0798450"}, + {file = "frozenlist-1.8.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c7366fe1418a6133d5aa824ee53d406550110984de7637d65a178010f759c6ef"}, + {file = "frozenlist-1.8.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:13d23a45c4cebade99340c4165bd90eeb4a56c6d8a9d8aa49568cac19a6d0dc4"}, + {file = "frozenlist-1.8.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:e4a3408834f65da56c83528fb52ce7911484f0d1eaf7b761fc66001db1646eff"}, + {file = "frozenlist-1.8.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:42145cd2748ca39f32801dad54aeea10039da6f86e303659db90db1c4b614c8c"}, + {file = "frozenlist-1.8.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:e2de870d16a7a53901e41b64ffdf26f2fbb8917b3e6ebf398098d72c5b20bd7f"}, + {file = "frozenlist-1.8.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:20e63c9493d33ee48536600d1a5c95eefc870cd71e7ab037763d1fbb89cc51e7"}, + {file = "frozenlist-1.8.0-cp310-cp310-win32.whl", hash = "sha256:adbeebaebae3526afc3c96fad434367cafbfd1b25d72369a9e5858453b1bb71a"}, + {file = "frozenlist-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:667c3777ca571e5dbeb76f331562ff98b957431df140b54c85fd4d52eea8d8f6"}, + {file = "frozenlist-1.8.0-cp310-cp310-win_arm64.whl", hash = "sha256:80f85f0a7cc86e7a54c46d99c9e1318ff01f4687c172ede30fd52d19d1da1c8e"}, + {file = "frozenlist-1.8.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:09474e9831bc2b2199fad6da3c14c7b0fbdd377cce9d3d77131be28906cb7d84"}, + {file = "frozenlist-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:17c883ab0ab67200b5f964d2b9ed6b00971917d5d8a92df149dc2c9779208ee9"}, + {file = "frozenlist-1.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fa47e444b8ba08fffd1c18e8cdb9a75db1b6a27f17507522834ad13ed5922b93"}, + {file = "frozenlist-1.8.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2552f44204b744fba866e573be4c1f9048d6a324dfe14475103fd51613eb1d1f"}, + {file = "frozenlist-1.8.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:957e7c38f250991e48a9a73e6423db1bb9dd14e722a10f6b8bb8e16a0f55f695"}, + {file = "frozenlist-1.8.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8585e3bb2cdea02fc88ffa245069c36555557ad3609e83be0ec71f54fd4abb52"}, + {file = "frozenlist-1.8.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:edee74874ce20a373d62dc28b0b18b93f645633c2943fd90ee9d898550770581"}, + {file = "frozenlist-1.8.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c9a63152fe95756b85f31186bddf42e4c02c6321207fd6601a1c89ebac4fe567"}, + {file = "frozenlist-1.8.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b6db2185db9be0a04fecf2f241c70b63b1a242e2805be291855078f2b404dd6b"}, + {file = "frozenlist-1.8.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:f4be2e3d8bc8aabd566f8d5b8ba7ecc09249d74ba3c9ed52e54dc23a293f0b92"}, + {file = "frozenlist-1.8.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:c8d1634419f39ea6f5c427ea2f90ca85126b54b50837f31497f3bf38266e853d"}, + {file = "frozenlist-1.8.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:1a7fa382a4a223773ed64242dbe1c9c326ec09457e6b8428efb4118c685c3dfd"}, + {file = "frozenlist-1.8.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:11847b53d722050808926e785df837353bd4d75f1d494377e59b23594d834967"}, + {file = "frozenlist-1.8.0-cp311-cp311-win32.whl", hash = "sha256:27c6e8077956cf73eadd514be8fb04d77fc946a7fe9f7fe167648b0b9085cc25"}, + {file = "frozenlist-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:ac913f8403b36a2c8610bbfd25b8013488533e71e62b4b4adce9c86c8cea905b"}, + {file = "frozenlist-1.8.0-cp311-cp311-win_arm64.whl", hash = "sha256:d4d3214a0f8394edfa3e303136d0575eece0745ff2b47bd2cb2e66dd92d4351a"}, + {file = "frozenlist-1.8.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:78f7b9e5d6f2fdb88cdde9440dc147259b62b9d3b019924def9f6478be254ac1"}, + {file = "frozenlist-1.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:229bf37d2e4acdaf808fd3f06e854a4a7a3661e871b10dc1f8f1896a3b05f18b"}, + {file = "frozenlist-1.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f833670942247a14eafbb675458b4e61c82e002a148f49e68257b79296e865c4"}, + {file = "frozenlist-1.8.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:494a5952b1c597ba44e0e78113a7266e656b9794eec897b19ead706bd7074383"}, + {file = "frozenlist-1.8.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96f423a119f4777a4a056b66ce11527366a8bb92f54e541ade21f2374433f6d4"}, + {file = "frozenlist-1.8.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3462dd9475af2025c31cc61be6652dfa25cbfb56cbbf52f4ccfe029f38decaf8"}, + {file = "frozenlist-1.8.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4c800524c9cd9bac5166cd6f55285957fcfc907db323e193f2afcd4d9abd69b"}, + {file = "frozenlist-1.8.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d6a5df73acd3399d893dafc71663ad22534b5aa4f94e8a2fabfe856c3c1b6a52"}, + {file = "frozenlist-1.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:405e8fe955c2280ce66428b3ca55e12b3c4e9c336fb2103a4937e891c69a4a29"}, + {file = "frozenlist-1.8.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:908bd3f6439f2fef9e85031b59fd4f1297af54415fb60e4254a95f75b3cab3f3"}, + {file = "frozenlist-1.8.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:294e487f9ec720bd8ffcebc99d575f7eff3568a08a253d1ee1a0378754b74143"}, + {file = "frozenlist-1.8.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:74c51543498289c0c43656701be6b077f4b265868fa7f8a8859c197006efb608"}, + {file = "frozenlist-1.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:776f352e8329135506a1d6bf16ac3f87bc25b28e765949282dcc627af36123aa"}, + {file = "frozenlist-1.8.0-cp312-cp312-win32.whl", hash = "sha256:433403ae80709741ce34038da08511d4a77062aa924baf411ef73d1146e74faf"}, + {file = "frozenlist-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:34187385b08f866104f0c0617404c8eb08165ab1272e884abc89c112e9c00746"}, + {file = "frozenlist-1.8.0-cp312-cp312-win_arm64.whl", hash = "sha256:fe3c58d2f5db5fbd18c2987cba06d51b0529f52bc3a6cdc33d3f4eab725104bd"}, + {file = "frozenlist-1.8.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8d92f1a84bb12d9e56f818b3a746f3efba93c1b63c8387a73dde655e1e42282a"}, + {file = "frozenlist-1.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:96153e77a591c8adc2ee805756c61f59fef4cf4073a9275ee86fe8cba41241f7"}, + {file = "frozenlist-1.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f21f00a91358803399890ab167098c131ec2ddd5f8f5fd5fe9c9f2c6fcd91e40"}, + {file = "frozenlist-1.8.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fb30f9626572a76dfe4293c7194a09fb1fe93ba94c7d4f720dfae3b646b45027"}, + {file = "frozenlist-1.8.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eaa352d7047a31d87dafcacbabe89df0aa506abb5b1b85a2fb91bc3faa02d822"}, + {file = "frozenlist-1.8.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:03ae967b4e297f58f8c774c7eabcce57fe3c2434817d4385c50661845a058121"}, + {file = "frozenlist-1.8.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f6292f1de555ffcc675941d65fffffb0a5bcd992905015f85d0592201793e0e5"}, + {file = "frozenlist-1.8.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29548f9b5b5e3460ce7378144c3010363d8035cea44bc0bf02d57f5a685e084e"}, + {file = "frozenlist-1.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ec3cc8c5d4084591b4237c0a272cc4f50a5b03396a47d9caaf76f5d7b38a4f11"}, + {file = "frozenlist-1.8.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:517279f58009d0b1f2e7c1b130b377a349405da3f7621ed6bfae50b10adf20c1"}, + {file = "frozenlist-1.8.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:db1e72ede2d0d7ccb213f218df6a078a9c09a7de257c2fe8fcef16d5925230b1"}, + {file = "frozenlist-1.8.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b4dec9482a65c54a5044486847b8a66bf10c9cb4926d42927ec4e8fd5db7fed8"}, + {file = "frozenlist-1.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:21900c48ae04d13d416f0e1e0c4d81f7931f73a9dfa0b7a8746fb2fe7dd970ed"}, + {file = "frozenlist-1.8.0-cp313-cp313-win32.whl", hash = "sha256:8b7b94a067d1c504ee0b16def57ad5738701e4ba10cec90529f13fa03c833496"}, + {file = "frozenlist-1.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:878be833caa6a3821caf85eb39c5ba92d28e85df26d57afb06b35b2efd937231"}, + {file = "frozenlist-1.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:44389d135b3ff43ba8cc89ff7f51f5a0bb6b63d829c8300f79a2fe4fe61bcc62"}, + {file = "frozenlist-1.8.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:e25ac20a2ef37e91c1b39938b591457666a0fa835c7783c3a8f33ea42870db94"}, + {file = "frozenlist-1.8.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:07cdca25a91a4386d2e76ad992916a85038a9b97561bf7a3fd12d5d9ce31870c"}, + {file = "frozenlist-1.8.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4e0c11f2cc6717e0a741f84a527c52616140741cd812a50422f83dc31749fb52"}, + {file = "frozenlist-1.8.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b3210649ee28062ea6099cfda39e147fa1bc039583c8ee4481cb7811e2448c51"}, + {file = "frozenlist-1.8.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:581ef5194c48035a7de2aefc72ac6539823bb71508189e5de01d60c9dcd5fa65"}, + {file = "frozenlist-1.8.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3ef2d026f16a2b1866e1d86fc4e1291e1ed8a387b2c333809419a2f8b3a77b82"}, + {file = "frozenlist-1.8.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5500ef82073f599ac84d888e3a8c1f77ac831183244bfd7f11eaa0289fb30714"}, + {file = "frozenlist-1.8.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:50066c3997d0091c411a66e710f4e11752251e6d2d73d70d8d5d4c76442a199d"}, + {file = "frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:5c1c8e78426e59b3f8005e9b19f6ff46e5845895adbde20ece9218319eca6506"}, + {file = "frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:eefdba20de0d938cec6a89bd4d70f346a03108a19b9df4248d3cf0d88f1b0f51"}, + {file = "frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:cf253e0e1c3ceb4aaff6df637ce033ff6535fb8c70a764a8f46aafd3d6ab798e"}, + {file = "frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:032efa2674356903cd0261c4317a561a6850f3ac864a63fc1583147fb05a79b0"}, + {file = "frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6da155091429aeba16851ecb10a9104a108bcd32f6c1642867eadaee401c1c41"}, + {file = "frozenlist-1.8.0-cp313-cp313t-win32.whl", hash = "sha256:0f96534f8bfebc1a394209427d0f8a63d343c9779cda6fc25e8e121b5fd8555b"}, + {file = "frozenlist-1.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5d63a068f978fc69421fb0e6eb91a9603187527c86b7cd3f534a5b77a592b888"}, + {file = "frozenlist-1.8.0-cp313-cp313t-win_arm64.whl", hash = "sha256:bf0a7e10b077bf5fb9380ad3ae8ce20ef919a6ad93b4552896419ac7e1d8e042"}, + {file = "frozenlist-1.8.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cee686f1f4cadeb2136007ddedd0aaf928ab95216e7691c63e50a8ec066336d0"}, + {file = "frozenlist-1.8.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:119fb2a1bd47307e899c2fac7f28e85b9a543864df47aa7ec9d3c1b4545f096f"}, + {file = "frozenlist-1.8.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4970ece02dbc8c3a92fcc5228e36a3e933a01a999f7094ff7c23fbd2beeaa67c"}, + {file = "frozenlist-1.8.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:cba69cb73723c3f329622e34bdbf5ce1f80c21c290ff04256cff1cd3c2036ed2"}, + {file = "frozenlist-1.8.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:778a11b15673f6f1df23d9586f83c4846c471a8af693a22e066508b77d201ec8"}, + {file = "frozenlist-1.8.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0325024fe97f94c41c08872db482cf8ac4800d80e79222c6b0b7b162d5b13686"}, + {file = "frozenlist-1.8.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:97260ff46b207a82a7567b581ab4190bd4dfa09f4db8a8b49d1a958f6aa4940e"}, + {file = "frozenlist-1.8.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:54b2077180eb7f83dd52c40b2750d0a9f175e06a42e3213ce047219de902717a"}, + {file = "frozenlist-1.8.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2f05983daecab868a31e1da44462873306d3cbfd76d1f0b5b69c473d21dbb128"}, + {file = "frozenlist-1.8.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:33f48f51a446114bc5d251fb2954ab0164d5be02ad3382abcbfe07e2531d650f"}, + {file = "frozenlist-1.8.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:154e55ec0655291b5dd1b8731c637ecdb50975a2ae70c606d100750a540082f7"}, + {file = "frozenlist-1.8.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:4314debad13beb564b708b4a496020e5306c7333fa9a3ab90374169a20ffab30"}, + {file = "frozenlist-1.8.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:073f8bf8becba60aa931eb3bc420b217bb7d5b8f4750e6f8b3be7f3da85d38b7"}, + {file = "frozenlist-1.8.0-cp314-cp314-win32.whl", hash = "sha256:bac9c42ba2ac65ddc115d930c78d24ab8d4f465fd3fc473cdedfccadb9429806"}, + {file = "frozenlist-1.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:3e0761f4d1a44f1d1a47996511752cf3dcec5bbdd9cc2b4fe595caf97754b7a0"}, + {file = "frozenlist-1.8.0-cp314-cp314-win_arm64.whl", hash = "sha256:d1eaff1d00c7751b7c6662e9c5ba6eb2c17a2306ba5e2a37f24ddf3cc953402b"}, + {file = "frozenlist-1.8.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:d3bb933317c52d7ea5004a1c442eef86f426886fba134ef8cf4226ea6ee1821d"}, + {file = "frozenlist-1.8.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:8009897cdef112072f93a0efdce29cd819e717fd2f649ee3016efd3cd885a7ed"}, + {file = "frozenlist-1.8.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2c5dcbbc55383e5883246d11fd179782a9d07a986c40f49abe89ddf865913930"}, + {file = "frozenlist-1.8.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:39ecbc32f1390387d2aa4f5a995e465e9e2f79ba3adcac92d68e3e0afae6657c"}, + {file = "frozenlist-1.8.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92db2bf818d5cc8d9c1f1fc56b897662e24ea5adb36ad1f1d82875bd64e03c24"}, + {file = "frozenlist-1.8.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2dc43a022e555de94c3b68a4ef0b11c4f747d12c024a520c7101709a2144fb37"}, + {file = "frozenlist-1.8.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cb89a7f2de3602cfed448095bab3f178399646ab7c61454315089787df07733a"}, + {file = "frozenlist-1.8.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:33139dc858c580ea50e7e60a1b0ea003efa1fd42e6ec7fdbad78fff65fad2fd2"}, + {file = "frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:168c0969a329b416119507ba30b9ea13688fafffac1b7822802537569a1cb0ef"}, + {file = "frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:28bd570e8e189d7f7b001966435f9dac6718324b5be2990ac496cf1ea9ddb7fe"}, + {file = "frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b2a095d45c5d46e5e79ba1e5b9cb787f541a8dee0433836cea4b96a2c439dcd8"}, + {file = "frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:eab8145831a0d56ec9c4139b6c3e594c7a83c2c8be25d5bcf2d86136a532287a"}, + {file = "frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:974b28cf63cc99dfb2188d8d222bc6843656188164848c4f679e63dae4b0708e"}, + {file = "frozenlist-1.8.0-cp314-cp314t-win32.whl", hash = "sha256:342c97bf697ac5480c0a7ec73cd700ecfa5a8a40ac923bd035484616efecc2df"}, + {file = "frozenlist-1.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:06be8f67f39c8b1dc671f5d83aaefd3358ae5cdcf8314552c57e7ed3e6475bdd"}, + {file = "frozenlist-1.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:102e6314ca4da683dca92e3b1355490fed5f313b768500084fbe6371fddfdb79"}, + {file = "frozenlist-1.8.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:d8b7138e5cd0647e4523d6685b0eac5d4be9a184ae9634492f25c6eb38c12a47"}, + {file = "frozenlist-1.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a6483e309ca809f1efd154b4d37dc6d9f61037d6c6a81c2dc7a15cb22c8c5dca"}, + {file = "frozenlist-1.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1b9290cf81e95e93fdf90548ce9d3c1211cf574b8e3f4b3b7cb0537cf2227068"}, + {file = "frozenlist-1.8.0-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:59a6a5876ca59d1b63af8cd5e7ffffb024c3dc1e9cf9301b21a2e76286505c95"}, + {file = "frozenlist-1.8.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6dc4126390929823e2d2d9dc79ab4046ed74680360fc5f38b585c12c66cdf459"}, + {file = "frozenlist-1.8.0-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:332db6b2563333c5671fecacd085141b5800cb866be16d5e3eb15a2086476675"}, + {file = "frozenlist-1.8.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9ff15928d62a0b80bb875655c39bf517938c7d589554cbd2669be42d97c2cb61"}, + {file = "frozenlist-1.8.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7bf6cdf8e07c8151fba6fe85735441240ec7f619f935a5205953d58009aef8c6"}, + {file = "frozenlist-1.8.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:48e6d3f4ec5c7273dfe83ff27c91083c6c9065af655dc2684d2c200c94308bb5"}, + {file = "frozenlist-1.8.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:1a7607e17ad33361677adcd1443edf6f5da0ce5e5377b798fba20fae194825f3"}, + {file = "frozenlist-1.8.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:5a3a935c3a4e89c733303a2d5a7c257ea44af3a56c8202df486b7f5de40f37e1"}, + {file = "frozenlist-1.8.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:940d4a017dbfed9daf46a3b086e1d2167e7012ee297fef9e1c545c4d022f5178"}, + {file = "frozenlist-1.8.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:b9be22a69a014bc47e78072d0ecae716f5eb56c15238acca0f43d6eb8e4a5bda"}, + {file = "frozenlist-1.8.0-cp39-cp39-win32.whl", hash = "sha256:1aa77cb5697069af47472e39612976ed05343ff2e84a3dcf15437b232cbfd087"}, + {file = "frozenlist-1.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:7398c222d1d405e796970320036b1b563892b65809d9e5261487bb2c7f7b5c6a"}, + {file = "frozenlist-1.8.0-cp39-cp39-win_arm64.whl", hash = "sha256:b4f3b365f31c6cd4af24545ca0a244a53688cad8834e32f56831c4923b50a103"}, + {file = "frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d"}, + {file = "frozenlist-1.8.0.tar.gz", hash = "sha256:3ede829ed8d842f6cd48fc7081d7a41001a56f1f38603f9d49bf3020d59a31ad"}, ] [[package]] @@ -503,88 +703,97 @@ grpc = ["grpcio (>=1.44.0,<2.0.0)"] [[package]] name = "grpcio" -version = "1.69.0" +version = "1.75.1" description = "HTTP/2-based RPC framework" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["main"] files = [ - {file = "grpcio-1.69.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:2060ca95a8db295ae828d0fc1c7f38fb26ccd5edf9aa51a0f44251f5da332e97"}, - {file = "grpcio-1.69.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:2e52e107261fd8fa8fa457fe44bfadb904ae869d87c1280bf60f93ecd3e79278"}, - {file = "grpcio-1.69.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:316463c0832d5fcdb5e35ff2826d9aa3f26758d29cdfb59a368c1d6c39615a11"}, - {file = "grpcio-1.69.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:26c9a9c4ac917efab4704b18eed9082ed3b6ad19595f047e8173b5182fec0d5e"}, - {file = "grpcio-1.69.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90b3646ced2eae3a0599658eeccc5ba7f303bf51b82514c50715bdd2b109e5ec"}, - {file = "grpcio-1.69.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:3b75aea7c6cb91b341c85e7c1d9db1e09e1dd630b0717f836be94971e015031e"}, - {file = "grpcio-1.69.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5cfd14175f9db33d4b74d63de87c64bb0ee29ce475ce3c00c01ad2a3dc2a9e51"}, - {file = "grpcio-1.69.0-cp310-cp310-win32.whl", hash = "sha256:9031069d36cb949205293cf0e243abd5e64d6c93e01b078c37921493a41b72dc"}, - {file = "grpcio-1.69.0-cp310-cp310-win_amd64.whl", hash = "sha256:cc89b6c29f3dccbe12d7a3b3f1b3999db4882ae076c1c1f6df231d55dbd767a5"}, - {file = "grpcio-1.69.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:8de1b192c29b8ce45ee26a700044717bcbbd21c697fa1124d440548964328561"}, - {file = "grpcio-1.69.0-cp311-cp311-macosx_10_14_universal2.whl", hash = "sha256:7e76accf38808f5c5c752b0ab3fd919eb14ff8fafb8db520ad1cc12afff74de6"}, - {file = "grpcio-1.69.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:d5658c3c2660417d82db51e168b277e0ff036d0b0f859fa7576c0ffd2aec1442"}, - {file = "grpcio-1.69.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5494d0e52bf77a2f7eb17c6da662886ca0a731e56c1c85b93505bece8dc6cf4c"}, - {file = "grpcio-1.69.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4ed866f9edb574fd9be71bf64c954ce1b88fc93b2a4cbf94af221e9426eb14d6"}, - {file = "grpcio-1.69.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c5ba38aeac7a2fe353615c6b4213d1fbb3a3c34f86b4aaa8be08baaaee8cc56d"}, - {file = "grpcio-1.69.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f79e05f5bbf551c4057c227d1b041ace0e78462ac8128e2ad39ec58a382536d2"}, - {file = "grpcio-1.69.0-cp311-cp311-win32.whl", hash = "sha256:bf1f8be0da3fcdb2c1e9f374f3c2d043d606d69f425cd685110dd6d0d2d61258"}, - {file = "grpcio-1.69.0-cp311-cp311-win_amd64.whl", hash = "sha256:fb9302afc3a0e4ba0b225cd651ef8e478bf0070cf11a529175caecd5ea2474e7"}, - {file = "grpcio-1.69.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:fc18a4de8c33491ad6f70022af5c460b39611e39578a4d84de0fe92f12d5d47b"}, - {file = "grpcio-1.69.0-cp312-cp312-macosx_10_14_universal2.whl", hash = "sha256:0f0270bd9ffbff6961fe1da487bdcd594407ad390cc7960e738725d4807b18c4"}, - {file = "grpcio-1.69.0-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:dc48f99cc05e0698e689b51a05933253c69a8c8559a47f605cff83801b03af0e"}, - {file = "grpcio-1.69.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e925954b18d41aeb5ae250262116d0970893b38232689c4240024e4333ac084"}, - {file = "grpcio-1.69.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87d222569273720366f68a99cb62e6194681eb763ee1d3b1005840678d4884f9"}, - {file = "grpcio-1.69.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:b62b0f41e6e01a3e5082000b612064c87c93a49b05f7602fe1b7aa9fd5171a1d"}, - {file = "grpcio-1.69.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:db6f9fd2578dbe37db4b2994c94a1d9c93552ed77dca80e1657bb8a05b898b55"}, - {file = "grpcio-1.69.0-cp312-cp312-win32.whl", hash = "sha256:b192b81076073ed46f4b4dd612b8897d9a1e39d4eabd822e5da7b38497ed77e1"}, - {file = "grpcio-1.69.0-cp312-cp312-win_amd64.whl", hash = "sha256:1227ff7836f7b3a4ab04e5754f1d001fa52a730685d3dc894ed8bc262cc96c01"}, - {file = "grpcio-1.69.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:a78a06911d4081a24a1761d16215a08e9b6d4d29cdbb7e427e6c7e17b06bcc5d"}, - {file = "grpcio-1.69.0-cp313-cp313-macosx_10_14_universal2.whl", hash = "sha256:dc5a351927d605b2721cbb46158e431dd49ce66ffbacb03e709dc07a491dde35"}, - {file = "grpcio-1.69.0-cp313-cp313-manylinux_2_17_aarch64.whl", hash = "sha256:3629d8a8185f5139869a6a17865d03113a260e311e78fbe313f1a71603617589"}, - {file = "grpcio-1.69.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c9a281878feeb9ae26db0622a19add03922a028d4db684658f16d546601a4870"}, - {file = "grpcio-1.69.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cc614e895177ab7e4b70f154d1a7c97e152577ea101d76026d132b7aaba003b"}, - {file = "grpcio-1.69.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:1ee76cd7e2e49cf9264f6812d8c9ac1b85dda0eaea063af07292400f9191750e"}, - {file = "grpcio-1.69.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:0470fa911c503af59ec8bc4c82b371ee4303ececbbdc055f55ce48e38b20fd67"}, - {file = "grpcio-1.69.0-cp313-cp313-win32.whl", hash = "sha256:b650f34aceac8b2d08a4c8d7dc3e8a593f4d9e26d86751ebf74ebf5107d927de"}, - {file = "grpcio-1.69.0-cp313-cp313-win_amd64.whl", hash = "sha256:028337786f11fecb5d7b7fa660475a06aabf7e5e52b5ac2df47414878c0ce7ea"}, - {file = "grpcio-1.69.0-cp38-cp38-linux_armv7l.whl", hash = "sha256:b7f693db593d6bf285e015d5538bf1c86cf9c60ed30b6f7da04a00ed052fe2f3"}, - {file = "grpcio-1.69.0-cp38-cp38-macosx_10_14_universal2.whl", hash = "sha256:8b94e83f66dbf6fd642415faca0608590bc5e8d30e2c012b31d7d1b91b1de2fd"}, - {file = "grpcio-1.69.0-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:b634851b92c090763dde61df0868c730376cdb73a91bcc821af56ae043b09596"}, - {file = "grpcio-1.69.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bf5f680d3ed08c15330d7830d06bc65f58ca40c9999309517fd62880d70cb06e"}, - {file = "grpcio-1.69.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:200e48a6e7b00f804cf00a1c26292a5baa96507c7749e70a3ec10ca1a288936e"}, - {file = "grpcio-1.69.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:45a4704339b6e5b24b0e136dea9ad3815a94f30eb4f1e1d44c4ac484ef11d8dd"}, - {file = "grpcio-1.69.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:85d347cb8237751b23539981dbd2d9d8f6e9ff90082b427b13022b948eb6347a"}, - {file = "grpcio-1.69.0-cp38-cp38-win32.whl", hash = "sha256:60e5de105dc02832dc8f120056306d0ef80932bcf1c0e2b4ca3b676de6dc6505"}, - {file = "grpcio-1.69.0-cp38-cp38-win_amd64.whl", hash = "sha256:282f47d0928e40f25d007f24eb8fa051cb22551e3c74b8248bc9f9bea9c35fe0"}, - {file = "grpcio-1.69.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:dd034d68a2905464c49479b0c209c773737a4245d616234c79c975c7c90eca03"}, - {file = "grpcio-1.69.0-cp39-cp39-macosx_10_14_universal2.whl", hash = "sha256:01f834732c22a130bdf3dc154d1053bdbc887eb3ccb7f3e6285cfbfc33d9d5cc"}, - {file = "grpcio-1.69.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:a7f4ed0dcf202a70fe661329f8874bc3775c14bb3911d020d07c82c766ce0eb1"}, - {file = "grpcio-1.69.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cd7ea241b10bc5f0bb0f82c0d7896822b7ed122b3ab35c9851b440c1ccf81588"}, - {file = "grpcio-1.69.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f03dc9b4da4c0dc8a1db7a5420f575251d7319b7a839004d8916257ddbe4816"}, - {file = "grpcio-1.69.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:ca71d73a270dff052fe4edf74fef142d6ddd1f84175d9ac4a14b7280572ac519"}, - {file = "grpcio-1.69.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5ccbed100dc43704e94ccff9e07680b540d64e4cc89213ab2832b51b4f68a520"}, - {file = "grpcio-1.69.0-cp39-cp39-win32.whl", hash = "sha256:1514341def9c6ec4b7f0b9628be95f620f9d4b99331b7ef0a1845fd33d9b579c"}, - {file = "grpcio-1.69.0-cp39-cp39-win_amd64.whl", hash = "sha256:c1fea55d26d647346acb0069b08dca70984101f2dc95066e003019207212e303"}, - {file = "grpcio-1.69.0.tar.gz", hash = "sha256:936fa44241b5379c5afc344e1260d467bee495747eaf478de825bab2791da6f5"}, + {file = "grpcio-1.75.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:1712b5890b22547dd29f3215c5788d8fc759ce6dd0b85a6ba6e2731f2d04c088"}, + {file = "grpcio-1.75.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:8d04e101bba4b55cea9954e4aa71c24153ba6182481b487ff376da28d4ba46cf"}, + {file = "grpcio-1.75.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:683cfc70be0c1383449097cba637317e4737a357cfc185d887fd984206380403"}, + {file = "grpcio-1.75.1-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:491444c081a54dcd5e6ada57314321ae526377f498d4aa09d975c3241c5b9e1c"}, + {file = "grpcio-1.75.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ce08d4e112d0d38487c2b631ec8723deac9bc404e9c7b1011426af50a79999e4"}, + {file = "grpcio-1.75.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5a2acda37fc926ccc4547977ac3e56b1df48fe200de968e8c8421f6e3093df6c"}, + {file = "grpcio-1.75.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:745c5fe6bf05df6a04bf2d11552c7d867a2690759e7ab6b05c318a772739bd75"}, + {file = "grpcio-1.75.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:259526a7159d39e2db40d566fe3e8f8e034d0fb2db5bf9c00e09aace655a4c2b"}, + {file = "grpcio-1.75.1-cp310-cp310-win32.whl", hash = "sha256:f4b29b9aabe33fed5df0a85e5f13b09ff25e2c05bd5946d25270a8bd5682dac9"}, + {file = "grpcio-1.75.1-cp310-cp310-win_amd64.whl", hash = "sha256:cf2e760978dcce7ff7d465cbc7e276c3157eedc4c27aa6de7b594c7a295d3d61"}, + {file = "grpcio-1.75.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:573855ca2e58e35032aff30bfbd1ee103fbcf4472e4b28d4010757700918e326"}, + {file = "grpcio-1.75.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:6a4996a2c8accc37976dc142d5991adf60733e223e5c9a2219e157dc6a8fd3a2"}, + {file = "grpcio-1.75.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b1ea1bbe77ecbc1be00af2769f4ae4a88ce93be57a4f3eebd91087898ed749f9"}, + {file = "grpcio-1.75.1-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:e5b425aee54cc5e3e3c58f00731e8a33f5567965d478d516d35ef99fd648ab68"}, + {file = "grpcio-1.75.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0049a7bf547dafaeeb1db17079ce79596c298bfe308fc084d023c8907a845b9a"}, + {file = "grpcio-1.75.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5b8ea230c7f77c0a1a3208a04a1eda164633fb0767b4cefd65a01079b65e5b1f"}, + {file = "grpcio-1.75.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:36990d629c3c9fb41e546414e5af52d0a7af37ce7113d9682c46d7e2919e4cca"}, + {file = "grpcio-1.75.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b10ad908118d38c2453ade7ff790e5bce36580c3742919007a2a78e3a1e521ca"}, + {file = "grpcio-1.75.1-cp311-cp311-win32.whl", hash = "sha256:d6be2b5ee7bea656c954dcf6aa8093c6f0e6a3ef9945c99d99fcbfc88c5c0bfe"}, + {file = "grpcio-1.75.1-cp311-cp311-win_amd64.whl", hash = "sha256:61c692fb05956b17dd6d1ab480f7f10ad0536dba3bc8fd4e3c7263dc244ed772"}, + {file = "grpcio-1.75.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:7b888b33cd14085d86176b1628ad2fcbff94cfbbe7809465097aa0132e58b018"}, + {file = "grpcio-1.75.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:8775036efe4ad2085975531d221535329f5dac99b6c2a854a995456098f99546"}, + {file = "grpcio-1.75.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:bb658f703468d7fbb5dcc4037c65391b7dc34f808ac46ed9136c24fc5eeb041d"}, + {file = "grpcio-1.75.1-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:4b7177a1cdb3c51b02b0c0a256b0a72fdab719600a693e0e9037949efffb200b"}, + {file = "grpcio-1.75.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7d4fa6ccc3ec2e68a04f7b883d354d7fea22a34c44ce535a2f0c0049cf626ddf"}, + {file = "grpcio-1.75.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3d86880ecaeb5b2f0a8afa63824de93adb8ebe4e49d0e51442532f4e08add7d6"}, + {file = "grpcio-1.75.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a8041d2f9e8a742aeae96f4b047ee44e73619f4f9d24565e84d5446c623673b6"}, + {file = "grpcio-1.75.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3652516048bf4c314ce12be37423c79829f46efffb390ad64149a10c6071e8de"}, + {file = "grpcio-1.75.1-cp312-cp312-win32.whl", hash = "sha256:44b62345d8403975513af88da2f3d5cc76f73ca538ba46596f92a127c2aea945"}, + {file = "grpcio-1.75.1-cp312-cp312-win_amd64.whl", hash = "sha256:b1e191c5c465fa777d4cafbaacf0c01e0d5278022082c0abbd2ee1d6454ed94d"}, + {file = "grpcio-1.75.1-cp313-cp313-linux_armv7l.whl", hash = "sha256:3bed22e750d91d53d9e31e0af35a7b0b51367e974e14a4ff229db5b207647884"}, + {file = "grpcio-1.75.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:5b8f381eadcd6ecaa143a21e9e80a26424c76a0a9b3d546febe6648f3a36a5ac"}, + {file = "grpcio-1.75.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5bf4001d3293e3414d0cf99ff9b1139106e57c3a66dfff0c5f60b2a6286ec133"}, + {file = "grpcio-1.75.1-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:9f82ff474103e26351dacfe8d50214e7c9322960d8d07ba7fa1d05ff981c8b2d"}, + {file = "grpcio-1.75.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0ee119f4f88d9f75414217823d21d75bfe0e6ed40135b0cbbfc6376bc9f7757d"}, + {file = "grpcio-1.75.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:664eecc3abe6d916fa6cf8dd6b778e62fb264a70f3430a3180995bf2da935446"}, + {file = "grpcio-1.75.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:c32193fa08b2fbebf08fe08e84f8a0aad32d87c3ad42999c65e9449871b1c66e"}, + {file = "grpcio-1.75.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5cebe13088b9254f6e615bcf1da9131d46cfa4e88039454aca9cb65f639bd3bc"}, + {file = "grpcio-1.75.1-cp313-cp313-win32.whl", hash = "sha256:4b4c678e7ed50f8ae8b8dbad15a865ee73ce12668b6aaf411bf3258b5bc3f970"}, + {file = "grpcio-1.75.1-cp313-cp313-win_amd64.whl", hash = "sha256:5573f51e3f296a1bcf71e7a690c092845fb223072120f4bdb7a5b48e111def66"}, + {file = "grpcio-1.75.1-cp314-cp314-linux_armv7l.whl", hash = "sha256:c05da79068dd96723793bffc8d0e64c45f316248417515f28d22204d9dae51c7"}, + {file = "grpcio-1.75.1-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:06373a94fd16ec287116a825161dca179a0402d0c60674ceeec8c9fba344fe66"}, + {file = "grpcio-1.75.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4484f4b7287bdaa7a5b3980f3c7224c3c622669405d20f69549f5fb956ad0421"}, + {file = "grpcio-1.75.1-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:2720c239c1180eee69f7883c1d4c83fc1a495a2535b5fa322887c70bf02b16e8"}, + {file = "grpcio-1.75.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:07a554fa31c668cf0e7a188678ceeca3cb8fead29bbe455352e712ec33ca701c"}, + {file = "grpcio-1.75.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:3e71a2105210366bfc398eef7f57a664df99194f3520edb88b9c3a7e46ee0d64"}, + {file = "grpcio-1.75.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:8679aa8a5b67976776d3c6b0521e99d1c34db8a312a12bcfd78a7085cb9b604e"}, + {file = "grpcio-1.75.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:aad1c774f4ebf0696a7f148a56d39a3432550612597331792528895258966dc0"}, + {file = "grpcio-1.75.1-cp314-cp314-win32.whl", hash = "sha256:62ce42d9994446b307649cb2a23335fa8e927f7ab2cbf5fcb844d6acb4d85f9c"}, + {file = "grpcio-1.75.1-cp314-cp314-win_amd64.whl", hash = "sha256:f86e92275710bea3000cb79feca1762dc0ad3b27830dd1a74e82ab321d4ee464"}, + {file = "grpcio-1.75.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:c09fba33327c3ac11b5c33dbdd8218eef8990d78f83b1656d628831812a8c0fb"}, + {file = "grpcio-1.75.1-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:7e21400b037be29545704889e72e586c238e346dcb2d08d8a7288d16c883a9ec"}, + {file = "grpcio-1.75.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c12121e509b9f8b0914d10054d24120237d19e870b1cd82acbb8a9b9ddd198a3"}, + {file = "grpcio-1.75.1-cp39-cp39-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:73577a93e692b3474b1bfe84285d098de36705dbd838bb4d6a056d326e4dc880"}, + {file = "grpcio-1.75.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e19e7dfa0d7ca7dea22be464339e18ac608fd75d88c56770c646cdabe54bc724"}, + {file = "grpcio-1.75.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:4e1c28f51c1cf67eccdfc1065e8e866c9ed622f09773ca60947089c117f848a1"}, + {file = "grpcio-1.75.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:030a6164bc2ca726052778c0cf8e3249617a34e368354f9e6107c27ad4af8c28"}, + {file = "grpcio-1.75.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:67697efef5a98d46d5db7b1720fa4043536f8b8e5072a5d61cfca762f287e939"}, + {file = "grpcio-1.75.1-cp39-cp39-win32.whl", hash = "sha256:52015cf73eb5d76f6404e0ce0505a69b51fd1f35810b3a01233b34b10baafb41"}, + {file = "grpcio-1.75.1-cp39-cp39-win_amd64.whl", hash = "sha256:9fe51e4a1f896ea84ac750900eae34d9e9b896b5b1e4a30b02dc31ad29f36383"}, + {file = "grpcio-1.75.1.tar.gz", hash = "sha256:3e81d89ece99b9ace23a6916880baca613c03a799925afb2857887efa8b1b3d2"}, ] +[package.dependencies] +typing-extensions = ">=4.12,<5.0" + [package.extras] -protobuf = ["grpcio-tools (>=1.69.0)"] +protobuf = ["grpcio-tools (>=1.75.1)"] [[package]] name = "grpcio-status" -version = "1.62.3" +version = "1.75.1" description = "Status proto mapping for gRPC" optional = false -python-versions = ">=3.6" +python-versions = ">=3.9" groups = ["main"] files = [ - {file = "grpcio-status-1.62.3.tar.gz", hash = "sha256:289bdd7b2459794a12cf95dc0cb727bd4a1742c37bd823f760236c937e53a485"}, - {file = "grpcio_status-1.62.3-py3-none-any.whl", hash = "sha256:f9049b762ba8de6b1086789d8315846e094edac2c50beaf462338b301a8fd4b8"}, + {file = "grpcio_status-1.75.1-py3-none-any.whl", hash = "sha256:f681b301be26dcf7abf5c765d4a22e4098765e1a65cbdfa3efca384edf8e4e3c"}, + {file = "grpcio_status-1.75.1.tar.gz", hash = "sha256:8162afa21833a2085c91089cc395ad880fac1378a1d60233d976649ed724cbf8"}, ] [package.dependencies] googleapis-common-protos = ">=1.5.5" -grpcio = ">=1.62.3" -protobuf = ">=4.21.6" +grpcio = ">=1.75.1" +protobuf = ">=6.31.1,<7.0.0" [[package]] name = "idna" @@ -601,16 +810,28 @@ files = [ [package.extras] all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] +[[package]] +name = "iniconfig" +version = "2.1.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760"}, + {file = "iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"}, +] + [[package]] name = "minio" -version = "7.2.14" +version = "7.2.18" description = "MinIO Python SDK for Amazon S3 Compatible Cloud Storage" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "minio-7.2.14-py3-none-any.whl", hash = "sha256:868dfe907e1702ce4bec86df1f3ced577a73ca85f344ef898d94fe2b5237f8c1"}, - {file = "minio-7.2.14.tar.gz", hash = "sha256:f5c24bf236fefd2edc567cd4455dc49a11ad8ff7ac984bb031b849d82f01222a"}, + {file = "minio-7.2.18-py3-none-any.whl", hash = "sha256:f23a6edbff8d0bc4b5c1a61b2628a01c5a3342aefc613ff9c276012e6321108f"}, + {file = "minio-7.2.18.tar.gz", hash = "sha256:173402a5716099159c5659f9de75be204ebe248557b9f1cc9cf45aa70e9d3024"}, ] [package.dependencies] @@ -622,287 +843,460 @@ urllib3 = "*" [[package]] name = "multidict" -version = "6.1.0" +version = "6.7.0" description = "multidict implementation" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["main"] files = [ - {file = "multidict-6.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3380252550e372e8511d49481bd836264c009adb826b23fefcc5dd3c69692f60"}, - {file = "multidict-6.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:99f826cbf970077383d7de805c0681799491cb939c25450b9b5b3ced03ca99f1"}, - {file = "multidict-6.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a114d03b938376557927ab23f1e950827c3b893ccb94b62fd95d430fd0e5cf53"}, - {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1c416351ee6271b2f49b56ad7f308072f6f44b37118d69c2cad94f3fa8a40d5"}, - {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6b5d83030255983181005e6cfbac1617ce9746b219bc2aad52201ad121226581"}, - {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3e97b5e938051226dc025ec80980c285b053ffb1e25a3db2a3aa3bc046bf7f56"}, - {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d618649d4e70ac6efcbba75be98b26ef5078faad23592f9b51ca492953012429"}, - {file = "multidict-6.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10524ebd769727ac77ef2278390fb0068d83f3acb7773792a5080f2b0abf7748"}, - {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ff3827aef427c89a25cc96ded1759271a93603aba9fb977a6d264648ebf989db"}, - {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:06809f4f0f7ab7ea2cabf9caca7d79c22c0758b58a71f9d32943ae13c7ace056"}, - {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f179dee3b863ab1c59580ff60f9d99f632f34ccb38bf67a33ec6b3ecadd0fd76"}, - {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:aaed8b0562be4a0876ee3b6946f6869b7bcdb571a5d1496683505944e268b160"}, - {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3c8b88a2ccf5493b6c8da9076fb151ba106960a2df90c2633f342f120751a9e7"}, - {file = "multidict-6.1.0-cp310-cp310-win32.whl", hash = "sha256:4a9cb68166a34117d6646c0023c7b759bf197bee5ad4272f420a0141d7eb03a0"}, - {file = "multidict-6.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:20b9b5fbe0b88d0bdef2012ef7dee867f874b72528cf1d08f1d59b0e3850129d"}, - {file = "multidict-6.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3efe2c2cb5763f2f1b275ad2bf7a287d3f7ebbef35648a9726e3b69284a4f3d6"}, - {file = "multidict-6.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c7053d3b0353a8b9de430a4f4b4268ac9a4fb3481af37dfe49825bf45ca24156"}, - {file = "multidict-6.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:27e5fc84ccef8dfaabb09d82b7d179c7cf1a3fbc8a966f8274fcb4ab2eb4cadb"}, - {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e2b90b43e696f25c62656389d32236e049568b39320e2735d51f08fd362761b"}, - {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d83a047959d38a7ff552ff94be767b7fd79b831ad1cd9920662db05fec24fe72"}, - {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d1a9dd711d0877a1ece3d2e4fea11a8e75741ca21954c919406b44e7cf971304"}, - {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec2abea24d98246b94913b76a125e855eb5c434f7c46546046372fe60f666351"}, - {file = "multidict-6.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4867cafcbc6585e4b678876c489b9273b13e9fff9f6d6d66add5e15d11d926cb"}, - {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5b48204e8d955c47c55b72779802b219a39acc3ee3d0116d5080c388970b76e3"}, - {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:d8fff389528cad1618fb4b26b95550327495462cd745d879a8c7c2115248e399"}, - {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a7a9541cd308eed5e30318430a9c74d2132e9a8cb46b901326272d780bf2d423"}, - {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:da1758c76f50c39a2efd5e9859ce7d776317eb1dd34317c8152ac9251fc574a3"}, - {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c943a53e9186688b45b323602298ab727d8865d8c9ee0b17f8d62d14b56f0753"}, - {file = "multidict-6.1.0-cp311-cp311-win32.whl", hash = "sha256:90f8717cb649eea3504091e640a1b8568faad18bd4b9fcd692853a04475a4b80"}, - {file = "multidict-6.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:82176036e65644a6cc5bd619f65f6f19781e8ec2e5330f51aa9ada7504cc1926"}, - {file = "multidict-6.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b04772ed465fa3cc947db808fa306d79b43e896beb677a56fb2347ca1a49c1fa"}, - {file = "multidict-6.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6180c0ae073bddeb5a97a38c03f30c233e0a4d39cd86166251617d1bbd0af436"}, - {file = "multidict-6.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:071120490b47aa997cca00666923a83f02c7fbb44f71cf7f136df753f7fa8761"}, - {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50b3a2710631848991d0bf7de077502e8994c804bb805aeb2925a981de58ec2e"}, - {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b58c621844d55e71c1b7f7c498ce5aa6985d743a1a59034c57a905b3f153c1ef"}, - {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55b6d90641869892caa9ca42ff913f7ff1c5ece06474fbd32fb2cf6834726c95"}, - {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b820514bfc0b98a30e3d85462084779900347e4d49267f747ff54060cc33925"}, - {file = "multidict-6.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10a9b09aba0c5b48c53761b7c720aaaf7cf236d5fe394cd399c7ba662d5f9966"}, - {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1e16bf3e5fc9f44632affb159d30a437bfe286ce9e02754759be5536b169b305"}, - {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:76f364861c3bfc98cbbcbd402d83454ed9e01a5224bb3a28bf70002a230f73e2"}, - {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:820c661588bd01a0aa62a1283f20d2be4281b086f80dad9e955e690c75fb54a2"}, - {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:0e5f362e895bc5b9e67fe6e4ded2492d8124bdf817827f33c5b46c2fe3ffaca6"}, - {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3ec660d19bbc671e3a6443325f07263be452c453ac9e512f5eb935e7d4ac28b3"}, - {file = "multidict-6.1.0-cp312-cp312-win32.whl", hash = "sha256:58130ecf8f7b8112cdb841486404f1282b9c86ccb30d3519faf301b2e5659133"}, - {file = "multidict-6.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:188215fc0aafb8e03341995e7c4797860181562380f81ed0a87ff455b70bf1f1"}, - {file = "multidict-6.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d569388c381b24671589335a3be6e1d45546c2988c2ebe30fdcada8457a31008"}, - {file = "multidict-6.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:052e10d2d37810b99cc170b785945421141bf7bb7d2f8799d431e7db229c385f"}, - {file = "multidict-6.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f90c822a402cb865e396a504f9fc8173ef34212a342d92e362ca498cad308e28"}, - {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b225d95519a5bf73860323e633a664b0d85ad3d5bede6d30d95b35d4dfe8805b"}, - {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:23bfd518810af7de1116313ebd9092cb9aa629beb12f6ed631ad53356ed6b86c"}, - {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c09fcfdccdd0b57867577b719c69e347a436b86cd83747f179dbf0cc0d4c1f3"}, - {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf6bea52ec97e95560af5ae576bdac3aa3aae0b6758c6efa115236d9e07dae44"}, - {file = "multidict-6.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57feec87371dbb3520da6192213c7d6fc892d5589a93db548331954de8248fd2"}, - {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0c3f390dc53279cbc8ba976e5f8035eab997829066756d811616b652b00a23a3"}, - {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:59bfeae4b25ec05b34f1956eaa1cb38032282cd4dfabc5056d0a1ec4d696d3aa"}, - {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:b2f59caeaf7632cc633b5cf6fc449372b83bbdf0da4ae04d5be36118e46cc0aa"}, - {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:37bb93b2178e02b7b618893990941900fd25b6b9ac0fa49931a40aecdf083fe4"}, - {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4e9f48f58c2c523d5a06faea47866cd35b32655c46b443f163d08c6d0ddb17d6"}, - {file = "multidict-6.1.0-cp313-cp313-win32.whl", hash = "sha256:3a37ffb35399029b45c6cc33640a92bef403c9fd388acce75cdc88f58bd19a81"}, - {file = "multidict-6.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:e9aa71e15d9d9beaad2c6b9319edcdc0a49a43ef5c0a4c8265ca9ee7d6c67774"}, - {file = "multidict-6.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:db7457bac39421addd0c8449933ac32d8042aae84a14911a757ae6ca3eef1392"}, - {file = "multidict-6.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d094ddec350a2fb899fec68d8353c78233debde9b7d8b4beeafa70825f1c281a"}, - {file = "multidict-6.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5845c1fd4866bb5dd3125d89b90e57ed3138241540897de748cdf19de8a2fca2"}, - {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9079dfc6a70abe341f521f78405b8949f96db48da98aeb43f9907f342f627cdc"}, - {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3914f5aaa0f36d5d60e8ece6a308ee1c9784cd75ec8151062614657a114c4478"}, - {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c08be4f460903e5a9d0f76818db3250f12e9c344e79314d1d570fc69d7f4eae4"}, - {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d093be959277cb7dee84b801eb1af388b6ad3ca6a6b6bf1ed7585895789d027d"}, - {file = "multidict-6.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3702ea6872c5a2a4eeefa6ffd36b042e9773f05b1f37ae3ef7264b1163c2dcf6"}, - {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:2090f6a85cafc5b2db085124d752757c9d251548cedabe9bd31afe6363e0aff2"}, - {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:f67f217af4b1ff66c68a87318012de788dd95fcfeb24cc889011f4e1c7454dfd"}, - {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:189f652a87e876098bbc67b4da1049afb5f5dfbaa310dd67c594b01c10388db6"}, - {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:6bb5992037f7a9eff7991ebe4273ea7f51f1c1c511e6a2ce511d0e7bdb754492"}, - {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:ac10f4c2b9e770c4e393876e35a7046879d195cd123b4f116d299d442b335bcd"}, - {file = "multidict-6.1.0-cp38-cp38-win32.whl", hash = "sha256:e27bbb6d14416713a8bd7aaa1313c0fc8d44ee48d74497a0ff4c3a1b6ccb5167"}, - {file = "multidict-6.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:22f3105d4fb15c8f57ff3959a58fcab6ce36814486500cd7485651230ad4d4ef"}, - {file = "multidict-6.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:4e18b656c5e844539d506a0a06432274d7bd52a7487e6828c63a63d69185626c"}, - {file = "multidict-6.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a185f876e69897a6f3325c3f19f26a297fa058c5e456bfcff8015e9a27e83ae1"}, - {file = "multidict-6.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ab7c4ceb38d91570a650dba194e1ca87c2b543488fe9309b4212694174fd539c"}, - {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e617fb6b0b6953fffd762669610c1c4ffd05632c138d61ac7e14ad187870669c"}, - {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:16e5f4bf4e603eb1fdd5d8180f1a25f30056f22e55ce51fb3d6ad4ab29f7d96f"}, - {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f4c035da3f544b1882bac24115f3e2e8760f10a0107614fc9839fd232200b875"}, - {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:957cf8e4b6e123a9eea554fa7ebc85674674b713551de587eb318a2df3e00255"}, - {file = "multidict-6.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:483a6aea59cb89904e1ceabd2b47368b5600fb7de78a6e4a2c2987b2d256cf30"}, - {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:87701f25a2352e5bf7454caa64757642734da9f6b11384c1f9d1a8e699758057"}, - {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:682b987361e5fd7a139ed565e30d81fd81e9629acc7d925a205366877d8c8657"}, - {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ce2186a7df133a9c895dea3331ddc5ddad42cdd0d1ea2f0a51e5d161e4762f28"}, - {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:9f636b730f7e8cb19feb87094949ba54ee5357440b9658b2a32a5ce4bce53972"}, - {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:73eae06aa53af2ea5270cc066dcaf02cc60d2994bbb2c4ef5764949257d10f43"}, - {file = "multidict-6.1.0-cp39-cp39-win32.whl", hash = "sha256:1ca0083e80e791cffc6efce7660ad24af66c8d4079d2a750b29001b53ff59ada"}, - {file = "multidict-6.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:aa466da5b15ccea564bdab9c89175c762bc12825f4659c11227f515cee76fa4a"}, - {file = "multidict-6.1.0-py3-none-any.whl", hash = "sha256:48e171e52d1c4d33888e529b999e5900356b9ae588c2f09a52dcefb158b27506"}, - {file = "multidict-6.1.0.tar.gz", hash = "sha256:22ae2ebf9b0c69d206c003e2f6a914ea33f0a932d4aa16f236afc049d9958f4a"}, + {file = "multidict-6.7.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:9f474ad5acda359c8758c8accc22032c6abe6dc87a8be2440d097785e27a9349"}, + {file = "multidict-6.7.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4b7a9db5a870f780220e931d0002bbfd88fb53aceb6293251e2c839415c1b20e"}, + {file = "multidict-6.7.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:03ca744319864e92721195fa28c7a3b2bc7b686246b35e4078c1e4d0eb5466d3"}, + {file = "multidict-6.7.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f0e77e3c0008bc9316e662624535b88d360c3a5d3f81e15cf12c139a75250046"}, + {file = "multidict-6.7.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:08325c9e5367aa379a3496aa9a022fe8837ff22e00b94db256d3a1378c76ab32"}, + {file = "multidict-6.7.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e2862408c99f84aa571ab462d25236ef9cb12a602ea959ba9c9009a54902fc73"}, + {file = "multidict-6.7.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4d72a9a2d885f5c208b0cb91ff2ed43636bb7e345ec839ff64708e04f69a13cc"}, + {file = "multidict-6.7.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:478cc36476687bac1514d651cbbaa94b86b0732fb6855c60c673794c7dd2da62"}, + {file = "multidict-6.7.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6843b28b0364dc605f21481c90fadb5f60d9123b442eb8a726bb74feef588a84"}, + {file = "multidict-6.7.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:23bfeee5316266e5ee2d625df2d2c602b829435fc3a235c2ba2131495706e4a0"}, + {file = "multidict-6.7.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:680878b9f3d45c31e1f730eef731f9b0bc1da456155688c6745ee84eb818e90e"}, + {file = "multidict-6.7.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:eb866162ef2f45063acc7a53a88ef6fe8bf121d45c30ea3c9cd87ce7e191a8d4"}, + {file = "multidict-6.7.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:df0e3bf7993bdbeca5ac25aa859cf40d39019e015c9c91809ba7093967f7a648"}, + {file = "multidict-6.7.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:661709cdcd919a2ece2234f9bae7174e5220c80b034585d7d8a755632d3e2111"}, + {file = "multidict-6.7.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:096f52730c3fb8ed419db2d44391932b63891b2c5ed14850a7e215c0ba9ade36"}, + {file = "multidict-6.7.0-cp310-cp310-win32.whl", hash = "sha256:afa8a2978ec65d2336305550535c9c4ff50ee527914328c8677b3973ade52b85"}, + {file = "multidict-6.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:b15b3afff74f707b9275d5ba6a91ae8f6429c3ffb29bbfd216b0b375a56f13d7"}, + {file = "multidict-6.7.0-cp310-cp310-win_arm64.whl", hash = "sha256:4b73189894398d59131a66ff157837b1fafea9974be486d036bb3d32331fdbf0"}, + {file = "multidict-6.7.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4d409aa42a94c0b3fa617708ef5276dfe81012ba6753a0370fcc9d0195d0a1fc"}, + {file = "multidict-6.7.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:14c9e076eede3b54c636f8ce1c9c252b5f057c62131211f0ceeec273810c9721"}, + {file = "multidict-6.7.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4c09703000a9d0fa3c3404b27041e574cc7f4df4c6563873246d0e11812a94b6"}, + {file = "multidict-6.7.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:a265acbb7bb33a3a2d626afbe756371dce0279e7b17f4f4eda406459c2b5ff1c"}, + {file = "multidict-6.7.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:51cb455de290ae462593e5b1cb1118c5c22ea7f0d3620d9940bf695cea5a4bd7"}, + {file = "multidict-6.7.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:db99677b4457c7a5c5a949353e125ba72d62b35f74e26da141530fbb012218a7"}, + {file = "multidict-6.7.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f470f68adc395e0183b92a2f4689264d1ea4b40504a24d9882c27375e6662bb9"}, + {file = "multidict-6.7.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0db4956f82723cc1c270de9c6e799b4c341d327762ec78ef82bb962f79cc07d8"}, + {file = "multidict-6.7.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3e56d780c238f9e1ae66a22d2adf8d16f485381878250db8d496623cd38b22bd"}, + {file = "multidict-6.7.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9d14baca2ee12c1a64740d4531356ba50b82543017f3ad6de0deb943c5979abb"}, + {file = "multidict-6.7.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:295a92a76188917c7f99cda95858c822f9e4aae5824246bba9b6b44004ddd0a6"}, + {file = "multidict-6.7.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:39f1719f57adbb767ef592a50ae5ebb794220d1188f9ca93de471336401c34d2"}, + {file = "multidict-6.7.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:0a13fb8e748dfc94749f622de065dd5c1def7e0d2216dba72b1d8069a389c6ff"}, + {file = "multidict-6.7.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e3aa16de190d29a0ea1b48253c57d99a68492c8dd8948638073ab9e74dc9410b"}, + {file = "multidict-6.7.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a048ce45dcdaaf1defb76b2e684f997fb5abf74437b6cb7b22ddad934a964e34"}, + {file = "multidict-6.7.0-cp311-cp311-win32.whl", hash = "sha256:a90af66facec4cebe4181b9e62a68be65e45ac9b52b67de9eec118701856e7ff"}, + {file = "multidict-6.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:95b5ffa4349df2887518bb839409bcf22caa72d82beec453216802f475b23c81"}, + {file = "multidict-6.7.0-cp311-cp311-win_arm64.whl", hash = "sha256:329aa225b085b6f004a4955271a7ba9f1087e39dcb7e65f6284a988264a63912"}, + {file = "multidict-6.7.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8a3862568a36d26e650a19bb5cbbba14b71789032aebc0423f8cc5f150730184"}, + {file = "multidict-6.7.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:960c60b5849b9b4f9dcc9bea6e3626143c252c74113df2c1540aebce70209b45"}, + {file = "multidict-6.7.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2049be98fb57a31b4ccf870bf377af2504d4ae35646a19037ec271e4c07998aa"}, + {file = "multidict-6.7.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0934f3843a1860dd465d38895c17fce1f1cb37295149ab05cd1b9a03afacb2a7"}, + {file = "multidict-6.7.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b3e34f3a1b8131ba06f1a73adab24f30934d148afcd5f5de9a73565a4404384e"}, + {file = "multidict-6.7.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:efbb54e98446892590dc2458c19c10344ee9a883a79b5cec4bc34d6656e8d546"}, + {file = "multidict-6.7.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a35c5fc61d4f51eb045061e7967cfe3123d622cd500e8868e7c0c592a09fedc4"}, + {file = "multidict-6.7.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29fe6740ebccba4175af1b9b87bf553e9c15cd5868ee967e010efcf94e4fd0f1"}, + {file = "multidict-6.7.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:123e2a72e20537add2f33a79e605f6191fba2afda4cbb876e35c1a7074298a7d"}, + {file = "multidict-6.7.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b284e319754366c1aee2267a2036248b24eeb17ecd5dc16022095e747f2f4304"}, + {file = "multidict-6.7.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:803d685de7be4303b5a657b76e2f6d1240e7e0a8aa2968ad5811fa2285553a12"}, + {file = "multidict-6.7.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c04a328260dfd5db8c39538f999f02779012268f54614902d0afc775d44e0a62"}, + {file = "multidict-6.7.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8a19cdb57cd3df4cd865849d93ee14920fb97224300c88501f16ecfa2604b4e0"}, + {file = "multidict-6.7.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9b2fd74c52accced7e75de26023b7dccee62511a600e62311b918ec5c168fc2a"}, + {file = "multidict-6.7.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3e8bfdd0e487acf992407a140d2589fe598238eaeffa3da8448d63a63cd363f8"}, + {file = "multidict-6.7.0-cp312-cp312-win32.whl", hash = "sha256:dd32a49400a2c3d52088e120ee00c1e3576cbff7e10b98467962c74fdb762ed4"}, + {file = "multidict-6.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:92abb658ef2d7ef22ac9f8bb88e8b6c3e571671534e029359b6d9e845923eb1b"}, + {file = "multidict-6.7.0-cp312-cp312-win_arm64.whl", hash = "sha256:490dab541a6a642ce1a9d61a4781656b346a55c13038f0b1244653828e3a83ec"}, + {file = "multidict-6.7.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:bee7c0588aa0076ce77c0ea5d19a68d76ad81fcd9fe8501003b9a24f9d4000f6"}, + {file = "multidict-6.7.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7ef6b61cad77091056ce0e7ce69814ef72afacb150b7ac6a3e9470def2198159"}, + {file = "multidict-6.7.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9c0359b1ec12b1d6849c59f9d319610b7f20ef990a6d454ab151aa0e3b9f78ca"}, + {file = "multidict-6.7.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cd240939f71c64bd658f186330603aac1a9a81bf6273f523fca63673cb7378a8"}, + {file = "multidict-6.7.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a60a4d75718a5efa473ebd5ab685786ba0c67b8381f781d1be14da49f1a2dc60"}, + {file = "multidict-6.7.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:53a42d364f323275126aff81fb67c5ca1b7a04fda0546245730a55c8c5f24bc4"}, + {file = "multidict-6.7.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3b29b980d0ddbecb736735ee5bef69bb2ddca56eff603c86f3f29a1128299b4f"}, + {file = "multidict-6.7.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f8a93b1c0ed2d04b97a5e9336fd2d33371b9a6e29ab7dd6503d63407c20ffbaf"}, + {file = "multidict-6.7.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9ff96e8815eecacc6645da76c413eb3b3d34cfca256c70b16b286a687d013c32"}, + {file = "multidict-6.7.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7516c579652f6a6be0e266aec0acd0db80829ca305c3d771ed898538804c2036"}, + {file = "multidict-6.7.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:040f393368e63fb0f3330e70c26bfd336656bed925e5cbe17c9da839a6ab13ec"}, + {file = "multidict-6.7.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b3bc26a951007b1057a1c543af845f1c7e3e71cc240ed1ace7bf4484aa99196e"}, + {file = "multidict-6.7.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:7b022717c748dd1992a83e219587aabe45980d88969f01b316e78683e6285f64"}, + {file = "multidict-6.7.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:9600082733859f00d79dee64effc7aef1beb26adb297416a4ad2116fd61374bd"}, + {file = "multidict-6.7.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:94218fcec4d72bc61df51c198d098ce2b378e0ccbac41ddbed5ef44092913288"}, + {file = "multidict-6.7.0-cp313-cp313-win32.whl", hash = "sha256:a37bd74c3fa9d00be2d7b8eca074dc56bd8077ddd2917a839bd989612671ed17"}, + {file = "multidict-6.7.0-cp313-cp313-win_amd64.whl", hash = "sha256:30d193c6cc6d559db42b6bcec8a5d395d34d60c9877a0b71ecd7c204fcf15390"}, + {file = "multidict-6.7.0-cp313-cp313-win_arm64.whl", hash = "sha256:ea3334cabe4d41b7ccd01e4d349828678794edbc2d3ae97fc162a3312095092e"}, + {file = "multidict-6.7.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:ad9ce259f50abd98a1ca0aa6e490b58c316a0fce0617f609723e40804add2c00"}, + {file = "multidict-6.7.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:07f5594ac6d084cbb5de2df218d78baf55ef150b91f0ff8a21cc7a2e3a5a58eb"}, + {file = "multidict-6.7.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:0591b48acf279821a579282444814a2d8d0af624ae0bc600aa4d1b920b6e924b"}, + {file = "multidict-6.7.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:749a72584761531d2b9467cfbdfd29487ee21124c304c4b6cb760d8777b27f9c"}, + {file = "multidict-6.7.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b4c3d199f953acd5b446bf7c0de1fe25d94e09e79086f8dc2f48a11a129cdf1"}, + {file = "multidict-6.7.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:9fb0211dfc3b51efea2f349ec92c114d7754dd62c01f81c3e32b765b70c45c9b"}, + {file = "multidict-6.7.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a027ec240fe73a8d6281872690b988eed307cd7d91b23998ff35ff577ca688b5"}, + {file = "multidict-6.7.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1d964afecdf3a8288789df2f5751dc0a8261138c3768d9af117ed384e538fad"}, + {file = "multidict-6.7.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:caf53b15b1b7df9fbd0709aa01409000a2b4dd03a5f6f5cc548183c7c8f8b63c"}, + {file = "multidict-6.7.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:654030da3197d927f05a536a66186070e98765aa5142794c9904555d3a9d8fb5"}, + {file = "multidict-6.7.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:2090d3718829d1e484706a2f525e50c892237b2bf9b17a79b059cb98cddc2f10"}, + {file = "multidict-6.7.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:2d2cfeec3f6f45651b3d408c4acec0ebf3daa9bc8a112a084206f5db5d05b754"}, + {file = "multidict-6.7.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:4ef089f985b8c194d341eb2c24ae6e7408c9a0e2e5658699c92f497437d88c3c"}, + {file = "multidict-6.7.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e93a0617cd16998784bf4414c7e40f17a35d2350e5c6f0bd900d3a8e02bd3762"}, + {file = "multidict-6.7.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f0feece2ef8ebc42ed9e2e8c78fc4aa3cf455733b507c09ef7406364c94376c6"}, + {file = "multidict-6.7.0-cp313-cp313t-win32.whl", hash = "sha256:19a1d55338ec1be74ef62440ca9e04a2f001a04d0cc49a4983dc320ff0f3212d"}, + {file = "multidict-6.7.0-cp313-cp313t-win_amd64.whl", hash = "sha256:3da4fb467498df97e986af166b12d01f05d2e04f978a9c1c680ea1988e0bc4b6"}, + {file = "multidict-6.7.0-cp313-cp313t-win_arm64.whl", hash = "sha256:b4121773c49a0776461f4a904cdf6264c88e42218aaa8407e803ca8025872792"}, + {file = "multidict-6.7.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3bab1e4aff7adaa34410f93b1f8e57c4b36b9af0426a76003f441ee1d3c7e842"}, + {file = "multidict-6.7.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:b8512bac933afc3e45fb2b18da8e59b78d4f408399a960339598374d4ae3b56b"}, + {file = "multidict-6.7.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:79dcf9e477bc65414ebfea98ffd013cb39552b5ecd62908752e0e413d6d06e38"}, + {file = "multidict-6.7.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:31bae522710064b5cbeddaf2e9f32b1abab70ac6ac91d42572502299e9953128"}, + {file = "multidict-6.7.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a0df7ff02397bb63e2fd22af2c87dfa39e8c7f12947bc524dbdc528282c7e34"}, + {file = "multidict-6.7.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7a0222514e8e4c514660e182d5156a415c13ef0aabbd71682fc714e327b95e99"}, + {file = "multidict-6.7.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2397ab4daaf2698eb51a76721e98db21ce4f52339e535725de03ea962b5a3202"}, + {file = "multidict-6.7.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8891681594162635948a636c9fe0ff21746aeb3dd5463f6e25d9bea3a8a39ca1"}, + {file = "multidict-6.7.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:18706cc31dbf402a7945916dd5cddf160251b6dab8a2c5f3d6d5a55949f676b3"}, + {file = "multidict-6.7.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f844a1bbf1d207dd311a56f383f7eda2d0e134921d45751842d8235e7778965d"}, + {file = "multidict-6.7.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:d4393e3581e84e5645506923816b9cc81f5609a778c7e7534054091acc64d1c6"}, + {file = "multidict-6.7.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:fbd18dc82d7bf274b37aa48d664534330af744e03bccf696d6f4c6042e7d19e7"}, + {file = "multidict-6.7.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:b6234e14f9314731ec45c42fc4554b88133ad53a09092cc48a88e771c125dadb"}, + {file = "multidict-6.7.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:08d4379f9744d8f78d98c8673c06e202ffa88296f009c71bbafe8a6bf847d01f"}, + {file = "multidict-6.7.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:9fe04da3f79387f450fd0061d4dd2e45a72749d31bf634aecc9e27f24fdc4b3f"}, + {file = "multidict-6.7.0-cp314-cp314-win32.whl", hash = "sha256:fbafe31d191dfa7c4c51f7a6149c9fb7e914dcf9ffead27dcfd9f1ae382b3885"}, + {file = "multidict-6.7.0-cp314-cp314-win_amd64.whl", hash = "sha256:2f67396ec0310764b9222a1728ced1ab638f61aadc6226f17a71dd9324f9a99c"}, + {file = "multidict-6.7.0-cp314-cp314-win_arm64.whl", hash = "sha256:ba672b26069957ee369cfa7fc180dde1fc6f176eaf1e6beaf61fbebbd3d9c000"}, + {file = "multidict-6.7.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:c1dcc7524066fa918c6a27d61444d4ee7900ec635779058571f70d042d86ed63"}, + {file = "multidict-6.7.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:27e0b36c2d388dc7b6ced3406671b401e84ad7eb0656b8f3a2f46ed0ce483718"}, + {file = "multidict-6.7.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2a7baa46a22e77f0988e3b23d4ede5513ebec1929e34ee9495be535662c0dfe2"}, + {file = "multidict-6.7.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7bf77f54997a9166a2f5675d1201520586439424c2511723a7312bdb4bcc034e"}, + {file = "multidict-6.7.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e011555abada53f1578d63389610ac8a5400fc70ce71156b0aa30d326f1a5064"}, + {file = "multidict-6.7.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:28b37063541b897fd6a318007373930a75ca6d6ac7c940dbe14731ffdd8d498e"}, + {file = "multidict-6.7.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:05047ada7a2fde2631a0ed706f1fd68b169a681dfe5e4cf0f8e4cb6618bbc2cd"}, + {file = "multidict-6.7.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:716133f7d1d946a4e1b91b1756b23c088881e70ff180c24e864c26192ad7534a"}, + {file = "multidict-6.7.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d1bed1b467ef657f2a0ae62844a607909ef1c6889562de5e1d505f74457d0b96"}, + {file = "multidict-6.7.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:ca43bdfa5d37bd6aee89d85e1d0831fb86e25541be7e9d376ead1b28974f8e5e"}, + {file = "multidict-6.7.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:44b546bd3eb645fd26fb949e43c02a25a2e632e2ca21a35e2e132c8105dc8599"}, + {file = "multidict-6.7.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:a6ef16328011d3f468e7ebc326f24c1445f001ca1dec335b2f8e66bed3006394"}, + {file = "multidict-6.7.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:5aa873cbc8e593d361ae65c68f85faadd755c3295ea2c12040ee146802f23b38"}, + {file = "multidict-6.7.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:3d7b6ccce016e29df4b7ca819659f516f0bc7a4b3efa3bb2012ba06431b044f9"}, + {file = "multidict-6.7.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:171b73bd4ee683d307599b66793ac80981b06f069b62eea1c9e29c9241aa66b0"}, + {file = "multidict-6.7.0-cp314-cp314t-win32.whl", hash = "sha256:b2d7f80c4e1fd010b07cb26820aae86b7e73b681ee4889684fb8d2d4537aab13"}, + {file = "multidict-6.7.0-cp314-cp314t-win_amd64.whl", hash = "sha256:09929cab6fcb68122776d575e03c6cc64ee0b8fca48d17e135474b042ce515cd"}, + {file = "multidict-6.7.0-cp314-cp314t-win_arm64.whl", hash = "sha256:cc41db090ed742f32bd2d2c721861725e6109681eddf835d0a82bd3a5c382827"}, + {file = "multidict-6.7.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:363eb68a0a59bd2303216d2346e6c441ba10d36d1f9969fcb6f1ba700de7bb5c"}, + {file = "multidict-6.7.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d874eb056410ca05fed180b6642e680373688efafc7f077b2a2f61811e873a40"}, + {file = "multidict-6.7.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8b55d5497b51afdfde55925e04a022f1de14d4f4f25cdfd4f5d9b0aa96166851"}, + {file = "multidict-6.7.0-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f8e5c0031b90ca9ce555e2e8fd5c3b02a25f14989cbc310701823832c99eb687"}, + {file = "multidict-6.7.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9cf41880c991716f3c7cec48e2f19ae4045fc9db5fc9cff27347ada24d710bb5"}, + {file = "multidict-6.7.0-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8cfc12a8630a29d601f48d47787bd7eb730e475e83edb5d6c5084317463373eb"}, + {file = "multidict-6.7.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3996b50c3237c4aec17459217c1e7bbdead9a22a0fcd3c365564fbd16439dde6"}, + {file = "multidict-6.7.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7f5170993a0dd3ab871c74f45c0a21a4e2c37a2f2b01b5f722a2ad9c6650469e"}, + {file = "multidict-6.7.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ec81878ddf0e98817def1e77d4f50dae5ef5b0e4fe796fae3bd674304172416e"}, + {file = "multidict-6.7.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:9281bf5b34f59afbc6b1e477a372e9526b66ca446f4bf62592839c195a718b32"}, + {file = "multidict-6.7.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:68af405971779d8b37198726f2b6fe3955db846fee42db7a4286fc542203934c"}, + {file = "multidict-6.7.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3ba3ef510467abb0667421a286dc906e30eb08569365f5cdb131d7aff7c2dd84"}, + {file = "multidict-6.7.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:b61189b29081a20c7e4e0b49b44d5d44bb0dc92be3c6d06a11cc043f81bf9329"}, + {file = "multidict-6.7.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:fb287618b9c7aa3bf8d825f02d9201b2f13078a5ed3b293c8f4d953917d84d5e"}, + {file = "multidict-6.7.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:521f33e377ff64b96c4c556b81c55d0cfffb96a11c194fd0c3f1e56f3d8dd5a4"}, + {file = "multidict-6.7.0-cp39-cp39-win32.whl", hash = "sha256:ce8fdc2dca699f8dbf055a61d73eaa10482569ad20ee3c36ef9641f69afa8c91"}, + {file = "multidict-6.7.0-cp39-cp39-win_amd64.whl", hash = "sha256:7e73299c99939f089dd9b2120a04a516b95cdf8c1cd2b18c53ebf0de80b1f18f"}, + {file = "multidict-6.7.0-cp39-cp39-win_arm64.whl", hash = "sha256:6bdce131e14b04fd34a809b6380dbfd826065c3e2fe8a50dbae659fa0c390546"}, + {file = "multidict-6.7.0-py3-none-any.whl", hash = "sha256:394fc5c42a333c9ffc3e421a4c85e08580d990e08b99f6bf35b4132114c5dcb3"}, + {file = "multidict-6.7.0.tar.gz", hash = "sha256:c6e99d9a65ca282e578dfea819cfa9c0a62b2499d8677392e09feaf305e9e6f5"}, ] +[[package]] +name = "packaging" +version = "25.0" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"}, + {file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"}, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746"}, + {file = "pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["coverage", "pytest", "pytest-benchmark"] + [[package]] name = "propcache" -version = "0.2.1" +version = "0.4.1" description = "Accelerated property cache" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "propcache-0.2.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6b3f39a85d671436ee3d12c017f8fdea38509e4f25b28eb25877293c98c243f6"}, - {file = "propcache-0.2.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d51fbe4285d5db5d92a929e3e21536ea3dd43732c5b177c7ef03f918dff9f2"}, - {file = "propcache-0.2.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6445804cf4ec763dc70de65a3b0d9954e868609e83850a47ca4f0cb64bd79fea"}, - {file = "propcache-0.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f9479aa06a793c5aeba49ce5c5692ffb51fcd9a7016e017d555d5e2b0045d212"}, - {file = "propcache-0.2.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d9631c5e8b5b3a0fda99cb0d29c18133bca1e18aea9effe55adb3da1adef80d3"}, - {file = "propcache-0.2.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3156628250f46a0895f1f36e1d4fbe062a1af8718ec3ebeb746f1d23f0c5dc4d"}, - {file = "propcache-0.2.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b6fb63ae352e13748289f04f37868099e69dba4c2b3e271c46061e82c745634"}, - {file = "propcache-0.2.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:887d9b0a65404929641a9fabb6452b07fe4572b269d901d622d8a34a4e9043b2"}, - {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a96dc1fa45bd8c407a0af03b2d5218392729e1822b0c32e62c5bf7eeb5fb3958"}, - {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:a7e65eb5c003a303b94aa2c3852ef130230ec79e349632d030e9571b87c4698c"}, - {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:999779addc413181912e984b942fbcc951be1f5b3663cd80b2687758f434c583"}, - {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:19a0f89a7bb9d8048d9c4370c9c543c396e894c76be5525f5e1ad287f1750ddf"}, - {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:1ac2f5fe02fa75f56e1ad473f1175e11f475606ec9bd0be2e78e4734ad575034"}, - {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:574faa3b79e8ebac7cb1d7930f51184ba1ccf69adfdec53a12f319a06030a68b"}, - {file = "propcache-0.2.1-cp310-cp310-win32.whl", hash = "sha256:03ff9d3f665769b2a85e6157ac8b439644f2d7fd17615a82fa55739bc97863f4"}, - {file = "propcache-0.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:2d3af2e79991102678f53e0dbf4c35de99b6b8b58f29a27ca0325816364caaba"}, - {file = "propcache-0.2.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1ffc3cca89bb438fb9c95c13fc874012f7b9466b89328c3c8b1aa93cdcfadd16"}, - {file = "propcache-0.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f174bbd484294ed9fdf09437f889f95807e5f229d5d93588d34e92106fbf6717"}, - {file = "propcache-0.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:70693319e0b8fd35dd863e3e29513875eb15c51945bf32519ef52927ca883bc3"}, - {file = "propcache-0.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b480c6a4e1138e1aa137c0079b9b6305ec6dcc1098a8ca5196283e8a49df95a9"}, - {file = "propcache-0.2.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d27b84d5880f6d8aa9ae3edb253c59d9f6642ffbb2c889b78b60361eed449787"}, - {file = "propcache-0.2.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:857112b22acd417c40fa4595db2fe28ab900c8c5fe4670c7989b1c0230955465"}, - {file = "propcache-0.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf6c4150f8c0e32d241436526f3c3f9cbd34429492abddbada2ffcff506c51af"}, - {file = "propcache-0.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:66d4cfda1d8ed687daa4bc0274fcfd5267873db9a5bc0418c2da19273040eeb7"}, - {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c2f992c07c0fca81655066705beae35fc95a2fa7366467366db627d9f2ee097f"}, - {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:4a571d97dbe66ef38e472703067021b1467025ec85707d57e78711c085984e54"}, - {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:bb6178c241278d5fe853b3de743087be7f5f4c6f7d6d22a3b524d323eecec505"}, - {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:ad1af54a62ffe39cf34db1aa6ed1a1873bd548f6401db39d8e7cd060b9211f82"}, - {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e7048abd75fe40712005bcfc06bb44b9dfcd8e101dda2ecf2f5aa46115ad07ca"}, - {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:160291c60081f23ee43d44b08a7e5fb76681221a8e10b3139618c5a9a291b84e"}, - {file = "propcache-0.2.1-cp311-cp311-win32.whl", hash = "sha256:819ce3b883b7576ca28da3861c7e1a88afd08cc8c96908e08a3f4dd64a228034"}, - {file = "propcache-0.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:edc9fc7051e3350643ad929df55c451899bb9ae6d24998a949d2e4c87fb596d3"}, - {file = "propcache-0.2.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:081a430aa8d5e8876c6909b67bd2d937bfd531b0382d3fdedb82612c618bc41a"}, - {file = "propcache-0.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d2ccec9ac47cf4e04897619c0e0c1a48c54a71bdf045117d3a26f80d38ab1fb0"}, - {file = "propcache-0.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:14d86fe14b7e04fa306e0c43cdbeebe6b2c2156a0c9ce56b815faacc193e320d"}, - {file = "propcache-0.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:049324ee97bb67285b49632132db351b41e77833678432be52bdd0289c0e05e4"}, - {file = "propcache-0.2.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1cd9a1d071158de1cc1c71a26014dcdfa7dd3d5f4f88c298c7f90ad6f27bb46d"}, - {file = "propcache-0.2.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98110aa363f1bb4c073e8dcfaefd3a5cea0f0834c2aab23dda657e4dab2f53b5"}, - {file = "propcache-0.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:647894f5ae99c4cf6bb82a1bb3a796f6e06af3caa3d32e26d2350d0e3e3faf24"}, - {file = "propcache-0.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bfd3223c15bebe26518d58ccf9a39b93948d3dcb3e57a20480dfdd315356baff"}, - {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d71264a80f3fcf512eb4f18f59423fe82d6e346ee97b90625f283df56aee103f"}, - {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:e73091191e4280403bde6c9a52a6999d69cdfde498f1fdf629105247599b57ec"}, - {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3935bfa5fede35fb202c4b569bb9c042f337ca4ff7bd540a0aa5e37131659348"}, - {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f508b0491767bb1f2b87fdfacaba5f7eddc2f867740ec69ece6d1946d29029a6"}, - {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:1672137af7c46662a1c2be1e8dc78cb6d224319aaa40271c9257d886be4363a6"}, - {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b74c261802d3d2b85c9df2dfb2fa81b6f90deeef63c2db9f0e029a3cac50b518"}, - {file = "propcache-0.2.1-cp312-cp312-win32.whl", hash = "sha256:d09c333d36c1409d56a9d29b3a1b800a42c76a57a5a8907eacdbce3f18768246"}, - {file = "propcache-0.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:c214999039d4f2a5b2073ac506bba279945233da8c786e490d411dfc30f855c1"}, - {file = "propcache-0.2.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aca405706e0b0a44cc6bfd41fbe89919a6a56999157f6de7e182a990c36e37bc"}, - {file = "propcache-0.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:12d1083f001ace206fe34b6bdc2cb94be66d57a850866f0b908972f90996b3e9"}, - {file = "propcache-0.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d93f3307ad32a27bda2e88ec81134b823c240aa3abb55821a8da553eed8d9439"}, - {file = "propcache-0.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba278acf14471d36316159c94a802933d10b6a1e117b8554fe0d0d9b75c9d536"}, - {file = "propcache-0.2.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4e6281aedfca15301c41f74d7005e6e3f4ca143584ba696ac69df4f02f40d629"}, - {file = "propcache-0.2.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5b750a8e5a1262434fb1517ddf64b5de58327f1adc3524a5e44c2ca43305eb0b"}, - {file = "propcache-0.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf72af5e0fb40e9babf594308911436c8efde3cb5e75b6f206c34ad18be5c052"}, - {file = "propcache-0.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b2d0a12018b04f4cb820781ec0dffb5f7c7c1d2a5cd22bff7fb055a2cb19ebce"}, - {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e800776a79a5aabdb17dcc2346a7d66d0777e942e4cd251defeb084762ecd17d"}, - {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:4160d9283bd382fa6c0c2b5e017acc95bc183570cd70968b9202ad6d8fc48dce"}, - {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:30b43e74f1359353341a7adb783c8f1b1c676367b011709f466f42fda2045e95"}, - {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:58791550b27d5488b1bb52bc96328456095d96206a250d28d874fafe11b3dfaf"}, - {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:0f022d381747f0dfe27e99d928e31bc51a18b65bb9e481ae0af1380a6725dd1f"}, - {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:297878dc9d0a334358f9b608b56d02e72899f3b8499fc6044133f0d319e2ec30"}, - {file = "propcache-0.2.1-cp313-cp313-win32.whl", hash = "sha256:ddfab44e4489bd79bda09d84c430677fc7f0a4939a73d2bba3073036f487a0a6"}, - {file = "propcache-0.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:556fc6c10989f19a179e4321e5d678db8eb2924131e64652a51fe83e4c3db0e1"}, - {file = "propcache-0.2.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:6a9a8c34fb7bb609419a211e59da8887eeca40d300b5ea8e56af98f6fbbb1541"}, - {file = "propcache-0.2.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ae1aa1cd222c6d205853b3013c69cd04515f9d6ab6de4b0603e2e1c33221303e"}, - {file = "propcache-0.2.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:accb6150ce61c9c4b7738d45550806aa2b71c7668c6942f17b0ac182b6142fd4"}, - {file = "propcache-0.2.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5eee736daafa7af6d0a2dc15cc75e05c64f37fc37bafef2e00d77c14171c2097"}, - {file = "propcache-0.2.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7a31fc1e1bd362874863fdeed71aed92d348f5336fd84f2197ba40c59f061bd"}, - {file = "propcache-0.2.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba4cfa1052819d16699e1d55d18c92b6e094d4517c41dd231a8b9f87b6fa681"}, - {file = "propcache-0.2.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f089118d584e859c62b3da0892b88a83d611c2033ac410e929cb6754eec0ed16"}, - {file = "propcache-0.2.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:781e65134efaf88feb447e8c97a51772aa75e48b794352f94cb7ea717dedda0d"}, - {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:31f5af773530fd3c658b32b6bdc2d0838543de70eb9a2156c03e410f7b0d3aae"}, - {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:a7a078f5d37bee6690959c813977da5291b24286e7b962e62a94cec31aa5188b"}, - {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:cea7daf9fc7ae6687cf1e2c049752f19f146fdc37c2cc376e7d0032cf4f25347"}, - {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:8b3489ff1ed1e8315674d0775dc7d2195fb13ca17b3808721b54dbe9fd020faf"}, - {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:9403db39be1393618dd80c746cb22ccda168efce239c73af13c3763ef56ffc04"}, - {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:5d97151bc92d2b2578ff7ce779cdb9174337390a535953cbb9452fb65164c587"}, - {file = "propcache-0.2.1-cp39-cp39-win32.whl", hash = "sha256:9caac6b54914bdf41bcc91e7eb9147d331d29235a7c967c150ef5df6464fd1bb"}, - {file = "propcache-0.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:92fc4500fcb33899b05ba73276dfb684a20d31caa567b7cb5252d48f896a91b1"}, - {file = "propcache-0.2.1-py3-none-any.whl", hash = "sha256:52277518d6aae65536e9cea52d4e7fd2f7a66f4aa2d30ed3f2fcea620ace3c54"}, - {file = "propcache-0.2.1.tar.gz", hash = "sha256:3f77ce728b19cb537714499928fe800c3dda29e8d9428778fc7c186da4c09a64"}, + {file = "propcache-0.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c2d1fa3201efaf55d730400d945b5b3ab6e672e100ba0f9a409d950ab25d7db"}, + {file = "propcache-0.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1eb2994229cc8ce7fe9b3db88f5465f5fd8651672840b2e426b88cdb1a30aac8"}, + {file = "propcache-0.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:66c1f011f45a3b33d7bcb22daed4b29c0c9e2224758b6be00686731e1b46f925"}, + {file = "propcache-0.4.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9a52009f2adffe195d0b605c25ec929d26b36ef986ba85244891dee3b294df21"}, + {file = "propcache-0.4.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5d4e2366a9c7b837555cf02fb9be2e3167d333aff716332ef1b7c3a142ec40c5"}, + {file = "propcache-0.4.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:9d2b6caef873b4f09e26ea7e33d65f42b944837563a47a94719cc3544319a0db"}, + {file = "propcache-0.4.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2b16ec437a8c8a965ecf95739448dd938b5c7f56e67ea009f4300d8df05f32b7"}, + {file = "propcache-0.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:296f4c8ed03ca7476813fe666c9ea97869a8d7aec972618671b33a38a5182ef4"}, + {file = "propcache-0.4.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:1f0978529a418ebd1f49dad413a2b68af33f85d5c5ca5c6ca2a3bed375a7ac60"}, + {file = "propcache-0.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fd138803047fb4c062b1c1dd95462f5209456bfab55c734458f15d11da288f8f"}, + {file = "propcache-0.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8c9b3cbe4584636d72ff556d9036e0c9317fa27b3ac1f0f558e7e84d1c9c5900"}, + {file = "propcache-0.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f93243fdc5657247533273ac4f86ae106cc6445a0efacb9a1bfe982fcfefd90c"}, + {file = "propcache-0.4.1-cp310-cp310-win32.whl", hash = "sha256:a0ee98db9c5f80785b266eb805016e36058ac72c51a064040f2bc43b61101cdb"}, + {file = "propcache-0.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:1cdb7988c4e5ac7f6d175a28a9aa0c94cb6f2ebe52756a3c0cda98d2809a9e37"}, + {file = "propcache-0.4.1-cp310-cp310-win_arm64.whl", hash = "sha256:d82ad62b19645419fe79dd63b3f9253e15b30e955c0170e5cebc350c1844e581"}, + {file = "propcache-0.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:60a8fda9644b7dfd5dece8c61d8a85e271cb958075bfc4e01083c148b61a7caf"}, + {file = "propcache-0.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c30b53e7e6bda1d547cabb47c825f3843a0a1a42b0496087bb58d8fedf9f41b5"}, + {file = "propcache-0.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6918ecbd897443087a3b7cd978d56546a812517dcaaca51b49526720571fa93e"}, + {file = "propcache-0.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3d902a36df4e5989763425a8ab9e98cd8ad5c52c823b34ee7ef307fd50582566"}, + {file = "propcache-0.4.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a9695397f85973bb40427dedddf70d8dc4a44b22f1650dd4af9eedf443d45165"}, + {file = "propcache-0.4.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2bb07ffd7eaad486576430c89f9b215f9e4be68c4866a96e97db9e97fead85dc"}, + {file = "propcache-0.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fd6f30fdcf9ae2a70abd34da54f18da086160e4d7d9251f81f3da0ff84fc5a48"}, + {file = "propcache-0.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fc38cba02d1acba4e2869eef1a57a43dfbd3d49a59bf90dda7444ec2be6a5570"}, + {file = "propcache-0.4.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:67fad6162281e80e882fb3ec355398cf72864a54069d060321f6cd0ade95fe85"}, + {file = "propcache-0.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f10207adf04d08bec185bae14d9606a1444715bc99180f9331c9c02093e1959e"}, + {file = "propcache-0.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e9b0d8d0845bbc4cfcdcbcdbf5086886bc8157aa963c31c777ceff7846c77757"}, + {file = "propcache-0.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:981333cb2f4c1896a12f4ab92a9cc8f09ea664e9b7dbdc4eff74627af3a11c0f"}, + {file = "propcache-0.4.1-cp311-cp311-win32.whl", hash = "sha256:f1d2f90aeec838a52f1c1a32fe9a619fefd5e411721a9117fbf82aea638fe8a1"}, + {file = "propcache-0.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:364426a62660f3f699949ac8c621aad6977be7126c5807ce48c0aeb8e7333ea6"}, + {file = "propcache-0.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:e53f3a38d3510c11953f3e6a33f205c6d1b001129f972805ca9b42fc308bc239"}, + {file = "propcache-0.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e153e9cd40cc8945138822807139367f256f89c6810c2634a4f6902b52d3b4e2"}, + {file = "propcache-0.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cd547953428f7abb73c5ad82cbb32109566204260d98e41e5dfdc682eb7f8403"}, + {file = "propcache-0.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f048da1b4f243fc44f205dfd320933a951b8d89e0afd4c7cacc762a8b9165207"}, + {file = "propcache-0.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ec17c65562a827bba85e3872ead335f95405ea1674860d96483a02f5c698fa72"}, + {file = "propcache-0.4.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:405aac25c6394ef275dee4c709be43745d36674b223ba4eb7144bf4d691b7367"}, + {file = "propcache-0.4.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0013cb6f8dde4b2a2f66903b8ba740bdfe378c943c4377a200551ceb27f379e4"}, + {file = "propcache-0.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:15932ab57837c3368b024473a525e25d316d8353016e7cc0e5ba9eb343fbb1cf"}, + {file = "propcache-0.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:031dce78b9dc099f4c29785d9cf5577a3faf9ebf74ecbd3c856a7b92768c3df3"}, + {file = "propcache-0.4.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ab08df6c9a035bee56e31af99be621526bd237bea9f32def431c656b29e41778"}, + {file = "propcache-0.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4d7af63f9f93fe593afbf104c21b3b15868efb2c21d07d8732c0c4287e66b6a6"}, + {file = "propcache-0.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:cfc27c945f422e8b5071b6e93169679e4eb5bf73bbcbf1ba3ae3a83d2f78ebd9"}, + {file = "propcache-0.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:35c3277624a080cc6ec6f847cbbbb5b49affa3598c4535a0a4682a697aaa5c75"}, + {file = "propcache-0.4.1-cp312-cp312-win32.whl", hash = "sha256:671538c2262dadb5ba6395e26c1731e1d52534bfe9ae56d0b5573ce539266aa8"}, + {file = "propcache-0.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:cb2d222e72399fcf5890d1d5cc1060857b9b236adff2792ff48ca2dfd46c81db"}, + {file = "propcache-0.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:204483131fb222bdaaeeea9f9e6c6ed0cac32731f75dfc1d4a567fc1926477c1"}, + {file = "propcache-0.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:43eedf29202c08550aac1d14e0ee619b0430aaef78f85864c1a892294fbc28cf"}, + {file = "propcache-0.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d62cdfcfd89ccb8de04e0eda998535c406bf5e060ffd56be6c586cbcc05b3311"}, + {file = "propcache-0.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cae65ad55793da34db5f54e4029b89d3b9b9490d8abe1b4c7ab5d4b8ec7ebf74"}, + {file = "propcache-0.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:333ddb9031d2704a301ee3e506dc46b1fe5f294ec198ed6435ad5b6a085facfe"}, + {file = "propcache-0.4.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:fd0858c20f078a32cf55f7e81473d96dcf3b93fd2ccdb3d40fdf54b8573df3af"}, + {file = "propcache-0.4.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:678ae89ebc632c5c204c794f8dab2837c5f159aeb59e6ed0539500400577298c"}, + {file = "propcache-0.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d472aeb4fbf9865e0c6d622d7f4d54a4e101a89715d8904282bb5f9a2f476c3f"}, + {file = "propcache-0.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4d3df5fa7e36b3225954fba85589da77a0fe6a53e3976de39caf04a0db4c36f1"}, + {file = "propcache-0.4.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:ee17f18d2498f2673e432faaa71698032b0127ebf23ae5974eeaf806c279df24"}, + {file = "propcache-0.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:580e97762b950f993ae618e167e7be9256b8353c2dcd8b99ec100eb50f5286aa"}, + {file = "propcache-0.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:501d20b891688eb8e7aa903021f0b72d5a55db40ffaab27edefd1027caaafa61"}, + {file = "propcache-0.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a0bd56e5b100aef69bd8562b74b46254e7c8812918d3baa700c8a8009b0af66"}, + {file = "propcache-0.4.1-cp313-cp313-win32.whl", hash = "sha256:bcc9aaa5d80322bc2fb24bb7accb4a30f81e90ab8d6ba187aec0744bc302ad81"}, + {file = "propcache-0.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:381914df18634f5494334d201e98245c0596067504b9372d8cf93f4bb23e025e"}, + {file = "propcache-0.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:8873eb4460fd55333ea49b7d189749ecf6e55bf85080f11b1c4530ed3034cba1"}, + {file = "propcache-0.4.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:92d1935ee1f8d7442da9c0c4fa7ac20d07e94064184811b685f5c4fada64553b"}, + {file = "propcache-0.4.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:473c61b39e1460d386479b9b2f337da492042447c9b685f28be4f74d3529e566"}, + {file = "propcache-0.4.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:c0ef0aaafc66fbd87842a3fe3902fd889825646bc21149eafe47be6072725835"}, + {file = "propcache-0.4.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f95393b4d66bfae908c3ca8d169d5f79cd65636ae15b5e7a4f6e67af675adb0e"}, + {file = "propcache-0.4.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c07fda85708bc48578467e85099645167a955ba093be0a2dcba962195676e859"}, + {file = "propcache-0.4.1-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:af223b406d6d000830c6f65f1e6431783fc3f713ba3e6cc8c024d5ee96170a4b"}, + {file = "propcache-0.4.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a78372c932c90ee474559c5ddfffd718238e8673c340dc21fe45c5b8b54559a0"}, + {file = "propcache-0.4.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:564d9f0d4d9509e1a870c920a89b2fec951b44bf5ba7d537a9e7c1ccec2c18af"}, + {file = "propcache-0.4.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:17612831fda0138059cc5546f4d12a2aacfb9e47068c06af35c400ba58ba7393"}, + {file = "propcache-0.4.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:41a89040cb10bd345b3c1a873b2bf36413d48da1def52f268a055f7398514874"}, + {file = "propcache-0.4.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e35b88984e7fa64aacecea39236cee32dd9bd8c55f57ba8a75cf2399553f9bd7"}, + {file = "propcache-0.4.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6f8b465489f927b0df505cbe26ffbeed4d6d8a2bbc61ce90eb074ff129ef0ab1"}, + {file = "propcache-0.4.1-cp313-cp313t-win32.whl", hash = "sha256:2ad890caa1d928c7c2965b48f3a3815c853180831d0e5503d35cf00c472f4717"}, + {file = "propcache-0.4.1-cp313-cp313t-win_amd64.whl", hash = "sha256:f7ee0e597f495cf415bcbd3da3caa3bd7e816b74d0d52b8145954c5e6fd3ff37"}, + {file = "propcache-0.4.1-cp313-cp313t-win_arm64.whl", hash = "sha256:929d7cbe1f01bb7baffb33dc14eb5691c95831450a26354cd210a8155170c93a"}, + {file = "propcache-0.4.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3f7124c9d820ba5548d431afb4632301acf965db49e666aa21c305cbe8c6de12"}, + {file = "propcache-0.4.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:c0d4b719b7da33599dfe3b22d3db1ef789210a0597bc650b7cee9c77c2be8c5c"}, + {file = "propcache-0.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9f302f4783709a78240ebc311b793f123328716a60911d667e0c036bc5dcbded"}, + {file = "propcache-0.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c80ee5802e3fb9ea37938e7eecc307fb984837091d5fd262bb37238b1ae97641"}, + {file = "propcache-0.4.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ed5a841e8bb29a55fb8159ed526b26adc5bdd7e8bd7bf793ce647cb08656cdf4"}, + {file = "propcache-0.4.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:55c72fd6ea2da4c318e74ffdf93c4fe4e926051133657459131a95c846d16d44"}, + {file = "propcache-0.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8326e144341460402713f91df60ade3c999d601e7eb5ff8f6f7862d54de0610d"}, + {file = "propcache-0.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:060b16ae65bc098da7f6d25bf359f1f31f688384858204fe5d652979e0015e5b"}, + {file = "propcache-0.4.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:89eb3fa9524f7bec9de6e83cf3faed9d79bffa560672c118a96a171a6f55831e"}, + {file = "propcache-0.4.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:dee69d7015dc235f526fe80a9c90d65eb0039103fe565776250881731f06349f"}, + {file = "propcache-0.4.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:5558992a00dfd54ccbc64a32726a3357ec93825a418a401f5cc67df0ac5d9e49"}, + {file = "propcache-0.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c9b822a577f560fbd9554812526831712c1436d2c046cedee4c3796d3543b144"}, + {file = "propcache-0.4.1-cp314-cp314-win32.whl", hash = "sha256:ab4c29b49d560fe48b696cdcb127dd36e0bc2472548f3bf56cc5cb3da2b2984f"}, + {file = "propcache-0.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:5a103c3eb905fcea0ab98be99c3a9a5ab2de60228aa5aceedc614c0281cf6153"}, + {file = "propcache-0.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:74c1fb26515153e482e00177a1ad654721bf9207da8a494a0c05e797ad27b992"}, + {file = "propcache-0.4.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:824e908bce90fb2743bd6b59db36eb4f45cd350a39637c9f73b1c1ea66f5b75f"}, + {file = "propcache-0.4.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:c2b5e7db5328427c57c8e8831abda175421b709672f6cfc3d630c3b7e2146393"}, + {file = "propcache-0.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6f6ff873ed40292cd4969ef5310179afd5db59fdf055897e282485043fc80ad0"}, + {file = "propcache-0.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:49a2dc67c154db2c1463013594c458881a069fcf98940e61a0569016a583020a"}, + {file = "propcache-0.4.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:005f08e6a0529984491e37d8dbc3dd86f84bd78a8ceb5fa9a021f4c48d4984be"}, + {file = "propcache-0.4.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5c3310452e0d31390da9035c348633b43d7e7feb2e37be252be6da45abd1abcc"}, + {file = "propcache-0.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c3c70630930447f9ef1caac7728c8ad1c56bc5015338b20fed0d08ea2480b3a"}, + {file = "propcache-0.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8e57061305815dfc910a3634dcf584f08168a8836e6999983569f51a8544cd89"}, + {file = "propcache-0.4.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:521a463429ef54143092c11a77e04056dd00636f72e8c45b70aaa3140d639726"}, + {file = "propcache-0.4.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:120c964da3fdc75e3731aa392527136d4ad35868cc556fd09bb6d09172d9a367"}, + {file = "propcache-0.4.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:d8f353eb14ee3441ee844ade4277d560cdd68288838673273b978e3d6d2c8f36"}, + {file = "propcache-0.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ab2943be7c652f09638800905ee1bab2c544e537edb57d527997a24c13dc1455"}, + {file = "propcache-0.4.1-cp314-cp314t-win32.whl", hash = "sha256:05674a162469f31358c30bcaa8883cb7829fa3110bf9c0991fe27d7896c42d85"}, + {file = "propcache-0.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:990f6b3e2a27d683cb7602ed6c86f15ee6b43b1194736f9baaeb93d0016633b1"}, + {file = "propcache-0.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:ecef2343af4cc68e05131e45024ba34f6095821988a9d0a02aa7c73fcc448aa9"}, + {file = "propcache-0.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3d233076ccf9e450c8b3bc6720af226b898ef5d051a2d145f7d765e6e9f9bcff"}, + {file = "propcache-0.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:357f5bb5c377a82e105e44bd3d52ba22b616f7b9773714bff93573988ef0a5fb"}, + {file = "propcache-0.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cbc3b6dfc728105b2a57c06791eb07a94229202ea75c59db644d7d496b698cac"}, + {file = "propcache-0.4.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:182b51b421f0501952d938dc0b0eb45246a5b5153c50d42b495ad5fb7517c888"}, + {file = "propcache-0.4.1-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4b536b39c5199b96fc6245eb5fb796c497381d3942f169e44e8e392b29c9ebcc"}, + {file = "propcache-0.4.1-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:db65d2af507bbfbdcedb254a11149f894169d90488dd3e7190f7cdcb2d6cd57a"}, + {file = "propcache-0.4.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fd2dbc472da1f772a4dae4fa24be938a6c544671a912e30529984dd80400cd88"}, + {file = "propcache-0.4.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:daede9cd44e0f8bdd9e6cc9a607fc81feb80fae7a5fc6cecaff0e0bb32e42d00"}, + {file = "propcache-0.4.1-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:71b749281b816793678ae7f3d0d84bd36e694953822eaad408d682efc5ca18e0"}, + {file = "propcache-0.4.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:0002004213ee1f36cfb3f9a42b5066100c44276b9b72b4e1504cddd3d692e86e"}, + {file = "propcache-0.4.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:fe49d0a85038f36ba9e3ffafa1103e61170b28e95b16622e11be0a0ea07c6781"}, + {file = "propcache-0.4.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:99d43339c83aaf4d32bda60928231848eee470c6bda8d02599cc4cebe872d183"}, + {file = "propcache-0.4.1-cp39-cp39-win32.whl", hash = "sha256:a129e76735bc792794d5177069691c3217898b9f5cee2b2661471e52ffe13f19"}, + {file = "propcache-0.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:948dab269721ae9a87fd16c514a0a2c2a1bdb23a9a61b969b0f9d9ee2968546f"}, + {file = "propcache-0.4.1-cp39-cp39-win_arm64.whl", hash = "sha256:5fd37c406dd6dc85aa743e214cef35dc54bbdd1419baac4f6ae5e5b1a2976938"}, + {file = "propcache-0.4.1-py3-none-any.whl", hash = "sha256:af2a6052aeb6cf17d3e46ee169099044fd8224cbaf75c76a2ef596e8163e2237"}, + {file = "propcache-0.4.1.tar.gz", hash = "sha256:f48107a8c637e80362555f37ecf49abe20370e557cc4ab374f04ec4423c97c3d"}, ] [[package]] name = "protobuf" -version = "6.31.1" +version = "6.32.1" description = "" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "protobuf-6.31.1-cp310-abi3-win32.whl", hash = "sha256:7fa17d5a29c2e04b7d90e5e32388b8bfd0e7107cd8e616feef7ed3fa6bdab5c9"}, - {file = "protobuf-6.31.1-cp310-abi3-win_amd64.whl", hash = "sha256:426f59d2964864a1a366254fa703b8632dcec0790d8862d30034d8245e1cd447"}, - {file = "protobuf-6.31.1-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:6f1227473dc43d44ed644425268eb7c2e488ae245d51c6866d19fe158e207402"}, - {file = "protobuf-6.31.1-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:a40fc12b84c154884d7d4c4ebd675d5b3b5283e155f324049ae396b95ddebc39"}, - {file = "protobuf-6.31.1-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:4ee898bf66f7a8b0bd21bce523814e6fbd8c6add948045ce958b73af7e8878c6"}, - {file = "protobuf-6.31.1-cp39-cp39-win32.whl", hash = "sha256:0414e3aa5a5f3ff423828e1e6a6e907d6c65c1d5b7e6e975793d5590bdeecc16"}, - {file = "protobuf-6.31.1-cp39-cp39-win_amd64.whl", hash = "sha256:8764cf4587791e7564051b35524b72844f845ad0bb011704c3736cce762d8fe9"}, - {file = "protobuf-6.31.1-py3-none-any.whl", hash = "sha256:720a6c7e6b77288b85063569baae8536671b39f15cc22037ec7045658d80489e"}, - {file = "protobuf-6.31.1.tar.gz", hash = "sha256:d8cac4c982f0b957a4dc73a80e2ea24fab08e679c0de9deb835f4a12d69aca9a"}, + {file = "protobuf-6.32.1-cp310-abi3-win32.whl", hash = "sha256:a8a32a84bc9f2aad712041b8b366190f71dde248926da517bde9e832e4412085"}, + {file = "protobuf-6.32.1-cp310-abi3-win_amd64.whl", hash = "sha256:b00a7d8c25fa471f16bc8153d0e53d6c9e827f0953f3c09aaa4331c718cae5e1"}, + {file = "protobuf-6.32.1-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:d8c7e6eb619ffdf105ee4ab76af5a68b60a9d0f66da3ea12d1640e6d8dab7281"}, + {file = "protobuf-6.32.1-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:2f5b80a49e1eb7b86d85fcd23fe92df154b9730a725c3b38c4e43b9d77018bf4"}, + {file = "protobuf-6.32.1-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:b1864818300c297265c83a4982fd3169f97122c299f56a56e2445c3698d34710"}, + {file = "protobuf-6.32.1-cp39-cp39-win32.whl", hash = "sha256:68ff170bac18c8178f130d1ccb94700cf72852298e016a2443bdb9502279e5f1"}, + {file = "protobuf-6.32.1-cp39-cp39-win_amd64.whl", hash = "sha256:d0975d0b2f3e6957111aa3935d08a0eb7e006b1505d825f862a1fffc8348e122"}, + {file = "protobuf-6.32.1-py3-none-any.whl", hash = "sha256:2601b779fc7d32a866c6b4404f9d42a3f67c5b9f3f15b4db3cccabe06b95c346"}, + {file = "protobuf-6.32.1.tar.gz", hash = "sha256:ee2469e4a021474ab9baafea6cd070e5bf27c7d29433504ddea1a4ee5850f68d"}, ] +[[package]] +name = "psycopg" +version = "3.2.10" +description = "PostgreSQL database adapter for Python" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "psycopg-3.2.10-py3-none-any.whl", hash = "sha256:ab5caf09a9ec42e314a21f5216dbcceac528e0e05142e42eea83a3b28b320ac3"}, + {file = "psycopg-3.2.10.tar.gz", hash = "sha256:0bce99269d16ed18401683a8569b2c5abd94f72f8364856d56c0389bcd50972a"}, +] + +[package.dependencies] +psycopg-pool = {version = "*", optional = true, markers = "extra == \"pool\""} +typing-extensions = {version = ">=4.6", markers = "python_version < \"3.13\""} +tzdata = {version = "*", markers = "sys_platform == \"win32\""} + +[package.extras] +binary = ["psycopg-binary (==3.2.10)"] +c = ["psycopg-c (==3.2.10)"] +dev = ["ast-comments (>=1.1.2)", "black (>=24.1.0)", "codespell (>=2.2)", "dnspython (>=2.1)", "flake8 (>=4.0)", "isort-psycopg", "isort[colors] (>=6.0)", "mypy (>=1.14)", "pre-commit (>=4.0.1)", "types-setuptools (>=57.4)", "types-shapely (>=2.0)", "wheel (>=0.37)"] +docs = ["Sphinx (>=5.0)", "furo (==2022.6.21)", "sphinx-autobuild (>=2021.3.14)", "sphinx-autodoc-typehints (>=1.12)"] +pool = ["psycopg-pool"] +test = ["anyio (>=4.0)", "mypy (>=1.14)", "pproxy (>=2.7)", "pytest (>=6.2.5)", "pytest-cov (>=3.0)", "pytest-randomly (>=3.5)"] + +[[package]] +name = "psycopg-pool" +version = "3.2.6" +description = "Connection Pool for Psycopg" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "psycopg_pool-3.2.6-py3-none-any.whl", hash = "sha256:5887318a9f6af906d041a0b1dc1c60f8f0dda8340c2572b74e10907b51ed5da7"}, + {file = "psycopg_pool-3.2.6.tar.gz", hash = "sha256:0f92a7817719517212fbfe2fd58b8c35c1850cdd2a80d36b581ba2085d9148e5"}, +] + +[package.dependencies] +typing-extensions = ">=4.6" + [[package]] name = "pycparser" -version = "2.22" +version = "2.23" description = "C parser in Python" optional = false python-versions = ">=3.8" groups = ["main"] +markers = "implementation_name != \"PyPy\"" files = [ - {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, - {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, + {file = "pycparser-2.23-py3-none-any.whl", hash = "sha256:e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934"}, + {file = "pycparser-2.23.tar.gz", hash = "sha256:78816d4f24add8f10a06d6f05b4d424ad9e96cfebf68a4ddc99c65c0720d00c2"}, ] [[package]] name = "pycryptodome" -version = "3.21.0" +version = "3.23.0" description = "Cryptographic library for Python" optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" groups = ["main"] files = [ - {file = "pycryptodome-3.21.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:dad9bf36eda068e89059d1f07408e397856be9511d7113ea4b586642a429a4fd"}, - {file = "pycryptodome-3.21.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:a1752eca64c60852f38bb29e2c86fca30d7672c024128ef5d70cc15868fa10f4"}, - {file = "pycryptodome-3.21.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:3ba4cc304eac4d4d458f508d4955a88ba25026890e8abff9b60404f76a62c55e"}, - {file = "pycryptodome-3.21.0-cp27-cp27m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7cb087b8612c8a1a14cf37dd754685be9a8d9869bed2ffaaceb04850a8aeef7e"}, - {file = "pycryptodome-3.21.0-cp27-cp27m-musllinux_1_1_aarch64.whl", hash = "sha256:26412b21df30b2861424a6c6d5b1d8ca8107612a4cfa4d0183e71c5d200fb34a"}, - {file = "pycryptodome-3.21.0-cp27-cp27m-win32.whl", hash = "sha256:cc2269ab4bce40b027b49663d61d816903a4bd90ad88cb99ed561aadb3888dd3"}, - {file = "pycryptodome-3.21.0-cp27-cp27m-win_amd64.whl", hash = "sha256:0fa0a05a6a697ccbf2a12cec3d6d2650b50881899b845fac6e87416f8cb7e87d"}, - {file = "pycryptodome-3.21.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:6cce52e196a5f1d6797ff7946cdff2038d3b5f0aba4a43cb6bf46b575fd1b5bb"}, - {file = "pycryptodome-3.21.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:a915597ffccabe902e7090e199a7bf7a381c5506a747d5e9d27ba55197a2c568"}, - {file = "pycryptodome-3.21.0-cp27-cp27mu-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a4e74c522d630766b03a836c15bff77cb657c5fdf098abf8b1ada2aebc7d0819"}, - {file = "pycryptodome-3.21.0-cp27-cp27mu-musllinux_1_1_aarch64.whl", hash = "sha256:a3804675283f4764a02db05f5191eb8fec2bb6ca34d466167fc78a5f05bbe6b3"}, - {file = "pycryptodome-3.21.0-cp36-abi3-macosx_10_9_universal2.whl", hash = "sha256:2480ec2c72438430da9f601ebc12c518c093c13111a5c1644c82cdfc2e50b1e4"}, - {file = "pycryptodome-3.21.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:de18954104667f565e2fbb4783b56667f30fb49c4d79b346f52a29cb198d5b6b"}, - {file = "pycryptodome-3.21.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2de4b7263a33947ff440412339cb72b28a5a4c769b5c1ca19e33dd6cd1dcec6e"}, - {file = "pycryptodome-3.21.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0714206d467fc911042d01ea3a1847c847bc10884cf674c82e12915cfe1649f8"}, - {file = "pycryptodome-3.21.0-cp36-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7d85c1b613121ed3dbaa5a97369b3b757909531a959d229406a75b912dd51dd1"}, - {file = "pycryptodome-3.21.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:8898a66425a57bcf15e25fc19c12490b87bd939800f39a03ea2de2aea5e3611a"}, - {file = "pycryptodome-3.21.0-cp36-abi3-musllinux_1_2_i686.whl", hash = "sha256:932c905b71a56474bff8a9c014030bc3c882cee696b448af920399f730a650c2"}, - {file = "pycryptodome-3.21.0-cp36-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:18caa8cfbc676eaaf28613637a89980ad2fd96e00c564135bf90bc3f0b34dd93"}, - {file = "pycryptodome-3.21.0-cp36-abi3-win32.whl", hash = "sha256:280b67d20e33bb63171d55b1067f61fbd932e0b1ad976b3a184303a3dad22764"}, - {file = "pycryptodome-3.21.0-cp36-abi3-win_amd64.whl", hash = "sha256:b7aa25fc0baa5b1d95b7633af4f5f1838467f1815442b22487426f94e0d66c53"}, - {file = "pycryptodome-3.21.0-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:2cb635b67011bc147c257e61ce864879ffe6d03342dc74b6045059dfbdedafca"}, - {file = "pycryptodome-3.21.0-pp27-pypy_73-win32.whl", hash = "sha256:4c26a2f0dc15f81ea3afa3b0c87b87e501f235d332b7f27e2225ecb80c0b1cdd"}, - {file = "pycryptodome-3.21.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:d5ebe0763c982f069d3877832254f64974139f4f9655058452603ff559c482e8"}, - {file = "pycryptodome-3.21.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ee86cbde706be13f2dec5a42b52b1c1d1cbb90c8e405c68d0755134735c8dc6"}, - {file = "pycryptodome-3.21.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0fd54003ec3ce4e0f16c484a10bc5d8b9bd77fa662a12b85779a2d2d85d67ee0"}, - {file = "pycryptodome-3.21.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:5dfafca172933506773482b0e18f0cd766fd3920bd03ec85a283df90d8a17bc6"}, - {file = "pycryptodome-3.21.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:590ef0898a4b0a15485b05210b4a1c9de8806d3ad3d47f74ab1dc07c67a6827f"}, - {file = "pycryptodome-3.21.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f35e442630bc4bc2e1878482d6f59ea22e280d7121d7adeaedba58c23ab6386b"}, - {file = "pycryptodome-3.21.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff99f952db3db2fbe98a0b355175f93ec334ba3d01bbde25ad3a5a33abc02b58"}, - {file = "pycryptodome-3.21.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:8acd7d34af70ee63f9a849f957558e49a98f8f1634f86a59d2be62bb8e93f71c"}, - {file = "pycryptodome-3.21.0.tar.gz", hash = "sha256:f7787e0d469bdae763b876174cf2e6c0f7be79808af26b1da96f1a64bcf47297"}, + {file = "pycryptodome-3.23.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a176b79c49af27d7f6c12e4b178b0824626f40a7b9fed08f712291b6d54bf566"}, + {file = "pycryptodome-3.23.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:573a0b3017e06f2cffd27d92ef22e46aa3be87a2d317a5abf7cc0e84e321bd75"}, + {file = "pycryptodome-3.23.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:63dad881b99ca653302b2c7191998dd677226222a3f2ea79999aa51ce695f720"}, + {file = "pycryptodome-3.23.0-cp27-cp27m-win32.whl", hash = "sha256:b34e8e11d97889df57166eda1e1ddd7676da5fcd4d71a0062a760e75060514b4"}, + {file = "pycryptodome-3.23.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:7ac1080a8da569bde76c0a104589c4f414b8ba296c0b3738cf39a466a9fb1818"}, + {file = "pycryptodome-3.23.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:6fe8258e2039eceb74dfec66b3672552b6b7d2c235b2dfecc05d16b8921649a8"}, + {file = "pycryptodome-3.23.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:0011f7f00cdb74879142011f95133274741778abba114ceca229adbf8e62c3e4"}, + {file = "pycryptodome-3.23.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:90460fc9e088ce095f9ee8356722d4f10f86e5be06e2354230a9880b9c549aae"}, + {file = "pycryptodome-3.23.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4764e64b269fc83b00f682c47443c2e6e85b18273712b98aa43bcb77f8570477"}, + {file = "pycryptodome-3.23.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb8f24adb74984aa0e5d07a2368ad95276cf38051fe2dc6605cbcf482e04f2a7"}, + {file = "pycryptodome-3.23.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d97618c9c6684a97ef7637ba43bdf6663a2e2e77efe0f863cce97a76af396446"}, + {file = "pycryptodome-3.23.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9a53a4fe5cb075075d515797d6ce2f56772ea7e6a1e5e4b96cf78a14bac3d265"}, + {file = "pycryptodome-3.23.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:763d1d74f56f031788e5d307029caef067febf890cd1f8bf61183ae142f1a77b"}, + {file = "pycryptodome-3.23.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:954af0e2bd7cea83ce72243b14e4fb518b18f0c1649b576d114973e2073b273d"}, + {file = "pycryptodome-3.23.0-cp313-cp313t-win32.whl", hash = "sha256:257bb3572c63ad8ba40b89f6fc9d63a2a628e9f9708d31ee26560925ebe0210a"}, + {file = "pycryptodome-3.23.0-cp313-cp313t-win_amd64.whl", hash = "sha256:6501790c5b62a29fcb227bd6b62012181d886a767ce9ed03b303d1f22eb5c625"}, + {file = "pycryptodome-3.23.0-cp313-cp313t-win_arm64.whl", hash = "sha256:9a77627a330ab23ca43b48b130e202582e91cc69619947840ea4d2d1be21eb39"}, + {file = "pycryptodome-3.23.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:187058ab80b3281b1de11c2e6842a357a1f71b42cb1e15bce373f3d238135c27"}, + {file = "pycryptodome-3.23.0-cp37-abi3-macosx_10_9_x86_64.whl", hash = "sha256:cfb5cd445280c5b0a4e6187a7ce8de5a07b5f3f897f235caa11f1f435f182843"}, + {file = "pycryptodome-3.23.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67bd81fcbe34f43ad9422ee8fd4843c8e7198dd88dd3d40e6de42ee65fbe1490"}, + {file = "pycryptodome-3.23.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8987bd3307a39bc03df5c8e0e3d8be0c4c3518b7f044b0f4c15d1aa78f52575"}, + {file = "pycryptodome-3.23.0-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aa0698f65e5b570426fc31b8162ed4603b0c2841cbb9088e2b01641e3065915b"}, + {file = "pycryptodome-3.23.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:53ecbafc2b55353edcebd64bf5da94a2a2cdf5090a6915bcca6eca6cc452585a"}, + {file = "pycryptodome-3.23.0-cp37-abi3-musllinux_1_2_i686.whl", hash = "sha256:156df9667ad9f2ad26255926524e1c136d6664b741547deb0a86a9acf5ea631f"}, + {file = "pycryptodome-3.23.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:dea827b4d55ee390dc89b2afe5927d4308a8b538ae91d9c6f7a5090f397af1aa"}, + {file = "pycryptodome-3.23.0-cp37-abi3-win32.whl", hash = "sha256:507dbead45474b62b2bbe318eb1c4c8ee641077532067fec9c1aa82c31f84886"}, + {file = "pycryptodome-3.23.0-cp37-abi3-win_amd64.whl", hash = "sha256:c75b52aacc6c0c260f204cbdd834f76edc9fb0d8e0da9fbf8352ef58202564e2"}, + {file = "pycryptodome-3.23.0-cp37-abi3-win_arm64.whl", hash = "sha256:11eeeb6917903876f134b56ba11abe95c0b0fd5e3330def218083c7d98bbcb3c"}, + {file = "pycryptodome-3.23.0-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:350ebc1eba1da729b35ab7627a833a1a355ee4e852d8ba0447fafe7b14504d56"}, + {file = "pycryptodome-3.23.0-pp27-pypy_73-win32.whl", hash = "sha256:93837e379a3e5fd2bb00302a47aee9fdf7940d83595be3915752c74033d17ca7"}, + {file = "pycryptodome-3.23.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ddb95b49df036ddd264a0ad246d1be5b672000f12d6961ea2c267083a5e19379"}, + {file = "pycryptodome-3.23.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e95564beb8782abfd9e431c974e14563a794a4944c29d6d3b7b5ea042110b4"}, + {file = "pycryptodome-3.23.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14e15c081e912c4b0d75632acd8382dfce45b258667aa3c67caf7a4d4c13f630"}, + {file = "pycryptodome-3.23.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7fc76bf273353dc7e5207d172b83f569540fc9a28d63171061c42e361d22353"}, + {file = "pycryptodome-3.23.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:45c69ad715ca1a94f778215a11e66b7ff989d792a4d63b68dc586a1da1392ff5"}, + {file = "pycryptodome-3.23.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:865d83c906b0fc6a59b510deceee656b6bc1c4fa0d82176e2b77e97a420a996a"}, + {file = "pycryptodome-3.23.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89d4d56153efc4d81defe8b65fd0821ef8b2d5ddf8ed19df31ba2f00872b8002"}, + {file = "pycryptodome-3.23.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3f2d0aaf8080bda0587d58fc9fe4766e012441e2eed4269a77de6aea981c8be"}, + {file = "pycryptodome-3.23.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:64093fc334c1eccfd3933c134c4457c34eaca235eeae49d69449dc4728079339"}, + {file = "pycryptodome-3.23.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:ce64e84a962b63a47a592690bdc16a7eaf709d2c2697ababf24a0def566899a6"}, + {file = "pycryptodome-3.23.0.tar.gz", hash = "sha256:447700a657182d60338bab09fdb27518f8856aecd80ae4c6bdddb67ff5da44ef"}, ] [[package]] name = "pydantic" -version = "2.10.5" +version = "2.12.0" description = "Data validation using Python type hints" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["main"] files = [ - {file = "pydantic-2.10.5-py3-none-any.whl", hash = "sha256:4dd4e322dbe55472cb7ca7e73f4b63574eecccf2835ffa2af9021ce113c83c53"}, - {file = "pydantic-2.10.5.tar.gz", hash = "sha256:278b38dbbaec562011d659ee05f63346951b3a248a6f3642e1bc68894ea2b4ff"}, + {file = "pydantic-2.12.0-py3-none-any.whl", hash = "sha256:f6a1da352d42790537e95e83a8bdfb91c7efbae63ffd0b86fa823899e807116f"}, + {file = "pydantic-2.12.0.tar.gz", hash = "sha256:c1a077e6270dbfb37bfd8b498b3981e2bb18f68103720e51fa6c306a5a9af563"}, ] [package.dependencies] annotated-types = ">=0.6.0" -pydantic-core = "2.27.2" -typing-extensions = ">=4.12.2" +pydantic-core = "2.41.1" +typing-extensions = ">=4.14.1" +typing-inspection = ">=0.4.2" [package.extras] email = ["email-validator (>=2.0.0)"] @@ -910,116 +1304,186 @@ timezone = ["tzdata"] [[package]] name = "pydantic-core" -version = "2.27.2" +version = "2.41.1" description = "Core functionality for Pydantic validation and serialization" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["main"] files = [ - {file = "pydantic_core-2.27.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2d367ca20b2f14095a8f4fa1210f5a7b78b8a20009ecced6b12818f455b1e9fa"}, - {file = "pydantic_core-2.27.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:491a2b73db93fab69731eaee494f320faa4e093dbed776be1a829c2eb222c34c"}, - {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7969e133a6f183be60e9f6f56bfae753585680f3b7307a8e555a948d443cc05a"}, - {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3de9961f2a346257caf0aa508a4da705467f53778e9ef6fe744c038119737ef5"}, - {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e2bb4d3e5873c37bb3dd58714d4cd0b0e6238cebc4177ac8fe878f8b3aa8e74c"}, - {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:280d219beebb0752699480fe8f1dc61ab6615c2046d76b7ab7ee38858de0a4e7"}, - {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47956ae78b6422cbd46f772f1746799cbb862de838fd8d1fbd34a82e05b0983a"}, - {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:14d4a5c49d2f009d62a2a7140d3064f686d17a5d1a268bc641954ba181880236"}, - {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:337b443af21d488716f8d0b6164de833e788aa6bd7e3a39c005febc1284f4962"}, - {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:03d0f86ea3184a12f41a2d23f7ccb79cdb5a18e06993f8a45baa8dfec746f0e9"}, - {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7041c36f5680c6e0f08d922aed302e98b3745d97fe1589db0a3eebf6624523af"}, - {file = "pydantic_core-2.27.2-cp310-cp310-win32.whl", hash = "sha256:50a68f3e3819077be2c98110c1f9dcb3817e93f267ba80a2c05bb4f8799e2ff4"}, - {file = "pydantic_core-2.27.2-cp310-cp310-win_amd64.whl", hash = "sha256:e0fd26b16394ead34a424eecf8a31a1f5137094cabe84a1bcb10fa6ba39d3d31"}, - {file = "pydantic_core-2.27.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:8e10c99ef58cfdf2a66fc15d66b16c4a04f62bca39db589ae8cba08bc55331bc"}, - {file = "pydantic_core-2.27.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:26f32e0adf166a84d0cb63be85c562ca8a6fa8de28e5f0d92250c6b7e9e2aff7"}, - {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c19d1ea0673cd13cc2f872f6c9ab42acc4e4f492a7ca9d3795ce2b112dd7e15"}, - {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5e68c4446fe0810e959cdff46ab0a41ce2f2c86d227d96dc3847af0ba7def306"}, - {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d9640b0059ff4f14d1f37321b94061c6db164fbe49b334b31643e0528d100d99"}, - {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:40d02e7d45c9f8af700f3452f329ead92da4c5f4317ca9b896de7ce7199ea459"}, - {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c1fd185014191700554795c99b347d64f2bb637966c4cfc16998a0ca700d048"}, - {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d81d2068e1c1228a565af076598f9e7451712700b673de8f502f0334f281387d"}, - {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1a4207639fb02ec2dbb76227d7c751a20b1a6b4bc52850568e52260cae64ca3b"}, - {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:3de3ce3c9ddc8bbd88f6e0e304dea0e66d843ec9de1b0042b0911c1663ffd474"}, - {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:30c5f68ded0c36466acede341551106821043e9afaad516adfb6e8fa80a4e6a6"}, - {file = "pydantic_core-2.27.2-cp311-cp311-win32.whl", hash = "sha256:c70c26d2c99f78b125a3459f8afe1aed4d9687c24fd677c6a4436bc042e50d6c"}, - {file = "pydantic_core-2.27.2-cp311-cp311-win_amd64.whl", hash = "sha256:08e125dbdc505fa69ca7d9c499639ab6407cfa909214d500897d02afb816e7cc"}, - {file = "pydantic_core-2.27.2-cp311-cp311-win_arm64.whl", hash = "sha256:26f0d68d4b235a2bae0c3fc585c585b4ecc51382db0e3ba402a22cbc440915e4"}, - {file = "pydantic_core-2.27.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9e0c8cfefa0ef83b4da9588448b6d8d2a2bf1a53c3f1ae5fca39eb3061e2f0b0"}, - {file = "pydantic_core-2.27.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:83097677b8e3bd7eaa6775720ec8e0405f1575015a463285a92bfdfe254529ef"}, - {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:172fce187655fece0c90d90a678424b013f8fbb0ca8b036ac266749c09438cb7"}, - {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:519f29f5213271eeeeb3093f662ba2fd512b91c5f188f3bb7b27bc5973816934"}, - {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05e3a55d124407fffba0dd6b0c0cd056d10e983ceb4e5dbd10dda135c31071d6"}, - {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c3ed807c7b91de05e63930188f19e921d1fe90de6b4f5cd43ee7fcc3525cb8c"}, - {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fb4aadc0b9a0c063206846d603b92030eb6f03069151a625667f982887153e2"}, - {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:28ccb213807e037460326424ceb8b5245acb88f32f3d2777427476e1b32c48c4"}, - {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:de3cd1899e2c279b140adde9357c4495ed9d47131b4a4eaff9052f23398076b3"}, - {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:220f892729375e2d736b97d0e51466252ad84c51857d4d15f5e9692f9ef12be4"}, - {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a0fcd29cd6b4e74fe8ddd2c90330fd8edf2e30cb52acda47f06dd615ae72da57"}, - {file = "pydantic_core-2.27.2-cp312-cp312-win32.whl", hash = "sha256:1e2cb691ed9834cd6a8be61228471d0a503731abfb42f82458ff27be7b2186fc"}, - {file = "pydantic_core-2.27.2-cp312-cp312-win_amd64.whl", hash = "sha256:cc3f1a99a4f4f9dd1de4fe0312c114e740b5ddead65bb4102884b384c15d8bc9"}, - {file = "pydantic_core-2.27.2-cp312-cp312-win_arm64.whl", hash = "sha256:3911ac9284cd8a1792d3cb26a2da18f3ca26c6908cc434a18f730dc0db7bfa3b"}, - {file = "pydantic_core-2.27.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7d14bd329640e63852364c306f4d23eb744e0f8193148d4044dd3dacdaacbd8b"}, - {file = "pydantic_core-2.27.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:82f91663004eb8ed30ff478d77c4d1179b3563df6cdb15c0817cd1cdaf34d154"}, - {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71b24c7d61131bb83df10cc7e687433609963a944ccf45190cfc21e0887b08c9"}, - {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fa8e459d4954f608fa26116118bb67f56b93b209c39b008277ace29937453dc9"}, - {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce8918cbebc8da707ba805b7fd0b382816858728ae7fe19a942080c24e5b7cd1"}, - {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eda3f5c2a021bbc5d976107bb302e0131351c2ba54343f8a496dc8783d3d3a6a"}, - {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd8086fa684c4775c27f03f062cbb9eaa6e17f064307e86b21b9e0abc9c0f02e"}, - {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8d9b3388db186ba0c099a6d20f0604a44eabdeef1777ddd94786cdae158729e4"}, - {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7a66efda2387de898c8f38c0cf7f14fca0b51a8ef0b24bfea5849f1b3c95af27"}, - {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:18a101c168e4e092ab40dbc2503bdc0f62010e95d292b27827871dc85450d7ee"}, - {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ba5dd002f88b78a4215ed2f8ddbdf85e8513382820ba15ad5ad8955ce0ca19a1"}, - {file = "pydantic_core-2.27.2-cp313-cp313-win32.whl", hash = "sha256:1ebaf1d0481914d004a573394f4be3a7616334be70261007e47c2a6fe7e50130"}, - {file = "pydantic_core-2.27.2-cp313-cp313-win_amd64.whl", hash = "sha256:953101387ecf2f5652883208769a79e48db18c6df442568a0b5ccd8c2723abee"}, - {file = "pydantic_core-2.27.2-cp313-cp313-win_arm64.whl", hash = "sha256:ac4dbfd1691affb8f48c2c13241a2e3b60ff23247cbcf981759c768b6633cf8b"}, - {file = "pydantic_core-2.27.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d3e8d504bdd3f10835468f29008d72fc8359d95c9c415ce6e767203db6127506"}, - {file = "pydantic_core-2.27.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:521eb9b7f036c9b6187f0b47318ab0d7ca14bd87f776240b90b21c1f4f149320"}, - {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85210c4d99a0114f5a9481b44560d7d1e35e32cc5634c656bc48e590b669b145"}, - {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d716e2e30c6f140d7560ef1538953a5cd1a87264c737643d481f2779fc247fe1"}, - {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f66d89ba397d92f840f8654756196d93804278457b5fbede59598a1f9f90b228"}, - {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:669e193c1c576a58f132e3158f9dfa9662969edb1a250c54d8fa52590045f046"}, - {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdbe7629b996647b99c01b37f11170a57ae675375b14b8c13b8518b8320ced5"}, - {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d262606bf386a5ba0b0af3b97f37c83d7011439e3dc1a9298f21efb292e42f1a"}, - {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:cabb9bcb7e0d97f74df8646f34fc76fbf793b7f6dc2438517d7a9e50eee4f14d"}, - {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_armv7l.whl", hash = "sha256:d2d63f1215638d28221f664596b1ccb3944f6e25dd18cd3b86b0a4c408d5ebb9"}, - {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bca101c00bff0adb45a833f8451b9105d9df18accb8743b08107d7ada14bd7da"}, - {file = "pydantic_core-2.27.2-cp38-cp38-win32.whl", hash = "sha256:f6f8e111843bbb0dee4cb6594cdc73e79b3329b526037ec242a3e49012495b3b"}, - {file = "pydantic_core-2.27.2-cp38-cp38-win_amd64.whl", hash = "sha256:fd1aea04935a508f62e0d0ef1f5ae968774a32afc306fb8545e06f5ff5cdf3ad"}, - {file = "pydantic_core-2.27.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:c10eb4f1659290b523af58fa7cffb452a61ad6ae5613404519aee4bfbf1df993"}, - {file = "pydantic_core-2.27.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ef592d4bad47296fb11f96cd7dc898b92e795032b4894dfb4076cfccd43a9308"}, - {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c61709a844acc6bf0b7dce7daae75195a10aac96a596ea1b776996414791ede4"}, - {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42c5f762659e47fdb7b16956c71598292f60a03aa92f8b6351504359dbdba6cf"}, - {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4c9775e339e42e79ec99c441d9730fccf07414af63eac2f0e48e08fd38a64d76"}, - {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:57762139821c31847cfb2df63c12f725788bd9f04bc2fb392790959b8f70f118"}, - {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d1e85068e818c73e048fe28cfc769040bb1f475524f4745a5dc621f75ac7630"}, - {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:097830ed52fd9e427942ff3b9bc17fab52913b2f50f2880dc4a5611446606a54"}, - {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:044a50963a614ecfae59bb1eaf7ea7efc4bc62f49ed594e18fa1e5d953c40e9f"}, - {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:4e0b4220ba5b40d727c7f879eac379b822eee5d8fff418e9d3381ee45b3b0362"}, - {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5e4f4bb20d75e9325cc9696c6802657b58bc1dbbe3022f32cc2b2b632c3fbb96"}, - {file = "pydantic_core-2.27.2-cp39-cp39-win32.whl", hash = "sha256:cca63613e90d001b9f2f9a9ceb276c308bfa2a43fafb75c8031c4f66039e8c6e"}, - {file = "pydantic_core-2.27.2-cp39-cp39-win_amd64.whl", hash = "sha256:77d1bca19b0f7021b3a982e6f903dcd5b2b06076def36a652e3907f596e29f67"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2bf14caea37e91198329b828eae1618c068dfb8ef17bb33287a7ad4b61ac314e"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:b0cb791f5b45307caae8810c2023a184c74605ec3bcbb67d13846c28ff731ff8"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:688d3fd9fcb71f41c4c015c023d12a79d1c4c0732ec9eb35d96e3388a120dcf3"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d591580c34f4d731592f0e9fe40f9cc1b430d297eecc70b962e93c5c668f15f"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:82f986faf4e644ffc189a7f1aafc86e46ef70372bb153e7001e8afccc6e54133"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:bec317a27290e2537f922639cafd54990551725fc844249e64c523301d0822fc"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:0296abcb83a797db256b773f45773da397da75a08f5fcaef41f2044adec05f50"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:0d75070718e369e452075a6017fbf187f788e17ed67a3abd47fa934d001863d9"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:7e17b560be3c98a8e3aa66ce828bdebb9e9ac6ad5466fba92eb74c4c95cb1151"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c33939a82924da9ed65dab5a65d427205a73181d8098e79b6b426bdf8ad4e656"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:00bad2484fa6bda1e216e7345a798bd37c68fb2d97558edd584942aa41b7d278"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c817e2b40aba42bac6f457498dacabc568c3b7a986fc9ba7c8d9d260b71485fb"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:251136cdad0cb722e93732cb45ca5299fb56e1344a833640bf93b2803f8d1bfd"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d2088237af596f0a524d3afc39ab3b036e8adb054ee57cbb1dcf8e09da5b29cc"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d4041c0b966a84b4ae7a09832eb691a35aec90910cd2dbe7a208de59be77965b"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:8083d4e875ebe0b864ffef72a4304827015cff328a1be6e22cc850753bfb122b"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f141ee28a0ad2123b6611b6ceff018039df17f32ada8b534e6aa039545a3efb2"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7d0c8399fcc1848491f00e0314bd59fb34a9c008761bcb422a057670c3f65e35"}, - {file = "pydantic_core-2.27.2.tar.gz", hash = "sha256:eb026e5a4c1fee05726072337ff51d1efb6f59090b7da90d30ea58625b1ffb39"}, + {file = "pydantic_core-2.41.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:e63036298322e9aea1c8b7c0a6c1204d615dbf6ec0668ce5b83ff27f07404a61"}, + {file = "pydantic_core-2.41.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:241299ca91fc77ef64f11ed909d2d9220a01834e8e6f8de61275c4dd16b7c936"}, + {file = "pydantic_core-2.41.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ab7e594a2a5c24ab8013a7dc8cfe5f2260e80e490685814122081705c2cf2b0"}, + {file = "pydantic_core-2.41.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b054ef1a78519cb934b58e9c90c09e93b837c935dcd907b891f2b265b129eb6e"}, + {file = "pydantic_core-2.41.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f2ab7d10d0ab2ed6da54c757233eb0f48ebfb4f86e9b88ccecb3f92bbd61a538"}, + {file = "pydantic_core-2.41.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2757606b7948bb853a27e4040820306eaa0ccb9e8f9f8a0fa40cb674e170f350"}, + {file = "pydantic_core-2.41.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cec0e75eb61f606bad0a32f2be87507087514e26e8c73db6cbdb8371ccd27917"}, + {file = "pydantic_core-2.41.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0234236514f44a5bf552105cfe2543a12f48203397d9d0f866affa569345a5b5"}, + {file = "pydantic_core-2.41.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:1b974e41adfbb4ebb0f65fc4ca951347b17463d60893ba7d5f7b9bb087c83897"}, + {file = "pydantic_core-2.41.1-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:248dafb3204136113c383e91a4d815269f51562b6659b756cf3df14eefc7d0bb"}, + {file = "pydantic_core-2.41.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:678f9d76a91d6bcedd7568bbf6beb77ae8447f85d1aeebaab7e2f0829cfc3a13"}, + {file = "pydantic_core-2.41.1-cp310-cp310-win32.whl", hash = "sha256:dff5bee1d21ee58277900692a641925d2dddfde65182c972569b1a276d2ac8fb"}, + {file = "pydantic_core-2.41.1-cp310-cp310-win_amd64.whl", hash = "sha256:5042da12e5d97d215f91567110fdfa2e2595a25f17c19b9ff024f31c34f9b53e"}, + {file = "pydantic_core-2.41.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4f276a6134fe1fc1daa692642a3eaa2b7b858599c49a7610816388f5e37566a1"}, + {file = "pydantic_core-2.41.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:07588570a805296ece009c59d9a679dc08fab72fb337365afb4f3a14cfbfc176"}, + {file = "pydantic_core-2.41.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28527e4b53400cd60ffbd9812ccb2b5135d042129716d71afd7e45bf42b855c0"}, + {file = "pydantic_core-2.41.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:46a1c935c9228bad738c8a41de06478770927baedf581d172494ab36a6b96575"}, + {file = "pydantic_core-2.41.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:447ddf56e2b7d28d200d3e9eafa936fe40485744b5a824b67039937580b3cb20"}, + {file = "pydantic_core-2.41.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:63892ead40c1160ac860b5debcc95c95c5a0035e543a8b5a4eac70dd22e995f4"}, + {file = "pydantic_core-2.41.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f4a9543ca355e6df8fbe9c83e9faab707701e9103ae857ecb40f1c0cf8b0e94d"}, + {file = "pydantic_core-2.41.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f2611bdb694116c31e551ed82e20e39a90bea9b7ad9e54aaf2d045ad621aa7a1"}, + {file = "pydantic_core-2.41.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fecc130893a9b5f7bfe230be1bb8c61fe66a19db8ab704f808cb25a82aad0bc9"}, + {file = "pydantic_core-2.41.1-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:1e2df5f8344c99b6ea5219f00fdc8950b8e6f2c422fbc1cc122ec8641fac85a1"}, + {file = "pydantic_core-2.41.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:35291331e9d8ed94c257bab6be1cb3a380b5eee570a2784bffc055e18040a2ea"}, + {file = "pydantic_core-2.41.1-cp311-cp311-win32.whl", hash = "sha256:2876a095292668d753f1a868c4a57c4ac9f6acbd8edda8debe4218d5848cf42f"}, + {file = "pydantic_core-2.41.1-cp311-cp311-win_amd64.whl", hash = "sha256:b92d6c628e9a338846a28dfe3fcdc1a3279388624597898b105e078cdfc59298"}, + {file = "pydantic_core-2.41.1-cp311-cp311-win_arm64.whl", hash = "sha256:7d82ae99409eb69d507a89835488fb657faa03ff9968a9379567b0d2e2e56bc5"}, + {file = "pydantic_core-2.41.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:db2f82c0ccbce8f021ad304ce35cbe02aa2f95f215cac388eed542b03b4d5eb4"}, + {file = "pydantic_core-2.41.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:47694a31c710ced9205d5f1e7e8af3ca57cbb8a503d98cb9e33e27c97a501601"}, + {file = "pydantic_core-2.41.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:93e9decce94daf47baf9e9d392f5f2557e783085f7c5e522011545d9d6858e00"}, + {file = "pydantic_core-2.41.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ab0adafdf2b89c8b84f847780a119437a0931eca469f7b44d356f2b426dd9741"}, + {file = "pydantic_core-2.41.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5da98cc81873f39fd56882e1569c4677940fbc12bce6213fad1ead784192d7c8"}, + {file = "pydantic_core-2.41.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:209910e88afb01fd0fd403947b809ba8dba0e08a095e1f703294fda0a8fdca51"}, + {file = "pydantic_core-2.41.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:365109d1165d78d98e33c5bfd815a9b5d7d070f578caefaabcc5771825b4ecb5"}, + {file = "pydantic_core-2.41.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:706abf21e60a2857acdb09502bc853ee5bce732955e7b723b10311114f033115"}, + {file = "pydantic_core-2.41.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:bf0bd5417acf7f6a7ec3b53f2109f587be176cb35f9cf016da87e6017437a72d"}, + {file = "pydantic_core-2.41.1-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:2e71b1c6ceb9c78424ae9f63a07292fb769fb890a4e7efca5554c47f33a60ea5"}, + {file = "pydantic_core-2.41.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:80745b9770b4a38c25015b517451c817799bfb9d6499b0d13d8227ec941cb513"}, + {file = "pydantic_core-2.41.1-cp312-cp312-win32.whl", hash = "sha256:83b64d70520e7890453f1aa21d66fda44e7b35f1cfea95adf7b4289a51e2b479"}, + {file = "pydantic_core-2.41.1-cp312-cp312-win_amd64.whl", hash = "sha256:377defd66ee2003748ee93c52bcef2d14fde48fe28a0b156f88c3dbf9bc49a50"}, + {file = "pydantic_core-2.41.1-cp312-cp312-win_arm64.whl", hash = "sha256:c95caff279d49c1d6cdfe2996e6c2ad712571d3b9caaa209a404426c326c4bde"}, + {file = "pydantic_core-2.41.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:70e790fce5f05204ef4403159857bfcd587779da78627b0babb3654f75361ebf"}, + {file = "pydantic_core-2.41.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9cebf1ca35f10930612d60bd0f78adfacee824c30a880e3534ba02c207cceceb"}, + {file = "pydantic_core-2.41.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:170406a37a5bc82c22c3274616bf6f17cc7df9c4a0a0a50449e559cb755db669"}, + {file = "pydantic_core-2.41.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:12d4257fc9187a0ccd41b8b327d6a4e57281ab75e11dda66a9148ef2e1fb712f"}, + {file = "pydantic_core-2.41.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a75a33b4db105dd1c8d57839e17ee12db8d5ad18209e792fa325dbb4baeb00f4"}, + {file = "pydantic_core-2.41.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:08a589f850803a74e0fcb16a72081cafb0d72a3cdda500106942b07e76b7bf62"}, + {file = "pydantic_core-2.41.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a97939d6ea44763c456bd8a617ceada2c9b96bb5b8ab3dfa0d0827df7619014"}, + {file = "pydantic_core-2.41.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d2ae423c65c556f09569524b80ffd11babff61f33055ef9773d7c9fabc11ed8d"}, + {file = "pydantic_core-2.41.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:4dc703015fbf8764d6a8001c327a87f1823b7328d40b47ce6000c65918ad2b4f"}, + {file = "pydantic_core-2.41.1-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:968e4ffdfd35698a5fe659e5e44c508b53664870a8e61c8f9d24d3d145d30257"}, + {file = "pydantic_core-2.41.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:fff2b76c8e172d34771cd4d4f0ade08072385310f214f823b5a6ad4006890d32"}, + {file = "pydantic_core-2.41.1-cp313-cp313-win32.whl", hash = "sha256:a38a5263185407ceb599f2f035faf4589d57e73c7146d64f10577f6449e8171d"}, + {file = "pydantic_core-2.41.1-cp313-cp313-win_amd64.whl", hash = "sha256:b42ae7fd6760782c975897e1fdc810f483b021b32245b0105d40f6e7a3803e4b"}, + {file = "pydantic_core-2.41.1-cp313-cp313-win_arm64.whl", hash = "sha256:ad4111acc63b7384e205c27a2f15e23ac0ee21a9d77ad6f2e9cb516ec90965fb"}, + {file = "pydantic_core-2.41.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:440d0df7415b50084a4ba9d870480c16c5f67c0d1d4d5119e3f70925533a0edc"}, + {file = "pydantic_core-2.41.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:71eaa38d342099405dae6484216dcf1e8e4b0bebd9b44a4e08c9b43db6a2ab67"}, + {file = "pydantic_core-2.41.1-cp313-cp313t-win_amd64.whl", hash = "sha256:555ecf7e50f1161d3f693bc49f23c82cf6cdeafc71fa37a06120772a09a38795"}, + {file = "pydantic_core-2.41.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:05226894a26f6f27e1deb735d7308f74ef5fa3a6de3e0135bb66cdcaee88f64b"}, + {file = "pydantic_core-2.41.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:85ff7911c6c3e2fd8d3779c50925f6406d770ea58ea6dde9c230d35b52b16b4a"}, + {file = "pydantic_core-2.41.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47f1f642a205687d59b52dc1a9a607f45e588f5a2e9eeae05edd80c7a8c47674"}, + {file = "pydantic_core-2.41.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:df11c24e138876ace5ec6043e5cae925e34cf38af1a1b3d63589e8f7b5f5cdc4"}, + {file = "pydantic_core-2.41.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7f0bf7f5c8f7bf345c527e8a0d72d6b26eda99c1227b0c34e7e59e181260de31"}, + {file = "pydantic_core-2.41.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:82b887a711d341c2c47352375d73b029418f55b20bd7815446d175a70effa706"}, + {file = "pydantic_core-2.41.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b5f1d5d6bbba484bdf220c72d8ecd0be460f4bd4c5e534a541bb2cd57589fb8b"}, + {file = "pydantic_core-2.41.1-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2bf1917385ebe0f968dc5c6ab1375886d56992b93ddfe6bf52bff575d03662be"}, + {file = "pydantic_core-2.41.1-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:4f94f3ab188f44b9a73f7295663f3ecb8f2e2dd03a69c8f2ead50d37785ecb04"}, + {file = "pydantic_core-2.41.1-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:3925446673641d37c30bd84a9d597e49f72eacee8b43322c8999fa17d5ae5bc4"}, + {file = "pydantic_core-2.41.1-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:49bd51cc27adb980c7b97357ae036ce9b3c4d0bb406e84fbe16fb2d368b602a8"}, + {file = "pydantic_core-2.41.1-cp314-cp314-win32.whl", hash = "sha256:a31ca0cd0e4d12ea0df0077df2d487fc3eb9d7f96bbb13c3c5b88dcc21d05159"}, + {file = "pydantic_core-2.41.1-cp314-cp314-win_amd64.whl", hash = "sha256:1b5c4374a152e10a22175d7790e644fbd8ff58418890e07e2073ff9d4414efae"}, + {file = "pydantic_core-2.41.1-cp314-cp314-win_arm64.whl", hash = "sha256:4fee76d757639b493eb600fba668f1e17475af34c17dd61db7a47e824d464ca9"}, + {file = "pydantic_core-2.41.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f9b9c968cfe5cd576fdd7361f47f27adeb120517e637d1b189eea1c3ece573f4"}, + {file = "pydantic_core-2.41.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1ebc7ab67b856384aba09ed74e3e977dded40e693de18a4f197c67d0d4e6d8e"}, + {file = "pydantic_core-2.41.1-cp314-cp314t-win_amd64.whl", hash = "sha256:8ae0dc57b62a762985bc7fbf636be3412394acc0ddb4ade07fe104230f1b9762"}, + {file = "pydantic_core-2.41.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:10ce489cf09a4956a1549af839b983edc59b0f60e1b068c21b10154e58f54f80"}, + {file = "pydantic_core-2.41.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ff548c908caffd9455fd1342366bcf8a1ec8a3fca42f35c7fc60883d6a901074"}, + {file = "pydantic_core-2.41.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d43bf082025082bda13be89a5f876cc2386b7727c7b322be2d2b706a45cea8e"}, + {file = "pydantic_core-2.41.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:666aee751faf1c6864b2db795775dd67b61fdcf646abefa309ed1da039a97209"}, + {file = "pydantic_core-2.41.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b83aaeff0d7bde852c32e856f3ee410842ebc08bc55c510771d87dcd1c01e1ed"}, + {file = "pydantic_core-2.41.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:055c7931b0329cb8acde20cdde6d9c2cbc2a02a0a8e54a792cddd91e2ea92c65"}, + {file = "pydantic_core-2.41.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:530bbb1347e3e5ca13a91ac087c4971d7da09630ef8febd27a20a10800c2d06d"}, + {file = "pydantic_core-2.41.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:65a0ea16cfea7bfa9e43604c8bd726e63a3788b61c384c37664b55209fcb1d74"}, + {file = "pydantic_core-2.41.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:8fa93fadff794c6d15c345c560513b160197342275c6d104cc879f932b978afc"}, + {file = "pydantic_core-2.41.1-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:c8a1af9ac51969a494c6a82b563abae6859dc082d3b999e8fa7ba5ee1b05e8e8"}, + {file = "pydantic_core-2.41.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:30edab28829703f876897c9471a857e43d847b8799c3c9e2fbce644724b50aa4"}, + {file = "pydantic_core-2.41.1-cp39-cp39-win32.whl", hash = "sha256:84d0ff869f98be2e93efdf1ae31e5a15f0926d22af8677d51676e373abbfe57a"}, + {file = "pydantic_core-2.41.1-cp39-cp39-win_amd64.whl", hash = "sha256:b5674314987cdde5a5511b029fa5fb1556b3d147a367e01dd583b19cfa8e35df"}, + {file = "pydantic_core-2.41.1-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:68f2251559b8efa99041bb63571ec7cdd2d715ba74cc82b3bc9eff824ebc8bf0"}, + {file = "pydantic_core-2.41.1-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:c7bc140c596097cb53b30546ca257dbe3f19282283190b1b5142928e5d5d3a20"}, + {file = "pydantic_core-2.41.1-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2896510fce8f4725ec518f8b9d7f015a00db249d2fd40788f442af303480063d"}, + {file = "pydantic_core-2.41.1-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ced20e62cfa0f496ba68fa5d6c7ee71114ea67e2a5da3114d6450d7f4683572a"}, + {file = "pydantic_core-2.41.1-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:b04fa9ed049461a7398138c604b00550bc89e3e1151d84b81ad6dc93e39c4c06"}, + {file = "pydantic_core-2.41.1-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:b3b7d9cfbfdc43c80a16638c6dc2768e3956e73031fca64e8e1a3ae744d1faeb"}, + {file = "pydantic_core-2.41.1-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eec83fc6abef04c7f9bec616e2d76ee9a6a4ae2a359b10c21d0f680e24a247ca"}, + {file = "pydantic_core-2.41.1-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6771a2d9f83c4038dfad5970a3eef215940682b2175e32bcc817bdc639019b28"}, + {file = "pydantic_core-2.41.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:fabcbdb12de6eada8d6e9a759097adb3c15440fafc675b3e94ae5c9cb8d678a0"}, + {file = "pydantic_core-2.41.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:80e97ccfaf0aaf67d55de5085b0ed0d994f57747d9d03f2de5cc9847ca737b08"}, + {file = "pydantic_core-2.41.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34df1fe8fea5d332484a763702e8b6a54048a9d4fe6ccf41e34a128238e01f52"}, + {file = "pydantic_core-2.41.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:421b5595f845842fc093f7250e24ee395f54ca62d494fdde96f43ecf9228ae01"}, + {file = "pydantic_core-2.41.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:dce8b22663c134583aaad24827863306a933f576c79da450be3984924e2031d1"}, + {file = "pydantic_core-2.41.1-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:300a9c162fea9906cc5c103893ca2602afd84f0ec90d3be36f4cc360125d22e1"}, + {file = "pydantic_core-2.41.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e019167628f6e6161ae7ab9fb70f6d076a0bf0d55aa9b20833f86a320c70dd65"}, + {file = "pydantic_core-2.41.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:13ab9cc2de6f9d4ab645a050ae5aee61a2424ac4d3a16ba23d4c2027705e0301"}, + {file = "pydantic_core-2.41.1-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:af2385d3f98243fb733862f806c5bb9122e5fba05b373e3af40e3c82d711cef1"}, + {file = "pydantic_core-2.41.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:6550617a0c2115be56f90c31a5370261d8ce9dbf051c3ed53b51172dd34da696"}, + {file = "pydantic_core-2.41.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc17b6ecf4983d298686014c92ebc955a9f9baf9f57dad4065e7906e7bee6222"}, + {file = "pydantic_core-2.41.1-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:42ae9352cf211f08b04ea110563d6b1e415878eea5b4c70f6bdb17dca3b932d2"}, + {file = "pydantic_core-2.41.1-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:e82947de92068b0a21681a13dd2102387197092fbe7defcfb8453e0913866506"}, + {file = "pydantic_core-2.41.1-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:e244c37d5471c9acdcd282890c6c4c83747b77238bfa19429b8473586c907656"}, + {file = "pydantic_core-2.41.1-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:1e798b4b304a995110d41ec93653e57975620ccb2842ba9420037985e7d7284e"}, + {file = "pydantic_core-2.41.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:f1fc716c0eb1663c59699b024428ad5ec2bcc6b928527b8fe28de6cb89f47efb"}, + {file = "pydantic_core-2.41.1.tar.gz", hash = "sha256:1ad375859a6d8c356b7704ec0f547a58e82ee80bb41baa811ad710e124bc8f2f"}, ] [package.dependencies] -typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" +typing-extensions = ">=4.14.1" + +[[package]] +name = "pygments" +version = "2.19.2" +description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b"}, + {file = "pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887"}, +] + +[package.extras] +windows-terminal = ["colorama (>=0.4.6)"] + +[[package]] +name = "pytest" +version = "8.4.2" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79"}, + {file = "pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01"}, +] + +[package.dependencies] +colorama = {version = ">=0.4", markers = "sys_platform == \"win32\""} +iniconfig = ">=1" +packaging = ">=20" +pluggy = ">=1.5,<2" +pygments = ">=2.7.2" + +[package.extras] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "pytest-asyncio" +version = "1.2.0" +description = "Pytest support for asyncio" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "pytest_asyncio-1.2.0-py3-none-any.whl", hash = "sha256:8e17ae5e46d8e7efe51ab6494dd2010f4ca8dae51652aa3c8d55acf50bfb2e99"}, + {file = "pytest_asyncio-1.2.0.tar.gz", hash = "sha256:c609a64a2a8768462d0c99811ddb8bd2583c33fd33cf7f21af1c142e824ffb57"}, +] + +[package.dependencies] +pytest = ">=8.2,<9" +typing-extensions = {version = ">=4.12", markers = "python_version < \"3.13\""} + +[package.extras] +docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1)"] +testing = ["coverage (>=6.2)", "hypothesis (>=5.7.1)"] [[package]] name = "python-dateutil" @@ -1038,30 +1502,30 @@ six = ">=1.5" [[package]] name = "ruff" -version = "0.9.2" +version = "0.9.10" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" groups = ["dev"] files = [ - {file = "ruff-0.9.2-py3-none-linux_armv6l.whl", hash = "sha256:80605a039ba1454d002b32139e4970becf84b5fee3a3c3bf1c2af6f61a784347"}, - {file = "ruff-0.9.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b9aab82bb20afd5f596527045c01e6ae25a718ff1784cb92947bff1f83068b00"}, - {file = "ruff-0.9.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:fbd337bac1cfa96be615f6efcd4bc4d077edbc127ef30e2b8ba2a27e18c054d4"}, - {file = "ruff-0.9.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82b35259b0cbf8daa22a498018e300b9bb0174c2bbb7bcba593935158a78054d"}, - {file = "ruff-0.9.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8b6a9701d1e371bf41dca22015c3f89769da7576884d2add7317ec1ec8cb9c3c"}, - {file = "ruff-0.9.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9cc53e68b3c5ae41e8faf83a3b89f4a5d7b2cb666dff4b366bb86ed2a85b481f"}, - {file = "ruff-0.9.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:8efd9da7a1ee314b910da155ca7e8953094a7c10d0c0a39bfde3fcfd2a015684"}, - {file = "ruff-0.9.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3292c5a22ea9a5f9a185e2d131dc7f98f8534a32fb6d2ee7b9944569239c648d"}, - {file = "ruff-0.9.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1a605fdcf6e8b2d39f9436d343d1f0ff70c365a1e681546de0104bef81ce88df"}, - {file = "ruff-0.9.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c547f7f256aa366834829a08375c297fa63386cbe5f1459efaf174086b564247"}, - {file = "ruff-0.9.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:d18bba3d3353ed916e882521bc3e0af403949dbada344c20c16ea78f47af965e"}, - {file = "ruff-0.9.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:b338edc4610142355ccf6b87bd356729b62bf1bc152a2fad5b0c7dc04af77bfe"}, - {file = "ruff-0.9.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:492a5e44ad9b22a0ea98cf72e40305cbdaf27fac0d927f8bc9e1df316dcc96eb"}, - {file = "ruff-0.9.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:af1e9e9fe7b1f767264d26b1075ac4ad831c7db976911fa362d09b2d0356426a"}, - {file = "ruff-0.9.2-py3-none-win32.whl", hash = "sha256:71cbe22e178c5da20e1514e1e01029c73dc09288a8028a5d3446e6bba87a5145"}, - {file = "ruff-0.9.2-py3-none-win_amd64.whl", hash = "sha256:c5e1d6abc798419cf46eed03f54f2e0c3adb1ad4b801119dedf23fcaf69b55b5"}, - {file = "ruff-0.9.2-py3-none-win_arm64.whl", hash = "sha256:a1b63fa24149918f8b37cef2ee6fff81f24f0d74b6f0bdc37bc3e1f2143e41c6"}, - {file = "ruff-0.9.2.tar.gz", hash = "sha256:b5eceb334d55fae5f316f783437392642ae18e16dcf4f1858d55d3c2a0f8f5d0"}, + {file = "ruff-0.9.10-py3-none-linux_armv6l.whl", hash = "sha256:eb4d25532cfd9fe461acc83498361ec2e2252795b4f40b17e80692814329e42d"}, + {file = "ruff-0.9.10-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:188a6638dab1aa9bb6228a7302387b2c9954e455fb25d6b4470cb0641d16759d"}, + {file = "ruff-0.9.10-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5284dcac6b9dbc2fcb71fdfc26a217b2ca4ede6ccd57476f52a587451ebe450d"}, + {file = "ruff-0.9.10-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47678f39fa2a3da62724851107f438c8229a3470f533894b5568a39b40029c0c"}, + {file = "ruff-0.9.10-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:99713a6e2766b7a17147b309e8c915b32b07a25c9efd12ada79f217c9c778b3e"}, + {file = "ruff-0.9.10-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:524ee184d92f7c7304aa568e2db20f50c32d1d0caa235d8ddf10497566ea1a12"}, + {file = "ruff-0.9.10-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:df92aeac30af821f9acf819fc01b4afc3dfb829d2782884f8739fb52a8119a16"}, + {file = "ruff-0.9.10-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de42e4edc296f520bb84954eb992a07a0ec5a02fecb834498415908469854a52"}, + {file = "ruff-0.9.10-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d257f95b65806104b6b1ffca0ea53f4ef98454036df65b1eda3693534813ecd1"}, + {file = "ruff-0.9.10-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b60dec7201c0b10d6d11be00e8f2dbb6f40ef1828ee75ed739923799513db24c"}, + {file = "ruff-0.9.10-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:d838b60007da7a39c046fcdd317293d10b845001f38bcb55ba766c3875b01e43"}, + {file = "ruff-0.9.10-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:ccaf903108b899beb8e09a63ffae5869057ab649c1e9231c05ae354ebc62066c"}, + {file = "ruff-0.9.10-py3-none-musllinux_1_2_i686.whl", hash = "sha256:f9567d135265d46e59d62dc60c0bfad10e9a6822e231f5b24032dba5a55be6b5"}, + {file = "ruff-0.9.10-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5f202f0d93738c28a89f8ed9eaba01b7be339e5d8d642c994347eaa81c6d75b8"}, + {file = "ruff-0.9.10-py3-none-win32.whl", hash = "sha256:bfb834e87c916521ce46b1788fbb8484966e5113c02df216680102e9eb960029"}, + {file = "ruff-0.9.10-py3-none-win_amd64.whl", hash = "sha256:f2160eeef3031bf4b17df74e307d4c5fb689a6f3a26a2de3f7ef4044e3c484f1"}, + {file = "ruff-0.9.10-py3-none-win_arm64.whl", hash = "sha256:5fd804c0327a5e5ea26615550e706942f348b197d5475ff34c19733aee4b2e69"}, + {file = "ruff-0.9.10.tar.gz", hash = "sha256:9bacb735d7bada9cfb0f2c227d3658fc443d90a727b47f206fb33f52f3c0eac7"}, ] [[package]] @@ -1090,62 +1554,85 @@ files = [ [[package]] name = "starlette" -version = "0.41.3" +version = "0.46.2" description = "The little ASGI library that shines." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["main"] files = [ - {file = "starlette-0.41.3-py3-none-any.whl", hash = "sha256:44cedb2b7c77a9de33a8b74b2b90e9f50d11fcf25d8270ea525ad71a25374ff7"}, - {file = "starlette-0.41.3.tar.gz", hash = "sha256:0e4ab3d16522a255be6b28260b938eae2482f98ce5cc934cb08dce8dc3ba5835"}, + {file = "starlette-0.46.2-py3-none-any.whl", hash = "sha256:595633ce89f8ffa71a015caed34a5b2dc1c0cdb3f0f1fbd1e69339cf2abeec35"}, + {file = "starlette-0.46.2.tar.gz", hash = "sha256:7f7361f34eed179294600af672f565727419830b54b7b084efe44bb82d2fccd5"}, ] [package.dependencies] -anyio = ">=3.4.0,<5" +anyio = ">=3.6.2,<5" [package.extras] -full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.7)", "pyyaml"] +full = ["httpx (>=0.27.0,<0.29.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.18)", "pyyaml"] [[package]] name = "structlog" -version = "25.1.0" +version = "25.4.0" description = "Structured Logging for Python" optional = false python-versions = ">=3.8" groups = ["main"] files = [ - {file = "structlog-25.1.0-py3-none-any.whl", hash = "sha256:843fe4f254540329f380812cbe612e1af5ec5b8172205ae634679cd35a6d6321"}, - {file = "structlog-25.1.0.tar.gz", hash = "sha256:2ef2a572e0e27f09664965d31a576afe64e46ac6084ef5cec3c2b8cd6e4e3ad3"}, + {file = "structlog-25.4.0-py3-none-any.whl", hash = "sha256:fe809ff5c27e557d14e613f45ca441aabda051d119ee5a0102aaba6ce40eed2c"}, + {file = "structlog-25.4.0.tar.gz", hash = "sha256:186cd1b0a8ae762e29417095664adf1d6a31702160a46dacb7796ea82f7409e4"}, ] -[package.extras] -dev = ["freezegun (>=0.2.8)", "mypy (>=1.4)", "pretend", "pytest (>=6.0)", "pytest-asyncio (>=0.17)", "rich", "simplejson", "twisted"] -docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-mermaid", "sphinxext-opengraph", "twisted"] -tests = ["freezegun (>=0.2.8)", "pretend", "pytest (>=6.0)", "pytest-asyncio (>=0.17)", "simplejson"] -typing = ["mypy (>=1.4)", "rich", "twisted"] - [[package]] name = "typing-extensions" -version = "4.12.2" -description = "Backported and Experimental Type Hints for Python 3.8+" +version = "4.15.0" +description = "Backported and Experimental Type Hints for Python 3.9+" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" +groups = ["main", "dev"] +files = [ + {file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"}, + {file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"}, +] +markers = {dev = "python_version < \"3.13\""} + +[[package]] +name = "typing-inspection" +version = "0.4.2" +description = "Runtime typing introspection tools" +optional = false +python-versions = ">=3.9" groups = ["main"] files = [ - {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, - {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, + {file = "typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7"}, + {file = "typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464"}, +] + +[package.dependencies] +typing-extensions = ">=4.12.0" + +[[package]] +name = "tzdata" +version = "2025.2" +description = "Provider of IANA time zone data" +optional = false +python-versions = ">=2" +groups = ["main"] +markers = "sys_platform == \"win32\"" +files = [ + {file = "tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8"}, + {file = "tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9"}, ] [[package]] name = "urllib3" -version = "2.3.0" +version = "2.5.0" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df"}, - {file = "urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d"}, + {file = "urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc"}, + {file = "urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760"}, ] [package.extras] @@ -1156,102 +1643,150 @@ zstd = ["zstandard (>=0.18.0)"] [[package]] name = "yarl" -version = "1.18.3" +version = "1.22.0" description = "Yet another URL library" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "yarl-1.18.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7df647e8edd71f000a5208fe6ff8c382a1de8edfbccdbbfe649d263de07d8c34"}, - {file = "yarl-1.18.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c69697d3adff5aa4f874b19c0e4ed65180ceed6318ec856ebc423aa5850d84f7"}, - {file = "yarl-1.18.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:602d98f2c2d929f8e697ed274fbadc09902c4025c5a9963bf4e9edfc3ab6f7ed"}, - {file = "yarl-1.18.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c654d5207c78e0bd6d749f6dae1dcbbfde3403ad3a4b11f3c5544d9906969dde"}, - {file = "yarl-1.18.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5094d9206c64181d0f6e76ebd8fb2f8fe274950a63890ee9e0ebfd58bf9d787b"}, - {file = "yarl-1.18.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:35098b24e0327fc4ebdc8ffe336cee0a87a700c24ffed13161af80124b7dc8e5"}, - {file = "yarl-1.18.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3236da9272872443f81fedc389bace88408f64f89f75d1bdb2256069a8730ccc"}, - {file = "yarl-1.18.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e2c08cc9b16f4f4bc522771d96734c7901e7ebef70c6c5c35dd0f10845270bcd"}, - {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:80316a8bd5109320d38eef8833ccf5f89608c9107d02d2a7f985f98ed6876990"}, - {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:c1e1cc06da1491e6734f0ea1e6294ce00792193c463350626571c287c9a704db"}, - {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fea09ca13323376a2fdfb353a5fa2e59f90cd18d7ca4eaa1fd31f0a8b4f91e62"}, - {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:e3b9fd71836999aad54084906f8663dffcd2a7fb5cdafd6c37713b2e72be1760"}, - {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:757e81cae69244257d125ff31663249b3013b5dc0a8520d73694aed497fb195b"}, - {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b1771de9944d875f1b98a745bc547e684b863abf8f8287da8466cf470ef52690"}, - {file = "yarl-1.18.3-cp310-cp310-win32.whl", hash = "sha256:8874027a53e3aea659a6d62751800cf6e63314c160fd607489ba5c2edd753cf6"}, - {file = "yarl-1.18.3-cp310-cp310-win_amd64.whl", hash = "sha256:93b2e109287f93db79210f86deb6b9bbb81ac32fc97236b16f7433db7fc437d8"}, - {file = "yarl-1.18.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8503ad47387b8ebd39cbbbdf0bf113e17330ffd339ba1144074da24c545f0069"}, - {file = "yarl-1.18.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:02ddb6756f8f4517a2d5e99d8b2f272488e18dd0bfbc802f31c16c6c20f22193"}, - {file = "yarl-1.18.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:67a283dd2882ac98cc6318384f565bffc751ab564605959df4752d42483ad889"}, - {file = "yarl-1.18.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d980e0325b6eddc81331d3f4551e2a333999fb176fd153e075c6d1c2530aa8a8"}, - {file = "yarl-1.18.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b643562c12680b01e17239be267bc306bbc6aac1f34f6444d1bded0c5ce438ca"}, - {file = "yarl-1.18.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c017a3b6df3a1bd45b9fa49a0f54005e53fbcad16633870104b66fa1a30a29d8"}, - {file = "yarl-1.18.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75674776d96d7b851b6498f17824ba17849d790a44d282929c42dbb77d4f17ae"}, - {file = "yarl-1.18.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ccaa3a4b521b780a7e771cc336a2dba389a0861592bbce09a476190bb0c8b4b3"}, - {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2d06d3005e668744e11ed80812e61efd77d70bb7f03e33c1598c301eea20efbb"}, - {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:9d41beda9dc97ca9ab0b9888cb71f7539124bc05df02c0cff6e5acc5a19dcc6e"}, - {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ba23302c0c61a9999784e73809427c9dbedd79f66a13d84ad1b1943802eaaf59"}, - {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:6748dbf9bfa5ba1afcc7556b71cda0d7ce5f24768043a02a58846e4a443d808d"}, - {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0b0cad37311123211dc91eadcb322ef4d4a66008d3e1bdc404808992260e1a0e"}, - {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0fb2171a4486bb075316ee754c6d8382ea6eb8b399d4ec62fde2b591f879778a"}, - {file = "yarl-1.18.3-cp311-cp311-win32.whl", hash = "sha256:61b1a825a13bef4a5f10b1885245377d3cd0bf87cba068e1d9a88c2ae36880e1"}, - {file = "yarl-1.18.3-cp311-cp311-win_amd64.whl", hash = "sha256:b9d60031cf568c627d028239693fd718025719c02c9f55df0a53e587aab951b5"}, - {file = "yarl-1.18.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1dd4bdd05407ced96fed3d7f25dbbf88d2ffb045a0db60dbc247f5b3c5c25d50"}, - {file = "yarl-1.18.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7c33dd1931a95e5d9a772d0ac5e44cac8957eaf58e3c8da8c1414de7dd27c576"}, - {file = "yarl-1.18.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:25b411eddcfd56a2f0cd6a384e9f4f7aa3efee14b188de13048c25b5e91f1640"}, - {file = "yarl-1.18.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:436c4fc0a4d66b2badc6c5fc5ef4e47bb10e4fd9bf0c79524ac719a01f3607c2"}, - {file = "yarl-1.18.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e35ef8683211db69ffe129a25d5634319a677570ab6b2eba4afa860f54eeaf75"}, - {file = "yarl-1.18.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:84b2deecba4a3f1a398df819151eb72d29bfeb3b69abb145a00ddc8d30094512"}, - {file = "yarl-1.18.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00e5a1fea0fd4f5bfa7440a47eff01d9822a65b4488f7cff83155a0f31a2ecba"}, - {file = "yarl-1.18.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d0e883008013c0e4aef84dcfe2a0b172c4d23c2669412cf5b3371003941f72bb"}, - {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5a3f356548e34a70b0172d8890006c37be92995f62d95a07b4a42e90fba54272"}, - {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ccd17349166b1bee6e529b4add61727d3f55edb7babbe4069b5764c9587a8cc6"}, - {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b958ddd075ddba5b09bb0be8a6d9906d2ce933aee81100db289badbeb966f54e"}, - {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c7d79f7d9aabd6011004e33b22bc13056a3e3fb54794d138af57f5ee9d9032cb"}, - {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:4891ed92157e5430874dad17b15eb1fda57627710756c27422200c52d8a4e393"}, - {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ce1af883b94304f493698b00d0f006d56aea98aeb49d75ec7d98cd4a777e9285"}, - {file = "yarl-1.18.3-cp312-cp312-win32.whl", hash = "sha256:f91c4803173928a25e1a55b943c81f55b8872f0018be83e3ad4938adffb77dd2"}, - {file = "yarl-1.18.3-cp312-cp312-win_amd64.whl", hash = "sha256:7e2ee16578af3b52ac2f334c3b1f92262f47e02cc6193c598502bd46f5cd1477"}, - {file = "yarl-1.18.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:90adb47ad432332d4f0bc28f83a5963f426ce9a1a8809f5e584e704b82685dcb"}, - {file = "yarl-1.18.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:913829534200eb0f789d45349e55203a091f45c37a2674678744ae52fae23efa"}, - {file = "yarl-1.18.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ef9f7768395923c3039055c14334ba4d926f3baf7b776c923c93d80195624782"}, - {file = "yarl-1.18.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88a19f62ff30117e706ebc9090b8ecc79aeb77d0b1f5ec10d2d27a12bc9f66d0"}, - {file = "yarl-1.18.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e17c9361d46a4d5addf777c6dd5eab0715a7684c2f11b88c67ac37edfba6c482"}, - {file = "yarl-1.18.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1a74a13a4c857a84a845505fd2d68e54826a2cd01935a96efb1e9d86c728e186"}, - {file = "yarl-1.18.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:41f7ce59d6ee7741af71d82020346af364949314ed3d87553763a2df1829cc58"}, - {file = "yarl-1.18.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f52a265001d830bc425f82ca9eabda94a64a4d753b07d623a9f2863fde532b53"}, - {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:82123d0c954dc58db301f5021a01854a85bf1f3bb7d12ae0c01afc414a882ca2"}, - {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:2ec9bbba33b2d00999af4631a3397d1fd78290c48e2a3e52d8dd72db3a067ac8"}, - {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:fbd6748e8ab9b41171bb95c6142faf068f5ef1511935a0aa07025438dd9a9bc1"}, - {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:877d209b6aebeb5b16c42cbb377f5f94d9e556626b1bfff66d7b0d115be88d0a"}, - {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b464c4ab4bfcb41e3bfd3f1c26600d038376c2de3297760dfe064d2cb7ea8e10"}, - {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8d39d351e7faf01483cc7ff7c0213c412e38e5a340238826be7e0e4da450fdc8"}, - {file = "yarl-1.18.3-cp313-cp313-win32.whl", hash = "sha256:61ee62ead9b68b9123ec24bc866cbef297dd266175d53296e2db5e7f797f902d"}, - {file = "yarl-1.18.3-cp313-cp313-win_amd64.whl", hash = "sha256:578e281c393af575879990861823ef19d66e2b1d0098414855dd367e234f5b3c"}, - {file = "yarl-1.18.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:61e5e68cb65ac8f547f6b5ef933f510134a6bf31bb178be428994b0cb46c2a04"}, - {file = "yarl-1.18.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fe57328fbc1bfd0bd0514470ac692630f3901c0ee39052ae47acd1d90a436719"}, - {file = "yarl-1.18.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a440a2a624683108a1b454705ecd7afc1c3438a08e890a1513d468671d90a04e"}, - {file = "yarl-1.18.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09c7907c8548bcd6ab860e5f513e727c53b4a714f459b084f6580b49fa1b9cee"}, - {file = "yarl-1.18.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b4f6450109834af88cb4cc5ecddfc5380ebb9c228695afc11915a0bf82116789"}, - {file = "yarl-1.18.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9ca04806f3be0ac6d558fffc2fdf8fcef767e0489d2684a21912cc4ed0cd1b8"}, - {file = "yarl-1.18.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77a6e85b90a7641d2e07184df5557132a337f136250caafc9ccaa4a2a998ca2c"}, - {file = "yarl-1.18.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6333c5a377c8e2f5fae35e7b8f145c617b02c939d04110c76f29ee3676b5f9a5"}, - {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0b3c92fa08759dbf12b3a59579a4096ba9af8dd344d9a813fc7f5070d86bbab1"}, - {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:4ac515b860c36becb81bb84b667466885096b5fc85596948548b667da3bf9f24"}, - {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:045b8482ce9483ada4f3f23b3774f4e1bf4f23a2d5c912ed5170f68efb053318"}, - {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:a4bb030cf46a434ec0225bddbebd4b89e6471814ca851abb8696170adb163985"}, - {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:54d6921f07555713b9300bee9c50fb46e57e2e639027089b1d795ecd9f7fa910"}, - {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1d407181cfa6e70077df3377938c08012d18893f9f20e92f7d2f314a437c30b1"}, - {file = "yarl-1.18.3-cp39-cp39-win32.whl", hash = "sha256:ac36703a585e0929b032fbaab0707b75dc12703766d0b53486eabd5139ebadd5"}, - {file = "yarl-1.18.3-cp39-cp39-win_amd64.whl", hash = "sha256:ba87babd629f8af77f557b61e49e7c7cac36f22f871156b91e10a6e9d4f829e9"}, - {file = "yarl-1.18.3-py3-none-any.whl", hash = "sha256:b57f4f58099328dfb26c6a771d09fb20dbbae81d20cfb66141251ea063bd101b"}, - {file = "yarl-1.18.3.tar.gz", hash = "sha256:ac1801c45cbf77b6c99242eeff4fffb5e4e73a800b5c4ad4fc0be5def634d2e1"}, + {file = "yarl-1.22.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c7bd6683587567e5a49ee6e336e0612bec8329be1b7d4c8af5687dcdeb67ee1e"}, + {file = "yarl-1.22.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5cdac20da754f3a723cceea5b3448e1a2074866406adeb4ef35b469d089adb8f"}, + {file = "yarl-1.22.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:07a524d84df0c10f41e3ee918846e1974aba4ec017f990dc735aad487a0bdfdf"}, + {file = "yarl-1.22.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e1b329cb8146d7b736677a2440e422eadd775d1806a81db2d4cded80a48efc1a"}, + {file = "yarl-1.22.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:75976c6945d85dbb9ee6308cd7ff7b1fb9409380c82d6119bd778d8fcfe2931c"}, + {file = "yarl-1.22.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:80ddf7a5f8c86cb3eb4bc9028b07bbbf1f08a96c5c0bc1244be5e8fefcb94147"}, + {file = "yarl-1.22.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d332fc2e3c94dad927f2112395772a4e4fedbcf8f80efc21ed7cdfae4d574fdb"}, + {file = "yarl-1.22.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0cf71bf877efeac18b38d3930594c0948c82b64547c1cf420ba48722fe5509f6"}, + {file = "yarl-1.22.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:663e1cadaddae26be034a6ab6072449a8426ddb03d500f43daf952b74553bba0"}, + {file = "yarl-1.22.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:6dcbb0829c671f305be48a7227918cfcd11276c2d637a8033a99a02b67bf9eda"}, + {file = "yarl-1.22.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f0d97c18dfd9a9af4490631905a3f131a8e4c9e80a39353919e2cfed8f00aedc"}, + {file = "yarl-1.22.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:437840083abe022c978470b942ff832c3940b2ad3734d424b7eaffcd07f76737"}, + {file = "yarl-1.22.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a899cbd98dce6f5d8de1aad31cb712ec0a530abc0a86bd6edaa47c1090138467"}, + {file = "yarl-1.22.0-cp310-cp310-win32.whl", hash = "sha256:595697f68bd1f0c1c159fcb97b661fc9c3f5db46498043555d04805430e79bea"}, + {file = "yarl-1.22.0-cp310-cp310-win_amd64.whl", hash = "sha256:cb95a9b1adaa48e41815a55ae740cfda005758104049a640a398120bf02515ca"}, + {file = "yarl-1.22.0-cp310-cp310-win_arm64.whl", hash = "sha256:b85b982afde6df99ecc996990d4ad7ccbdbb70e2a4ba4de0aecde5922ba98a0b"}, + {file = "yarl-1.22.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1ab72135b1f2db3fed3997d7e7dc1b80573c67138023852b6efb336a5eae6511"}, + {file = "yarl-1.22.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:669930400e375570189492dc8d8341301578e8493aec04aebc20d4717f899dd6"}, + {file = "yarl-1.22.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:792a2af6d58177ef7c19cbf0097aba92ca1b9cb3ffdd9c7470e156c8f9b5e028"}, + {file = "yarl-1.22.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3ea66b1c11c9150f1372f69afb6b8116f2dd7286f38e14ea71a44eee9ec51b9d"}, + {file = "yarl-1.22.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3e2daa88dc91870215961e96a039ec73e4937da13cf77ce17f9cad0c18df3503"}, + {file = "yarl-1.22.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ba440ae430c00eee41509353628600212112cd5018d5def7e9b05ea7ac34eb65"}, + {file = "yarl-1.22.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e6438cc8f23a9c1478633d216b16104a586b9761db62bfacb6425bac0a36679e"}, + {file = "yarl-1.22.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c52a6e78aef5cf47a98ef8e934755abf53953379b7d53e68b15ff4420e6683d"}, + {file = "yarl-1.22.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3b06bcadaac49c70f4c88af4ffcfbe3dc155aab3163e75777818092478bcbbe7"}, + {file = "yarl-1.22.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:6944b2dc72c4d7f7052683487e3677456050ff77fcf5e6204e98caf785ad1967"}, + {file = "yarl-1.22.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:d5372ca1df0f91a86b047d1277c2aaf1edb32d78bbcefffc81b40ffd18f027ed"}, + {file = "yarl-1.22.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:51af598701f5299012b8416486b40fceef8c26fc87dc6d7d1f6fc30609ea0aa6"}, + {file = "yarl-1.22.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b266bd01fedeffeeac01a79ae181719ff848a5a13ce10075adbefc8f1daee70e"}, + {file = "yarl-1.22.0-cp311-cp311-win32.whl", hash = "sha256:a9b1ba5610a4e20f655258d5a1fdc7ebe3d837bb0e45b581398b99eb98b1f5ca"}, + {file = "yarl-1.22.0-cp311-cp311-win_amd64.whl", hash = "sha256:078278b9b0b11568937d9509b589ee83ef98ed6d561dfe2020e24a9fd08eaa2b"}, + {file = "yarl-1.22.0-cp311-cp311-win_arm64.whl", hash = "sha256:b6a6f620cfe13ccec221fa312139135166e47ae169f8253f72a0abc0dae94376"}, + {file = "yarl-1.22.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e340382d1afa5d32b892b3ff062436d592ec3d692aeea3bef3a5cfe11bbf8c6f"}, + {file = "yarl-1.22.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f1e09112a2c31ffe8d80be1b0988fa6a18c5d5cad92a9ffbb1c04c91bfe52ad2"}, + {file = "yarl-1.22.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:939fe60db294c786f6b7c2d2e121576628468f65453d86b0fe36cb52f987bd74"}, + {file = "yarl-1.22.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e1651bf8e0398574646744c1885a41198eba53dc8a9312b954073f845c90a8df"}, + {file = "yarl-1.22.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b8a0588521a26bf92a57a1705b77b8b59044cdceccac7151bd8d229e66b8dedb"}, + {file = "yarl-1.22.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:42188e6a615c1a75bcaa6e150c3fe8f3e8680471a6b10150c5f7e83f47cc34d2"}, + {file = "yarl-1.22.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f6d2cb59377d99718913ad9a151030d6f83ef420a2b8f521d94609ecc106ee82"}, + {file = "yarl-1.22.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50678a3b71c751d58d7908edc96d332af328839eea883bb554a43f539101277a"}, + {file = "yarl-1.22.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1e8fbaa7cec507aa24ea27a01456e8dd4b6fab829059b69844bd348f2d467124"}, + {file = "yarl-1.22.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:433885ab5431bc3d3d4f2f9bd15bfa1614c522b0f1405d62c4f926ccd69d04fa"}, + {file = "yarl-1.22.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:b790b39c7e9a4192dc2e201a282109ed2985a1ddbd5ac08dc56d0e121400a8f7"}, + {file = "yarl-1.22.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:31f0b53913220599446872d757257be5898019c85e7971599065bc55065dc99d"}, + {file = "yarl-1.22.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a49370e8f711daec68d09b821a34e1167792ee2d24d405cbc2387be4f158b520"}, + {file = "yarl-1.22.0-cp312-cp312-win32.whl", hash = "sha256:70dfd4f241c04bd9239d53b17f11e6ab672b9f1420364af63e8531198e3f5fe8"}, + {file = "yarl-1.22.0-cp312-cp312-win_amd64.whl", hash = "sha256:8884d8b332a5e9b88e23f60bb166890009429391864c685e17bd73a9eda9105c"}, + {file = "yarl-1.22.0-cp312-cp312-win_arm64.whl", hash = "sha256:ea70f61a47f3cc93bdf8b2f368ed359ef02a01ca6393916bc8ff877427181e74"}, + {file = "yarl-1.22.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8dee9c25c74997f6a750cd317b8ca63545169c098faee42c84aa5e506c819b53"}, + {file = "yarl-1.22.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:01e73b85a5434f89fc4fe27dcda2aff08ddf35e4d47bbbea3bdcd25321af538a"}, + {file = "yarl-1.22.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:22965c2af250d20c873cdbee8ff958fb809940aeb2e74ba5f20aaf6b7ac8c70c"}, + {file = "yarl-1.22.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b4f15793aa49793ec8d1c708ab7f9eded1aa72edc5174cae703651555ed1b601"}, + {file = "yarl-1.22.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5542339dcf2747135c5c85f68680353d5cb9ffd741c0f2e8d832d054d41f35a"}, + {file = "yarl-1.22.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5c401e05ad47a75869c3ab3e35137f8468b846770587e70d71e11de797d113df"}, + {file = "yarl-1.22.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:243dda95d901c733f5b59214d28b0120893d91777cb8aa043e6ef059d3cddfe2"}, + {file = "yarl-1.22.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bec03d0d388060058f5d291a813f21c011041938a441c593374da6077fe21b1b"}, + {file = "yarl-1.22.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b0748275abb8c1e1e09301ee3cf90c8a99678a4e92e4373705f2a2570d581273"}, + {file = "yarl-1.22.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:47fdb18187e2a4e18fda2c25c05d8251a9e4a521edaed757fef033e7d8498d9a"}, + {file = "yarl-1.22.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c7044802eec4524fde550afc28edda0dd5784c4c45f0be151a2d3ba017daca7d"}, + {file = "yarl-1.22.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:139718f35149ff544caba20fce6e8a2f71f1e39b92c700d8438a0b1d2a631a02"}, + {file = "yarl-1.22.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e1b51bebd221006d3d2f95fbe124b22b247136647ae5dcc8c7acafba66e5ee67"}, + {file = "yarl-1.22.0-cp313-cp313-win32.whl", hash = "sha256:d3e32536234a95f513bd374e93d717cf6b2231a791758de6c509e3653f234c95"}, + {file = "yarl-1.22.0-cp313-cp313-win_amd64.whl", hash = "sha256:47743b82b76d89a1d20b83e60d5c20314cbd5ba2befc9cda8f28300c4a08ed4d"}, + {file = "yarl-1.22.0-cp313-cp313-win_arm64.whl", hash = "sha256:5d0fcda9608875f7d052eff120c7a5da474a6796fe4d83e152e0e4d42f6d1a9b"}, + {file = "yarl-1.22.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:719ae08b6972befcba4310e49edb1161a88cdd331e3a694b84466bd938a6ab10"}, + {file = "yarl-1.22.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:47d8a5c446df1c4db9d21b49619ffdba90e77c89ec6e283f453856c74b50b9e3"}, + {file = "yarl-1.22.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:cfebc0ac8333520d2d0423cbbe43ae43c8838862ddb898f5ca68565e395516e9"}, + {file = "yarl-1.22.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4398557cbf484207df000309235979c79c4356518fd5c99158c7d38203c4da4f"}, + {file = "yarl-1.22.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2ca6fd72a8cd803be290d42f2dec5cdcd5299eeb93c2d929bf060ad9efaf5de0"}, + {file = "yarl-1.22.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ca1f59c4e1ab6e72f0a23c13fca5430f889634166be85dbf1013683e49e3278e"}, + {file = "yarl-1.22.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6c5010a52015e7c70f86eb967db0f37f3c8bd503a695a49f8d45700144667708"}, + {file = "yarl-1.22.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d7672ecf7557476642c88497c2f8d8542f8e36596e928e9bcba0e42e1e7d71f"}, + {file = "yarl-1.22.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:3b7c88eeef021579d600e50363e0b6ee4f7f6f728cd3486b9d0f3ee7b946398d"}, + {file = "yarl-1.22.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:f4afb5c34f2c6fecdcc182dfcfc6af6cccf1aa923eed4d6a12e9d96904e1a0d8"}, + {file = "yarl-1.22.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:59c189e3e99a59cf8d83cbb31d4db02d66cda5a1a4374e8a012b51255341abf5"}, + {file = "yarl-1.22.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:5a3bf7f62a289fa90f1990422dc8dff5a458469ea71d1624585ec3a4c8d6960f"}, + {file = "yarl-1.22.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:de6b9a04c606978fdfe72666fa216ffcf2d1a9f6a381058d4378f8d7b1e5de62"}, + {file = "yarl-1.22.0-cp313-cp313t-win32.whl", hash = "sha256:1834bb90991cc2999f10f97f5f01317f99b143284766d197e43cd5b45eb18d03"}, + {file = "yarl-1.22.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ff86011bd159a9d2dfc89c34cfd8aff12875980e3bd6a39ff097887520e60249"}, + {file = "yarl-1.22.0-cp313-cp313t-win_arm64.whl", hash = "sha256:7861058d0582b847bc4e3a4a4c46828a410bca738673f35a29ba3ca5db0b473b"}, + {file = "yarl-1.22.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:34b36c2c57124530884d89d50ed2c1478697ad7473efd59cfd479945c95650e4"}, + {file = "yarl-1.22.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:0dd9a702591ca2e543631c2a017e4a547e38a5c0f29eece37d9097e04a7ac683"}, + {file = "yarl-1.22.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:594fcab1032e2d2cc3321bb2e51271e7cd2b516c7d9aee780ece81b07ff8244b"}, + {file = "yarl-1.22.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f3d7a87a78d46a2e3d5b72587ac14b4c16952dd0887dbb051451eceac774411e"}, + {file = "yarl-1.22.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:852863707010316c973162e703bddabec35e8757e67fcb8ad58829de1ebc8590"}, + {file = "yarl-1.22.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:131a085a53bfe839a477c0845acf21efc77457ba2bcf5899618136d64f3303a2"}, + {file = "yarl-1.22.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:078a8aefd263f4d4f923a9677b942b445a2be970ca24548a8102689a3a8ab8da"}, + {file = "yarl-1.22.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bca03b91c323036913993ff5c738d0842fc9c60c4648e5c8d98331526df89784"}, + {file = "yarl-1.22.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:68986a61557d37bb90d3051a45b91fa3d5c516d177dfc6dd6f2f436a07ff2b6b"}, + {file = "yarl-1.22.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:4792b262d585ff0dff6bcb787f8492e40698443ec982a3568c2096433660c694"}, + {file = "yarl-1.22.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:ebd4549b108d732dba1d4ace67614b9545b21ece30937a63a65dd34efa19732d"}, + {file = "yarl-1.22.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f87ac53513d22240c7d59203f25cc3beac1e574c6cd681bbfd321987b69f95fd"}, + {file = "yarl-1.22.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:22b029f2881599e2f1b06f8f1db2ee63bd309e2293ba2d566e008ba12778b8da"}, + {file = "yarl-1.22.0-cp314-cp314-win32.whl", hash = "sha256:6a635ea45ba4ea8238463b4f7d0e721bad669f80878b7bfd1f89266e2ae63da2"}, + {file = "yarl-1.22.0-cp314-cp314-win_amd64.whl", hash = "sha256:0d6e6885777af0f110b0e5d7e5dda8b704efed3894da26220b7f3d887b839a79"}, + {file = "yarl-1.22.0-cp314-cp314-win_arm64.whl", hash = "sha256:8218f4e98d3c10d683584cb40f0424f4b9fd6e95610232dd75e13743b070ee33"}, + {file = "yarl-1.22.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:45c2842ff0e0d1b35a6bf1cd6c690939dacb617a70827f715232b2e0494d55d1"}, + {file = "yarl-1.22.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:d947071e6ebcf2e2bee8fce76e10faca8f7a14808ca36a910263acaacef08eca"}, + {file = "yarl-1.22.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:334b8721303e61b00019474cc103bdac3d7b1f65e91f0bfedeec2d56dfe74b53"}, + {file = "yarl-1.22.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e7ce67c34138a058fd092f67d07a72b8e31ff0c9236e751957465a24b28910c"}, + {file = "yarl-1.22.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d77e1b2c6d04711478cb1c4ab90db07f1609ccf06a287d5607fcd90dc9863acf"}, + {file = "yarl-1.22.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4647674b6150d2cae088fc07de2738a84b8bcedebef29802cf0b0a82ab6face"}, + {file = "yarl-1.22.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:efb07073be061c8f79d03d04139a80ba33cbd390ca8f0297aae9cce6411e4c6b"}, + {file = "yarl-1.22.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e51ac5435758ba97ad69617e13233da53908beccc6cfcd6c34bbed8dcbede486"}, + {file = "yarl-1.22.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:33e32a0dd0c8205efa8e83d04fc9f19313772b78522d1bdc7d9aed706bfd6138"}, + {file = "yarl-1.22.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:bf4a21e58b9cde0e401e683ebd00f6ed30a06d14e93f7c8fd059f8b6e8f87b6a"}, + {file = "yarl-1.22.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:e4b582bab49ac33c8deb97e058cd67c2c50dac0dd134874106d9c774fd272529"}, + {file = "yarl-1.22.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:0b5bcc1a9c4839e7e30b7b30dd47fe5e7e44fb7054ec29b5bb8d526aa1041093"}, + {file = "yarl-1.22.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:c0232bce2170103ec23c454e54a57008a9a72b5d1c3105dc2496750da8cfa47c"}, + {file = "yarl-1.22.0-cp314-cp314t-win32.whl", hash = "sha256:8009b3173bcd637be650922ac455946197d858b3630b6d8787aa9e5c4564533e"}, + {file = "yarl-1.22.0-cp314-cp314t-win_amd64.whl", hash = "sha256:9fb17ea16e972c63d25d4a97f016d235c78dd2344820eb35bc034bc32012ee27"}, + {file = "yarl-1.22.0-cp314-cp314t-win_arm64.whl", hash = "sha256:9f6d73c1436b934e3f01df1e1b21ff765cd1d28c77dfb9ace207f746d4610ee1"}, + {file = "yarl-1.22.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3aa27acb6de7a23785d81557577491f6c38a5209a254d1191519d07d8fe51748"}, + {file = "yarl-1.22.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:af74f05666a5e531289cb1cc9c883d1de2088b8e5b4de48004e5ca8a830ac859"}, + {file = "yarl-1.22.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:62441e55958977b8167b2709c164c91a6363e25da322d87ae6dd9c6019ceecf9"}, + {file = "yarl-1.22.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b580e71cac3f8113d3135888770903eaf2f507e9421e5697d6ee6d8cd1c7f054"}, + {file = "yarl-1.22.0-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e81fda2fb4a07eda1a2252b216aa0df23ebcd4d584894e9612e80999a78fd95b"}, + {file = "yarl-1.22.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:99b6fc1d55782461b78221e95fc357b47ad98b041e8e20f47c1411d0aacddc60"}, + {file = "yarl-1.22.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:088e4e08f033db4be2ccd1f34cf29fe994772fb54cfe004bbf54db320af56890"}, + {file = "yarl-1.22.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e4e1f6f0b4da23e61188676e3ed027ef0baa833a2e633c29ff8530800edccba"}, + {file = "yarl-1.22.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:84fc3ec96fce86ce5aa305eb4aa9358279d1aa644b71fab7b8ed33fe3ba1a7ca"}, + {file = "yarl-1.22.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:5dbeefd6ca588b33576a01b0ad58aa934bc1b41ef89dee505bf2932b22ddffba"}, + {file = "yarl-1.22.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:14291620375b1060613f4aab9ebf21850058b6b1b438f386cc814813d901c60b"}, + {file = "yarl-1.22.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:a4fcfc8eb2c34148c118dfa02e6427ca278bfd0f3df7c5f99e33d2c0e81eae3e"}, + {file = "yarl-1.22.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:029866bde8d7b0878b9c160e72305bbf0a7342bcd20b9999381704ae03308dc8"}, + {file = "yarl-1.22.0-cp39-cp39-win32.whl", hash = "sha256:4dcc74149ccc8bba31ce1944acee24813e93cfdee2acda3c172df844948ddf7b"}, + {file = "yarl-1.22.0-cp39-cp39-win_amd64.whl", hash = "sha256:10619d9fdee46d20edc49d3479e2f8269d0779f1b031e6f7c2aa1c76be04b7ed"}, + {file = "yarl-1.22.0-cp39-cp39-win_arm64.whl", hash = "sha256:dd7afd3f8b0bfb4e0d9fc3c31bfe8a4ec7debe124cfd90619305def3c8ca8cd2"}, + {file = "yarl-1.22.0-py3-none-any.whl", hash = "sha256:1380560bdba02b6b6c90de54133c81c9f2a453dee9912fe58c1dcced1edb7cff"}, + {file = "yarl-1.22.0.tar.gz", hash = "sha256:bebf8557577d4401ba8bd9ff33906f1376c877aa78d1fe216ad01b4d6745af71"}, ] [package.dependencies] idna = ">=2.0" multidict = ">=4.0" -propcache = ">=0.2.0" +propcache = ">=0.2.1" [metadata] lock-version = "2.1" python-versions = ">=3.12,<4.0" -content-hash = "e274f852588768d9b93a5c571de0a8991a7d2ec5778b576c8a6634dc3b54817f" +content-hash = "9a9b086a0b20f0b4841ddba9ce8568ab071c687c15316faff55de56afb967daf" diff --git a/libs/common/pyproject.toml b/libs/common/pyproject.toml index b281a9c..4146e4f 100644 --- a/libs/common/pyproject.toml +++ b/libs/common/pyproject.toml @@ -9,84 +9,19 @@ readme = "README.md" python = ">=3.12,<4.0" pydantic = "^2.10.5" structlog = "^25.1.0" -dapr = "^1.14.0" +dapr = "1.16.0" minio = "^7.2.14" fastapi = "^0.115.6" +dapr-ext-workflow = "^1.16.0" +psycopg = {extras = ["pool"], version = "^3.2.9"} +asyncpg = "^0.30.0" [tool.poetry.group.dev.dependencies] ruff = "^0.9.2" +pytest = "^8.4.1" +pytest-asyncio = "^1.1.0" [build-system] requires = ["poetry-core>=2.0.0,<3.0.0"] build-backend = "poetry.core.masonry.api" -[tool.ruff] -select = [ - "E", # pycodestyle errors - "W", # pycodestyle warnings - "F", # pyflakes - "I", # isort - "C", # flake8-comprehensions - "B", # flake8-bugbear - "UP", # pyupgrade - "NPY", # numpydoc - "A", # flake8-annotations - "TCH001", # Move application-only imports into TYPE_CHECKING block - "TCH002", # Move third-party imports into TYPE_CHECKING block - "TCH003", # Move standard library imports into TYPE_CHECKING block -] -ignore = [ - "E501", # line too long, handled by black - "B008", # do not perform function calls in argument defaults - "C901", # too complex - "W191", # indentation contains tabs - "F722", # syntax error in forward annotation - "UP007", # X | Y syntax while we're still supporting 3.9 - "UP038", # isinstance() X | Y instance ^ - "B905", # zip() without strict (isn't supported in 3.9) -] - -exclude = [ - ".bzr", - ".direnv", - ".eggs", - ".git", - ".git-rewrite", - ".hg", - ".mypy_cache", - ".nox", - ".pants.d", - ".pytype", - ".ruff_cache", - ".svn", - ".tox", - ".venv", - "__pypackages__", - "_build", - "buck-out", - "build", - "dist", - "node_modules", - "venv", -] - -line-length = 120 -indent-width = 4 -target-version = "py312" - -[tool.ruff.lint] -fixable = ["ALL"] -unfixable = ["B"] - -dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" -extend-select = [ - "I", # isort, added per instructions in the VS code extension (https://marketplace.visualstudio.com/items?itemName=charliermarsh.ruff) -] - -[tool.ruff.format] -quote-style = "double" -indent-style = "space" -skip-magic-trailing-comma = false -line-ending = "auto" - - diff --git a/libs/common/tests/test_example.py b/libs/common/tests/test_example.py new file mode 100644 index 0000000..b497045 --- /dev/null +++ b/libs/common/tests/test_example.py @@ -0,0 +1,3 @@ +def test_example(): + """Simple example test to verify pytest is working.""" + assert True diff --git a/libs/common/tests/test_helpers.py b/libs/common/tests/test_helpers.py new file mode 100644 index 0000000..b21ba80 --- /dev/null +++ b/libs/common/tests/test_helpers.py @@ -0,0 +1,118 @@ +"""Tests for chromium.local_state module.""" + +from common.helpers import get_drive_from_path + + +class TestGetDriveFromPath: + """Test suite for get_drive_from_path function.""" + + def test_valid_path_with_colon(self): + """Test valid path with colon in drive letter.""" + assert get_drive_from_path("/C:/Users/test") == "/C:" + assert get_drive_from_path("/D:/Program Files") == "/D:" + assert get_drive_from_path("/E:/temp") == "/E:" + + def test_invalid_path_without_colon(self): + """Test invalid path without colon in drive letter - should fail.""" + assert get_drive_from_path("/C/Users/test") is None + assert get_drive_from_path("/D/Program Files") is None + assert get_drive_from_path("/E/temp") is None + + def test_lowercase_drive_letters(self): + """Test lowercase drive letters are preserved.""" + assert get_drive_from_path("/c:/Users/test") == "/c:" + assert get_drive_from_path("/d:/temp") == "/d:" + + def test_mixed_case_drive_letters(self): + """Test mixed case scenarios.""" + assert get_drive_from_path("/C:/Users/test") == "/C:" + assert get_drive_from_path("/c:/Users/test") == "/c:" + + def test_all_valid_drive_letters(self): + """Test all valid drive letters A-Z with colon.""" + for letter in "ABCDEFGHIJKLMNOPQRSTUVWXYZ": + assert get_drive_from_path(f"/{letter}:/Users") == f"/{letter}:" + # Without colon should fail + assert get_drive_from_path(f"/{letter}/Users") is None + + def test_invalid_numeric_drive(self): + """Test invalid numeric drive letters.""" + assert get_drive_from_path("/1:/Users/test") is None + assert get_drive_from_path("/123/Users/test") is None + assert get_drive_from_path("/0:/temp") is None + + def test_invalid_special_characters(self): + """Test invalid special characters as drive.""" + assert get_drive_from_path("/$:/Users/test") is None + assert get_drive_from_path("/@:/Users/test") is None + assert get_drive_from_path("/#/Users/test") is None + assert get_drive_from_path("/*/Users/test") is None + + def test_invalid_multiple_letters(self): + """Test invalid multiple letters without colon.""" + assert get_drive_from_path("/AB/Users/test") is None + assert get_drive_from_path("/CD:/Users/test") is None + assert get_drive_from_path("/ABC/Users/test") is None + + def test_invalid_colon_usage(self): + """Test invalid colon usage.""" + assert get_drive_from_path("/C;/Users/test") is None # semicolon instead + assert get_drive_from_path("/C::/Users/test") is None # double colon + assert get_drive_from_path("/:C/Users/test") is None # colon before letter + + def test_path_without_leading_slash(self): + """Test paths without leading slash.""" + assert get_drive_from_path("C:/Users/test") == "C:" + assert get_drive_from_path("C/Users/test") is None + + def test_path_with_only_drive(self): + """Test paths with only drive letter.""" + assert get_drive_from_path("/C:") == "/C:" + assert get_drive_from_path("/C") is None # Without colon should fail + assert get_drive_from_path("/D:") == "/D:" + + def test_path_with_trailing_elements(self): + """Test various path structures.""" + assert get_drive_from_path("/C:/Users/test/Documents/file.txt") == "/C:" + assert get_drive_from_path("/C/Users/test/Documents/file.txt") is None # Without colon should fail + assert get_drive_from_path("/D:/Program Files/App/config.ini") == "/D:" + + def test_edge_case_single_slash(self): + """Test edge case of single slash.""" + assert get_drive_from_path("/") is None + + def test_edge_case_double_slash(self): + """Test edge case of double slash.""" + assert get_drive_from_path("//") is None + assert get_drive_from_path("//C/Users") is None + + def test_path_with_spaces(self): + """Test paths with spaces in directories.""" + assert get_drive_from_path("/C:/Program Files/Test") == "/C:" + assert get_drive_from_path("/C/Program Files/Test") is None # Without colon should fail + + def test_mixed_slashes_in_path(self): + """Test that function handles forward slashes correctly.""" + # The function expects POSIX-style paths with forward slashes + assert get_drive_from_path("/C:/Users/test") == "/C:" + assert get_drive_from_path("/C/Users/test") is None # Without colon should fail + + def test_unicode_characters(self): + """Test paths with unicode characters in later parts.""" + assert get_drive_from_path("/C:/Users/тест") == "/C:" + assert get_drive_from_path("/C/Users/测试") is None # Without colon should fail + + def test_empty_drive_after_slash(self): + """Test path with empty drive section.""" + assert get_drive_from_path("//Users/test") is None + + def test_real_world_chrome_paths(self): + """Test with real-world Chrome Local State paths.""" + assert get_drive_from_path("/C:/Users/itadmin/AppData/Local/Google/Chrome/User Data/Local State") == "/C:" + assert get_drive_from_path("/C/Users/itadmin/AppData/Local/Google/Chrome/User Data/Local State") is None # Without colon should fail + assert get_drive_from_path("/C/DPAPIUser/AppData/Local/Google/Chrome/User Data/Local State") is None # Without colon should fail + + def test_alternate_browser_paths(self): + """Test with other Chromium-based browser paths.""" + assert get_drive_from_path("/D:/Users/test/AppData/Local/Microsoft/Edge/User Data/Local State") == "/D:" + assert get_drive_from_path("/E/Users/test/AppData/Local/BraveSoftware/Brave-Browser/User Data/Local State") is None # Without colon should fail diff --git a/libs/file_enrichment_modules/.vscode/settings.json b/libs/file_enrichment_modules/.vscode/settings.json index c12c0b9..962ed99 100644 --- a/libs/file_enrichment_modules/.vscode/settings.json +++ b/libs/file_enrichment_modules/.vscode/settings.json @@ -6,15 +6,13 @@ "editor.formatOnSave": false }, "[python]": { - "editor.formatOnSave": true, "editor.codeActionsOnSave": { - "source.fixAll": "explicit", - "source.organizeImports": "explicit", + "source.fixAll.ruff": "explicit", + "source.organizeImports.ruff": "explicit" }, - "editor.defaultFormatter": "charliermarsh.ruff", + "editor.defaultFormatter": "charliermarsh.ruff" }, "autoDocstring.docstringFormat": "google", - "editor.formatOnSave": true, "files.exclude": { "**/.DS_Store": true, "**/.git": true, @@ -46,16 +44,6 @@ "reportMissingModuleSource": "none", }, "python.analysis.useLibraryCodeForTypes": true, // Pyright - // "python.formatting.provider": "none", - "python.languageServer": "Pylance", - "python.linting.enabled": true, - "python.linting.flake8Args": [ - "--max-line-length=240", - "--ignore=E203,E722,W503", - ], - "python.linting.flake8Enabled": true, - "python.linting.lintOnSave": true, - "python.linting.pylintEnabled": false, "python.testing.pytestArgs": [ "tests" ], diff --git a/libs/file_enrichment_modules/file_enrichment_modules/base64_decoder/analyzer.py b/libs/file_enrichment_modules/file_enrichment_modules/base64_decoder/analyzer.py index cc8097a..0b2e1b0 100644 --- a/libs/file_enrichment_modules/file_enrichment_modules/base64_decoder/analyzer.py +++ b/libs/file_enrichment_modules/file_enrichment_modules/base64_decoder/analyzer.py @@ -5,16 +5,15 @@ import re import tempfile from typing import Any -import structlog from common.helpers import is_plaintext +from common.logger import get_logger from common.models import EnrichmentResult, File, Transform from common.state_helpers import get_file_enriched from common.storage import StorageMinio from dapr.clients import DaprClient - from file_enrichment_modules.module_loader import EnrichmentModule -logger = structlog.get_logger(module=__name__) +logger = get_logger(__name__) class Base64DecoderAnalyzer(EnrichmentModule): @@ -35,8 +34,13 @@ class Base64DecoderAnalyzer(EnrichmentModule): # Allow whitespace/newlines within long sequences self.long_base64_pattern = re.compile(r"([A-Za-z0-9+/\s]{200,}={0,2})") - def should_process(self, object_id: str) -> bool: - """Determine if this module should run on plaintext files.""" + def should_process(self, object_id: str, file_path: str | None = None) -> bool: + """Determine if this module should run on plaintext files. + + Args: + object_id: The object ID of the file + file_path: Optional path to already downloaded file + """ # there are some performance issues, so we're disabling this one for now return False @@ -56,7 +60,15 @@ class Base64DecoderAnalyzer(EnrichmentModule): try: num_bytes = file_enriched.size if file_enriched.size < self.size_limit else self.size_limit - file_bytes = self.storage.download_bytes(file_enriched.object_id, length=num_bytes) + + if file_path: + # Use provided file path + with open(file_path, "rb") as f: + file_bytes = f.read(num_bytes) + else: + # Fallback to original method + file_bytes = self.storage.download_bytes(file_enriched.object_id, length=num_bytes) + file_content = file_bytes.decode("utf-8", errors="ignore") # Quick check using efficient patterns @@ -214,15 +226,28 @@ class Base64DecoderAnalyzer(EnrichmentModule): ) return candidates - def process(self, object_id: str) -> EnrichmentResult | None: - """Process file to find and decode base64 content.""" + def process(self, object_id: str, file_path: str | None = None) -> EnrichmentResult | None: + """Process file to find and decode base64 content. + + Args: + object_id: The object ID of the file + file_path: Optional path to already downloaded file + """ try: file_enriched = get_file_enriched(object_id) enrichment_result = EnrichmentResult(module_name=self.name) # Download and read the file content (respect size limit) num_bytes = file_enriched.size if file_enriched.size < self.size_limit else self.size_limit - file_bytes = self.storage.download_bytes(file_enriched.object_id, length=num_bytes) + + if file_path: + # Use provided file path + with open(file_path, "rb") as f: + file_bytes = f.read(num_bytes) + else: + # Fallback to original method + file_bytes = self.storage.download_bytes(file_enriched.object_id, length=num_bytes) + file_content = file_bytes.decode("utf-8", errors="ignore") # Extract potential base64 candidates with efficient filtering @@ -296,6 +321,7 @@ class Base64DecoderAnalyzer(EnrichmentModule): file_message = File( object_id=decoded_object_id, agent_id=file_enriched.agent_id, + source=file_enriched.source, project=file_enriched.project, timestamp=file_enriched.timestamp, expiration=file_enriched.expiration, diff --git a/libs/file_enrichment_modules/file_enrichment_modules/certificate/analyzer.py b/libs/file_enrichment_modules/file_enrichment_modules/certificate/analyzer.py index 4f628bf..7c95b70 100644 --- a/libs/file_enrichment_modules/file_enrichment_modules/certificate/analyzer.py +++ b/libs/file_enrichment_modules/file_enrichment_modules/certificate/analyzer.py @@ -1,813 +1,396 @@ # enrichment_modules/certificate/analyzer.py -import csv import tempfile from datetime import UTC, datetime +from pathlib import Path -import structlog -import yara_x -from common.models import EnrichmentResult, FileObject, Finding, FindingCategory, FindingOrigin, Transform +from common.logger import get_logger +from common.models import EnrichmentResult, Transform from common.state_helpers import get_file_enriched from common.storage import StorageMinio from cryptography import x509 -from cryptography.hazmat.backends import default_backend -from cryptography.hazmat.primitives.asymmetric import dsa, ec, rsa -from cryptography.hazmat.primitives.serialization import pkcs7 -from cryptography.hazmat.primitives.serialization.pkcs12 import load_pkcs12 -from cryptography.x509.oid import ExtensionOID, NameOID - +from cryptography.hazmat.primitives import serialization +from cryptography.hazmat.primitives.serialization import pkcs12 from file_enrichment_modules.module_loader import EnrichmentModule -logger = structlog.get_logger(module=__name__) +logger = get_logger(__name__) class CertificateAnalyzer(EnrichmentModule): def __init__(self): super().__init__("certificate_analyzer") self.storage = StorageMinio() - - # the workflows this module should automatically run in self.workflows = ["default"] - # Yara rule to check for certificate content - self.yara_rule = yara_x.compile(""" -rule Certificate_File -{ - meta: - description = "Detects certificate files (PEM/CRT/P7B) and private keys" + # Valid certificate file extensions + self.valid_extensions = {".pem", ".crt", ".cer", ".der", ".p7b", ".p7c", ".pfx", ".p12"} - strings: - $pem_begin_cert = "-----BEGIN CERTIFICATE-----" - $pem_begin_pkcs7 = "-----BEGIN PKCS7-----" - $der_cert_magic = { 30 82 ?? ?? 30 82 ?? ?? A0 03 02 01 } // Common DER cert header pattern - $pem_begin_private_key = /-----BEGIN (RSA |DSA |EC |ECDSA |EdDSA |)?PRIVATE KEY-----/ - $pem_begin_encrypted_private_key = /-----BEGIN ENCRYPTED PRIVATE KEY-----/ + # Common passwords to try for encrypted certificates/keys + self.common_passwords = [ + "", + "12345", + "123456", + "12345678", + "123456789", + "password", + "password123", + "qwerty123", + "qwerty1", + "secret", + "123123", + ] - condition: - ($pem_begin_cert at 0) or - ($pem_begin_pkcs7 at 0) or - ($der_cert_magic at 0) or - ($pem_begin_private_key at 0) or - ($pem_begin_encrypted_private_key at 0) -} - """) - - def should_process(self, object_id: str) -> bool: + def should_process(self, object_id: str, file_path: str | None = None) -> bool: """Determine if this module should run.""" file_enriched = get_file_enriched(object_id) - # Check file extension first - cert_extensions = [".pem", ".crt", ".cer", ".der", ".p7b", ".p7c", ".pfx", ".p12"] - if any(file_enriched.file_name.lower().endswith(ext) for ext in cert_extensions): - return True + # Check if it's a certificate-related file by extension or magic type + file_extension = Path(file_enriched.file_name).suffix.lower() + magic_type = file_enriched.magic_type.lower() - # Check MIME type - cert_mime_types = [ - "application/x-x509-ca-cert", - "application/pkix-cert", - "application/x-pkcs7-certificates", - "application/x-pem-file", - ] - if any(mime_type in file_enriched.mime_type.lower() for mime_type in cert_mime_types): - return True + return ( + file_extension in self.valid_extensions + or "certificate" in magic_type + or "pkcs" in magic_type + or "x.509" in magic_type + or "pem" in magic_type + ) - # Check magic type - cert_magic_types = ["certificate", "x509", "pkcs7"] - if any(magic_type in file_enriched.magic_type.lower() for magic_type in cert_magic_types): - return True + def _try_decrypt_with_passwords(self, data: bytes, file_extension: str) -> dict: + """Try to decrypt encrypted certificate/key with common passwords.""" + decrypt_results = {"is_encrypted": False, "password_found": None, "decryption_successful": False} - # Check using Yara rule as a fallback - file_bytes = self.storage.download_bytes(file_enriched.object_id) - should_run = len(self.yara_rule.scan(file_bytes).matching_rules) > 0 - - logger.debug(f"CertificateAnalyzer should_run: {should_run}") - return should_run - - def _format_extension_value(self, ext_name, ext_value): - """Format extension values in a human-readable way.""" - ext_str = str(ext_value) - - # Handle specific common extensions - if ext_name == "keyUsage": - # Parse keyUsage into component parts - try: - parts = ext_str.strip("").split("=") - usages = [] - for part in parts: - clean_part = part.strip().rstrip(",") - if "=" in clean_part: - key, value = clean_part.split("=") - if value.lower() == "true": - usages.append(key) - elif clean_part and clean_part != ")" and ">" not in clean_part: - usages.append(clean_part) - return ", ".join(usages) - except Exception: - pass - - elif ext_name == "extendedKeyUsage": - # Parse OIDs into known extended key usages - try: - oid_mapping = { - "1.3.6.1.5.5.7.3.1": "serverAuth", - "1.3.6.1.5.5.7.3.2": "clientAuth", - "1.3.6.1.5.5.7.3.3": "codeSigning", - "1.3.6.1.5.5.7.3.4": "emailProtection", - "1.3.6.1.5.5.7.3.8": "timeStamping", - "1.3.6.1.5.5.7.3.9": "OCSPSigning", - "1.3.6.1.4.1.311.10.3.4": "Microsoft Document Encryption", - # Add more mappings as needed - } - - # Extract OIDs from string like ", "") - - # If the extension value is too long, format with line breaks every 80 chars - if len(ext_str) > 80: - formatted = "" - for i in range(0, len(ext_str), 80): - if i > 0: - formatted += "\n " - formatted += ext_str[i : i + 80] - return formatted - - return ext_str - - def _load_certificates(self, file_path): - """Load certificates from file in various formats.""" - with open(file_path, "rb") as f: - file_data = f.read() - - certificates = [] - used_password = None - - # Try PEM format first (most common) + # First check if the file is encrypted at all by trying to load without password try: - certs = self._load_pem_certificates(file_data) - if certs: - return certs, used_password - except Exception as e: - logger.debug(f"Not a valid PEM certificate: {str(e)}") + if file_extension in {".pfx", ".p12"}: + # Try PKCS#12 without password first + try: + pkcs12.load_key_and_certificates(data, None) + decrypt_results.update( + {"is_encrypted": False, "password_found": None, "decryption_successful": True} + ) + return decrypt_results + except ValueError: + # File is encrypted, continue with password attempts + decrypt_results["is_encrypted"] = True + else: + # Try PEM private key without password + try: + serialization.load_pem_private_key(data, None) + decrypt_results.update( + {"is_encrypted": False, "password_found": None, "decryption_successful": True} + ) + return decrypt_results + except (ValueError, TypeError): + # May be encrypted or may not be a private key, continue checking + pass + except Exception: + pass - # Try DER format - try: - cert = x509.load_der_x509_certificate(file_data, default_backend()) - return [cert], used_password - except Exception as e: - logger.debug(f"Not a valid DER certificate: {str(e)}") - - # Try PKCS#7 format - try: - # First try PEM encoded PKCS#7 + # Try with passwords if potentially encrypted + for password in self.common_passwords: try: - pkcs7_data = pkcs7.load_pem_pkcs7_certificates(file_data) - if pkcs7_data: - return pkcs7_data, used_password + password_bytes = password.encode("utf-8") + + # Try PKCS#12 (PFX/P12) format + if file_extension in {".pfx", ".p12"}: + try: + pkcs12.load_key_and_certificates(data, password_bytes) + decrypt_results.update( + {"is_encrypted": True, "password_found": password, "decryption_successful": True} + ) + return decrypt_results + except ValueError: + decrypt_results["is_encrypted"] = True + continue + + # Try encrypted private key + try: + serialization.load_pem_private_key(data, password_bytes) + decrypt_results.update( + {"is_encrypted": True, "password_found": password, "decryption_successful": True} + ) + return decrypt_results + except ValueError: + decrypt_results["is_encrypted"] = True + continue + except Exception: - # Then try DER encoded PKCS#7 - pkcs7_data = pkcs7.load_der_pkcs7_certificates(file_data) - if pkcs7_data: - return pkcs7_data, used_password - except Exception as e: - logger.debug(f"Not a valid PKCS#7 certificate store: {str(e)}") - - # Try PKCS#12 format (PFX/P12) with various passwords - passwords_to_try = [ - None, - b"", - b"12345", - b"123456", - b"12345678", - b"123456789", - b"password", - b"password123", - b"qwerty123", - b"qwerty1", - b"secret", - b"123123", - ] - - for password in passwords_to_try: - try: - # Handle None password case - if password is None: - pkcs12_data = load_pkcs12(file_data, password=None) - else: - # Ensure password is bytes - if isinstance(password, str): - password = password.encode("utf-8") - pkcs12_data = load_pkcs12(file_data, password=password) - - certificates = [] - - # Get the main certificate - if pkcs12_data.cert: - certificates.append(pkcs12_data.cert.certificate) - - # Get any additional certificates in the chain - if pkcs12_data.additional_certs: - for cert in pkcs12_data.additional_certs: - certificates.append(cert.certificate) - - if certificates: - # Record which password worked - if password is None: - used_password = "None" - elif password == b"": - used_password = "Empty string" - else: - used_password = password.decode("utf-8") - - logger.info(f"Successfully loaded PKCS#12 file with password: {used_password}") - return certificates, used_password - - except Exception as e: - logger.debug(f"Failed to load PKCS#12 with password {password}: {str(e)}") continue - # If we get here, we couldn't parse the certificate - logger.error("Could not parse certificate file in any known format!") + return decrypt_results - def _load_pem_certificates(self, data): - """Load one or more PEM certificates from data.""" - certs = [] + def _parse_certificate_data(self, data: bytes, file_extension: str) -> dict: + """Parse certificate data and extract metadata.""" + result = {"certificates": [], "private_keys": [], "public_keys": [], "errors": [], "encryption_info": {}} - # Split by BEGIN/END markers to handle multiple PEM certs in one file - pem_sections = [] - current_section = "" - in_cert = False + # Try password decryption first + decrypt_info = self._try_decrypt_with_passwords(data, file_extension) + result["encryption_info"] = decrypt_info - for line in data.decode("utf-8", errors="replace").splitlines(): - if "-----BEGIN CERTIFICATE-----" in line: - current_section = line + "\n" - in_cert = True - elif "-----END CERTIFICATE-----" in line and in_cert: - current_section += line + "\n" - pem_sections.append(current_section) - current_section = "" - in_cert = False - elif in_cert: - current_section += line + "\n" - - # Process each PEM section - for pem_data in pem_sections: - try: - cert = x509.load_pem_x509_certificate(pem_data.encode("utf-8"), default_backend()) - certs.append(cert) - except Exception as e: - logger.debug(f"Failed to parse PEM certificate section: {str(e)}") - - return certs - - def _get_cert_info(self, cert): - """Extract information from a certificate.""" + # Parse different certificate formats try: - # Create a base info dictionary with safe defaults - info = { - "subject": "", - "issuer": "", - "serial_number": "", - "not_valid_before": "", - "not_valid_after": "", - "signature_algorithm": "", - "version": "", - "public_key_type": "", - "key_size": "", - "is_valid_now": False, - "extensions": {}, - "fingerprint_sha1": "", - "fingerprint_sha256": "", - } + # Try PKCS#12 first (PFX/P12) + if file_extension in {".pfx", ".p12"}: + password = None + if decrypt_info["decryption_successful"] and decrypt_info["password_found"] is not None: + password = decrypt_info["password_found"].encode("utf-8") - # Fill in certificate details - try: - info["subject"] = self._format_name(cert.subject) - except: - pass - info["issuer"] = self._format_name(cert.issuer) - info["serial_number"] = f"{cert.serial_number:x}" + try: + private_key, certificate, additional_certificates = pkcs12.load_key_and_certificates(data, password) - # Use UTC-aware datetime properties - try: - info["not_valid_before"] = cert.not_valid_before_utc.isoformat() - info["not_valid_after"] = cert.not_valid_after_utc.isoformat() - except AttributeError: - # Fallback for older cryptography versions - info["not_valid_before"] = cert.not_valid_before.isoformat() - info["not_valid_after"] = cert.not_valid_after.isoformat() + if certificate: + result["certificates"].append(self._extract_certificate_info(certificate)) - # Handle potential missing attributes with try-except - try: - info["signature_algorithm"] = cert.signature_algorithm_oid._name - except (AttributeError, TypeError): - info["signature_algorithm"] = "Unknown" + if additional_certificates: + for cert in additional_certificates: + result["certificates"].append(self._extract_certificate_info(cert)) - try: - info["version"] = cert.version.name - except (AttributeError, TypeError): - info["version"] = "Unknown" + if private_key: + result["private_keys"].append(self._extract_private_key_info(private_key)) - # Get public key info - try: - info["public_key_type"] = self._get_public_key_type(cert.public_key()) - info["key_size"] = self._get_key_size(cert.public_key()) - except Exception as e: - logger.debug(f"Error getting public key info: {str(e)}") - info["public_key_type"] = "Unknown" - info["key_size"] = "Unknown" + except Exception as e: + result["errors"].append(f"PKCS#12 parsing error: {str(e)}") - # Check validity - info["is_valid_now"] = self._is_certificate_valid(cert) + # Try DER format + elif file_extension == ".der": + try: + cert = x509.load_der_x509_certificate(data) + result["certificates"].append(self._extract_certificate_info(cert)) + except Exception as e: + result["errors"].append(f"DER certificate parsing error: {str(e)}") - # Get extensions and fingerprints - try: - info["extensions"] = self._get_extensions(cert) - except Exception as e: - logger.debug(f"Error getting extensions: {str(e)}") - - try: - info["fingerprint_sha1"] = self._get_fingerprint(cert, "sha1") - info["fingerprint_sha256"] = self._get_fingerprint(cert, "sha256") - except Exception as e: - logger.debug(f"Error getting fingerprints: {str(e)}") - - # Add SANs if available - try: - sans = self._get_subject_alternative_names(cert) - if sans: - info["subject_alternative_names"] = sans - except Exception as e: - logger.debug(f"Error getting SANs: {str(e)}") - - return info - except Exception as e: - logger.error(f"Error extracting certificate info: {str(e)}") - # Return a minimal valid dictionary with error information - return {"error": str(e), "is_valid_now": False} - - def _format_name(self, name): - """Format a certificate name (subject or issuer).""" - name_parts = [] - for attribute in name: - oid = attribute.oid - if oid == NameOID.COMMON_NAME: - name_parts.append(f"CN={attribute.value}") - elif oid == NameOID.ORGANIZATION_NAME: - name_parts.append(f"O={attribute.value}") - elif oid == NameOID.ORGANIZATIONAL_UNIT_NAME: - name_parts.append(f"OU={attribute.value}") - elif oid == NameOID.COUNTRY_NAME: - name_parts.append(f"C={attribute.value}") - elif oid == NameOID.STATE_OR_PROVINCE_NAME: - name_parts.append(f"ST={attribute.value}") - elif oid == NameOID.LOCALITY_NAME: - name_parts.append(f"L={attribute.value}") + # Try PEM format (default for most text-based formats) else: - name_parts.append(f"{oid._name}={attribute.value}") - return ", ".join(name_parts) + # Try to load as certificate + try: + cert = x509.load_pem_x509_certificate(data) + result["certificates"].append(self._extract_certificate_info(cert)) + except Exception: + pass - def _get_public_key_type(self, public_key): - """Get the type of the public key.""" - if isinstance(public_key, rsa.RSAPublicKey): - return "RSA" - elif isinstance(public_key, ec.EllipticCurvePublicKey): - return f"EC ({public_key.curve.name})" - elif isinstance(public_key, dsa.DSAPublicKey): - return "DSA" - else: - return "Unknown" + # Try to load as private key + password = None + if decrypt_info["decryption_successful"] and decrypt_info["password_found"] is not None: + password = decrypt_info["password_found"].encode("utf-8") - def _get_key_size(self, public_key): - """Get the size of the public key.""" - try: - if isinstance(public_key, rsa.RSAPublicKey): - return public_key.key_size - elif isinstance(public_key, ec.EllipticCurvePublicKey): - return public_key.key_size - elif isinstance(public_key, dsa.DSAPublicKey): - return public_key.key_size - else: - return "Unknown" - except Exception: - return "Unknown" + try: + private_key = serialization.load_pem_private_key(data, password) + result["private_keys"].append(self._extract_private_key_info(private_key)) + except Exception: + pass - def _is_certificate_valid(self, cert): - """Check if the certificate is currently valid (not expired).""" - try: - now = datetime.now(UTC) + # Try to load as public key + try: + public_key = serialization.load_pem_public_key(data) + result["public_keys"].append(self._extract_public_key_info(public_key)) + except Exception: + pass - # Use UTC-aware datetime properties - try: - not_valid_before = cert.not_valid_before_utc - not_valid_after = cert.not_valid_after_utc - except AttributeError: - # Fallback for older cryptography versions - not_valid_before = cert.not_valid_before - not_valid_after = cert.not_valid_after - - # Ensure both dates are timezone-aware for fallback - if not_valid_before.tzinfo is None: - not_valid_before = not_valid_before.replace(tzinfo=UTC) - if not_valid_after.tzinfo is None: - not_valid_after = not_valid_after.replace(tzinfo=UTC) - - return not_valid_before <= now <= not_valid_after except Exception as e: - logger.error(f"Error checking certificate validity: {str(e)}") - return False + result["errors"].append(f"General parsing error: {str(e)}") - def _get_extensions(self, cert): - """Get certificate extensions.""" - extensions = {} + return result + + def _extract_certificate_info(self, cert: x509.Certificate) -> dict: + """Extract detailed information from a certificate.""" + now = datetime.now(UTC) + + info = { + "version": cert.version.value, + "serial_number": str(cert.serial_number), + "subject": cert.subject.rfc4514_string(), + "issuer": cert.issuer.rfc4514_string(), + "not_valid_before": cert.not_valid_before_utc.isoformat(), + "not_valid_after": cert.not_valid_after_utc.isoformat(), + "is_valid": cert.not_valid_before_utc <= now <= cert.not_valid_after_utc, + "signature_algorithm": cert.signature_algorithm_oid._name, + "public_key_info": self._extract_public_key_info(cert.public_key()), + "extensions": [], + "extended_key_usage": [], + } + + # Extract extensions for ext in cert.extensions: - try: - extensions[ext.oid._name] = str(ext.value) - except Exception: - extensions[ext.oid._name] = "Unable to parse extension value" - return extensions + ext_info = {"name": ext.oid._name, "critical": ext.critical, "value": str(ext.value)} - def _get_subject_alternative_names(self, cert): - """Get Subject Alternative Names.""" - try: - san_extension = cert.extensions.get_extension_for_oid(ExtensionOID.SUBJECT_ALTERNATIVE_NAME) - if san_extension: - san_value = san_extension.value + # Special handling for Extended Key Usage + if isinstance(ext.value, x509.ExtendedKeyUsage): + ext_info["extended_key_usage_oids"] = [eku.dotted_string for eku in ext.value] + info["extended_key_usage"] = [eku._name for eku in ext.value] - sans = [] - for name in san_value: - if isinstance(name, x509.DNSName): - sans.append(f"DNS:{name.value}") - elif isinstance(name, x509.IPAddress): - sans.append(f"IP:{name.value}") - elif isinstance(name, x509.RFC822Name): # RFC822Name is for email addresses - sans.append(f"email:{name.value}") - elif isinstance(name, x509.UniformResourceIdentifier): # For URIs - sans.append(f"URI:{name.value}") - else: - sans.append(f"Other:{name}") + info["extensions"].append(ext_info) - return sans - except x509.ExtensionNotFound: - return [] - except Exception as e: - logger.error(f"Error getting SANs: {str(e)}") - return [] + return info - def _get_fingerprint(self, cert, hash_algorithm): - """Get certificate fingerprint using specified hash algorithm.""" - try: - if hash_algorithm == "sha1": - from cryptography.hazmat.primitives.hashes import SHA1 + def _extract_private_key_info(self, private_key) -> dict: + """Extract information from a private key.""" + key_type = type(private_key).__name__ - digest = SHA1() - elif hash_algorithm == "sha256": - from cryptography.hazmat.primitives.hashes import SHA256 + info = {"key_type": key_type, "key_size": getattr(private_key, "key_size", None)} - digest = SHA256() + # Add algorithm-specific information + if hasattr(private_key, "curve"): + info["curve"] = private_key.curve.name + elif hasattr(private_key, "key_size"): + info["key_size_bits"] = private_key.key_size + + return info + + def _extract_public_key_info(self, public_key) -> dict: + """Extract information from a public key.""" + key_type = type(public_key).__name__ + + info = {"key_type": key_type, "key_size": getattr(public_key, "key_size", None)} + + # Add algorithm-specific information + if hasattr(public_key, "curve"): + info["curve"] = public_key.curve.name + elif hasattr(public_key, "key_size"): + info["key_size_bits"] = public_key.key_size + + return info + + def _generate_report(self, analysis_result: dict, file_name: str) -> str: + """Generate a human-readable report.""" + report_lines = [f"Certificate Analysis Report for: {file_name}", "=" * 50, ""] + + # Encryption info + encryption_info = analysis_result["encryption_info"] + if encryption_info.get("is_encrypted") is not None: + report_lines.append("ENCRYPTION STATUS:") + if encryption_info["is_encrypted"]: + report_lines.append(" - File is encrypted: YES") + if encryption_info["decryption_successful"]: + password = encryption_info["password_found"] + report_lines.append(f" - Password found: '{password}'") + else: + report_lines.append(" - Password cracking: FAILED (none of the common passwords worked)") else: - return "Unsupported hash algorithm" + report_lines.append(" - File is encrypted: NO") + if encryption_info["decryption_successful"]: + report_lines.append(" - File loaded successfully without password") + report_lines.append("") + + # Certificates + if analysis_result["certificates"]: + report_lines.append("CERTIFICATES:") + for i, cert in enumerate(analysis_result["certificates"], 1): + report_lines.append(f" Certificate #{i}:") + report_lines.append(f" - Version: {cert['version']}") + report_lines.append(f" - Serial Number: {cert['serial_number']}") + report_lines.append(f" - Subject: {cert['subject']}") + report_lines.append(f" - Issuer: {cert['issuer']}") + report_lines.append(f" - Valid From: {cert['not_valid_before']}") + report_lines.append(f" - Valid Until: {cert['not_valid_after']}") + report_lines.append(f" - Currently Valid: {'YES' if cert['is_valid'] else 'NO'}") + report_lines.append(f" - Signature Algorithm: {cert['signature_algorithm']}") + report_lines.append(f" - Public Key Type: {cert['public_key_info']['key_type']}") + if cert["public_key_info"].get("key_size_bits"): + report_lines.append(f" - Key Size: {cert['public_key_info']['key_size_bits']} bits") + if cert["public_key_info"].get("curve"): + report_lines.append(f" - Curve: {cert['public_key_info']['curve']}") + + if cert["extended_key_usage"]: + report_lines.append(" - Extended Key Usage:") + for eku in cert["extended_key_usage"]: + report_lines.append(f" * {eku}") + + if cert["extensions"]: + report_lines.append(" - Extensions:") + for ext in cert["extensions"]: + critical_text = " (CRITICAL)" if ext["critical"] else "" + report_lines.append(f" * {ext['name']}{critical_text}") + if ext.get("extended_key_usage_oids"): + report_lines.append(f" OIDs: {', '.join(ext['extended_key_usage_oids'])}") + + report_lines.append("") + + # Private keys + if analysis_result["private_keys"]: + report_lines.append("PRIVATE KEYS:") + for i, key in enumerate(analysis_result["private_keys"], 1): + report_lines.append(f" Private Key #{i}:") + report_lines.append(f" - Key Type: {key['key_type']}") + if key.get("key_size_bits"): + report_lines.append(f" - Key Size: {key['key_size_bits']} bits") + if key.get("curve"): + report_lines.append(f" - Curve: {key['curve']}") + report_lines.append("") + + # Public keys + if analysis_result["public_keys"]: + report_lines.append("PUBLIC KEYS:") + for i, key in enumerate(analysis_result["public_keys"], 1): + report_lines.append(f" Public Key #{i}:") + report_lines.append(f" - Key Type: {key['key_type']}") + if key.get("key_size_bits"): + report_lines.append(f" - Key Size: {key['key_size_bits']} bits") + if key.get("curve"): + report_lines.append(f" - Curve: {key['curve']}") + report_lines.append("") + + # Errors + if analysis_result["errors"]: + report_lines.append("ERRORS:") + for error in analysis_result["errors"]: + report_lines.append(f" - {error}") + report_lines.append("") + + return "\n".join(report_lines) + + def _analyze_certificate(self, file_path: str, file_enriched) -> EnrichmentResult | None: + """Analyze certificate file and generate enrichment result.""" + enrichment_result = EnrichmentResult(module_name=self.name, dependencies=self.dependencies) + + try: + with open(file_path, "rb") as f: + data = f.read() + + file_extension = Path(file_enriched.file_name).suffix.lower() + analysis_result = self._parse_certificate_data(data, file_extension) + + enrichment_result.results = analysis_result + + # Generate human-readable report + report = self._generate_report(analysis_result, file_enriched.file_name) + + with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8", delete=False) as tmp_file: + tmp_file.write(report) + tmp_file.flush() + + object_id = self.storage.upload_file(tmp_file.name) + + displayable_parsed = Transform( + type="displayable_parsed", + object_id=f"{object_id}", + metadata={ + "file_name": f"{file_enriched.file_name}_analysis.txt", + "display_type_in_dashboard": "monaco", + "default_display": True, + }, + ) + + enrichment_result.transforms = [displayable_parsed] + return enrichment_result - fingerprint = cert.fingerprint(digest) - return ":".join(f"{b:02x}" for b in fingerprint) except Exception as e: - logger.error(f"Error getting fingerprint: {str(e)}") - return "Unknown" + logger.exception(e, message=f"Error analyzing certificate file for {file_enriched.file_name}") + return None - def process(self, object_id: str) -> EnrichmentResult | None: + def process(self, object_id: str, file_path: str | None = None) -> EnrichmentResult | None: """Process certificate file.""" try: file_enriched = get_file_enriched(object_id) - enrichment_result = EnrichmentResult(module_name=self.name, dependencies=self.dependencies) - transforms = [] - findings = [] - with self.storage.download(file_enriched.object_id) as temp_file: - try: - certificates, used_password = self._load_certificates(temp_file.name) - cert_info_list = [] - expired_certs = [] - - for cert in certificates: - try: - cert_info = self._get_cert_info(cert) - # Ensure is_valid_now exists in the cert_info dict - if "is_valid_now" not in cert_info: - cert_info["is_valid_now"] = False - cert_info_list.append(cert_info) - - # Check if certificate is expired - if not cert_info.get("is_valid_now", False): - expired_certs.append(cert_info) - except Exception as e: - logger.error(f"Error processing certificate: {str(e)}") - # Add a minimal cert_info with error details - cert_info_list.append({"error": str(e), "is_valid_now": False}) - - # Generate summary report - report_lines = [] - report_lines.append("# Certificate Analysis Summary") - report_lines.append(f"\nFile name: {file_enriched.file_name}") - report_lines.append(f"\nTotal certificates: {len(cert_info_list)}") - report_lines.append(f"\nValid certificates: {len(cert_info_list) - len(expired_certs)}") - report_lines.append(f"\nExpired certificates: {len(expired_certs)}") - - # Add password info if it's a PKCS#12 file - if used_password is not None: - report_lines.append( - f"\n**Note**: Successfully decrypted PKCS#12/PFX file using password: '{used_password}'" - ) - - # Certificate details - for i, cert_info in enumerate(cert_info_list, 1): - eku = "" - report_lines.append(f"\n## Certificate {i}") - - # Handle error case - if "error" in cert_info and "subject" not in cert_info: - report_lines.append(f"\n**ERROR**: {cert_info['error']}") - continue - - # Format subject and issuer with line breaks for readability - report_lines.append(f"\n**Subject**: \n{cert_info['subject']}") - report_lines.append(f"\n**Issuer**: \n{cert_info['issuer']}") - - # Basic certificate details - report_lines.append(f"\n**Serial Number**: {cert_info['serial_number']}") - report_lines.append(f"\n**Valid From**: {cert_info['not_valid_before']}") - report_lines.append(f"\n**Valid To**: {cert_info['not_valid_after']}") - report_lines.append( - f"\n**Status**: {'Valid' if cert_info.get('is_valid_now', False) else 'Expired or Not Yet Valid'}" - ) - report_lines.append(f"\n**Version**: {cert_info['version']}") - report_lines.append(f"\n**Signature Algorithm**: {cert_info['signature_algorithm']}") - report_lines.append( - f"\n**Public Key**: {cert_info['public_key_type']} ({cert_info['key_size']} bits)" - ) - - # Fingerprints with better formatting - if cert_info["fingerprint_sha1"]: - report_lines.append(f"\n**SHA-1 Fingerprint**: \n{cert_info['fingerprint_sha1']}") - if cert_info["fingerprint_sha256"]: - report_lines.append(f"\n**SHA-256 Fingerprint**: \n{cert_info['fingerprint_sha256']}") - - # Subject Alternative Names with better formatting - if "subject_alternative_names" in cert_info and cert_info["subject_alternative_names"]: - report_lines.append("\n**Subject Alternative Names**:") - for san in cert_info["subject_alternative_names"]: - # Extract the SAN value and format it properly - san_type = san.split(":", 1)[0] if ":" in san else "Other" - san_value = san.split(":", 1)[1] if ":" in san else san - - # Format as a list item with link if it's a domain or email - if san_type == "DNS" or san_type == "email": - # Make email addresses and domains clickable if possible - if san_type == "DNS": - report_lines.append(f"- **{san_type}**: [{san_value}](https://{san_value})") - elif san_type == "email": - report_lines.append(f"- **{san_type}**: [{san_value}](mailto:{san_value})") - else: - report_lines.append(f"- **{san_type}**: {san_value}") - else: - report_lines.append(f"- **{san_type}**: {san_value}") - - # Extensions with better formatting - if cert_info["extensions"]: - report_lines.append("\n**Key Extensions**:") - for ext_name, ext_value in cert_info["extensions"].items(): - # Skip displaying SANs here as we already showed them above - if ext_name != "subjectAltName": - # Format the extension value in a readable way - try: - formatted_value = self._format_extension_value(ext_name, ext_value) - except Exception as e: - logger.error(f"Error formatting extension {ext_name}: {str(e)}") - formatted_value = str(ext_value) - - report_lines.append(f"- **{ext_name}**: \n {formatted_value}") - if ext_name == "extendedKeyUsage": - eku = formatted_value - - # Update the raw_data to include password information if a PKCS#12 file was loaded - raw_data = {"certificates": cert_info_list} - if used_password is not None: - raw_data["pkcs12_password"] = used_password - - # Create summary report transform - with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8") as tmp_report: - tmp_report.write("\n".join(report_lines)) - tmp_report.flush() - report_id = self.storage.upload_file(tmp_report.name) - - transforms.append( - Transform( - type="finding_summary", - object_id=f"{report_id}", - metadata={ - "file_name": f"{file_enriched.file_name}_analysis.md", - "display_type_in_dashboard": "markdown", - "default_display": True, - }, - ) - ) - - # Export certificate info to CSV - with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8", newline="") as tmp_csv: - fieldnames = [ - "subject", - "issuer", - "serial_number", - "not_valid_before", - "not_valid_after", - "is_valid_now", - "version", - "signature_algorithm", - "public_key_type", - "key_size", - "fingerprint_sha1", - "fingerprint_sha256", - ] - - # Add pkcs12_password field if applicable - if used_password is not None: - fieldnames.append("pkcs12_password") - - writer = csv.DictWriter(tmp_csv, fieldnames=fieldnames) - writer.writeheader() - - for cert_info in cert_info_list: - # Create a row with only the fields we want - row = {field: cert_info[field] for field in fieldnames if field in cert_info} - # Add password information if available - if used_password is not None and "pkcs12_password" in fieldnames: - row["pkcs12_password"] = used_password - writer.writerow(row) - - tmp_csv.flush() - csv_id = self.storage.upload_file(tmp_csv.name) - - transforms.append( - Transform( - type="certificate_info.csv", - object_id=f"{csv_id}", - metadata={ - "file_name": f"{file_enriched.file_name}_certificates.csv", - "offer_as_download": True, - }, - ) - ) - - # Create finding for valid certificates - valid_certs = [cert for cert in cert_info_list if cert.get("is_valid_now", False)] - if valid_certs: - finding_data = [] - - # Create a summary of valid certificates - valid_summary = "## Valid Certificates Detected\n\n" - valid_summary += "The following certificates are currently valid:\n\n" - - for i, cert in enumerate(valid_certs, 1): - valid_summary += f"**Certificate {i}**\n" - valid_summary += f"- **Subject:** {cert['subject']}\n" - if eku: - valid_summary += f"- **EKU:** {eku}\n" - valid_summary += f"- **Issuer:** {cert['issuer']}\n" - valid_summary += f"- **Valid From:** {cert['not_valid_before']}\n" - valid_summary += f"- **Valid To:** {cert['not_valid_after']}\n\n" - - # Add password information if a PKCS#12 file was loaded - if used_password is not None: - valid_summary += f"\n**Note**: Successfully decrypted PKCS#12/PFX file using password: '{used_password}'\n" - - # Add the valid cert summary as a finding - display_data = FileObject(type="finding_summary", metadata={"summary": valid_summary}) - finding_data.append(display_data) - - # Include password information in the finding if applicable - finding_raw_data = {"valid_certificates": valid_certs} - if used_password is not None: - finding_raw_data["pkcs12_password"] = used_password - - finding = Finding( - category=FindingCategory.CREDENTIAL, - finding_name="valid_certificates", - origin_type=FindingOrigin.ENRICHMENT_MODULE, - origin_name=self.name, - object_id=file_enriched.object_id, - severity=5, - raw_data=finding_raw_data, - data=finding_data, - ) - - findings.append(finding) - - # Create additional finding for PKCS#12 password if one was found - if used_password is not None and used_password not in ["None", "Empty string"]: - password_finding_summary = "## PKCS#12/PFX Password Discovered\n\n" - password_finding_summary += ( - f"Successfully decrypted PKCS#12/PFX file using password: **'{used_password}'**\n\n" - ) - password_finding_summary += "This password may be used for other encrypted files or systems." - - password_display_data = FileObject( - type="finding_summary", metadata={"summary": password_finding_summary} - ) - - password_finding = Finding( - category=FindingCategory.CREDENTIAL, - finding_name="pkcs12_password_found", - origin_type=FindingOrigin.ENRICHMENT_MODULE, - origin_name=self.name, - object_id=file_enriched.object_id, - severity=7, # Higher severity for password discovery - raw_data={"pkcs12_password": used_password}, - data=[password_display_data], - ) - - findings.append(password_finding) - - # Add the results to the enrichment result - enrichment_result.transforms = transforms - enrichment_result.findings = findings - enrichment_result.results = raw_data - - return enrichment_result - - except Exception as e: - logger.exception(e, message=f"Error processing certificate file: {file_enriched.file_name}") - - # Create an error report - error_report = ( - f"# Certificate Analysis Error\n\nFailed to analyze {file_enriched.file_name}: {str(e)}" - ) - - with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8") as tmp_error: - tmp_error.write(error_report) - tmp_error.flush() - error_id = self.storage.upload_file(tmp_error.name) - - transforms.append( - Transform( - type="finding_summary", - object_id=f"{error_id}", - metadata={ - "file_name": f"{file_enriched.file_name}_analysis_error.md", - "display_type_in_dashboard": "markdown", - "default_display": True, - }, - ) - ) - - enrichment_result.transforms = transforms - return enrichment_result + if file_path: + return self._analyze_certificate(file_path, file_enriched) + else: + with self.storage.download(file_enriched.object_id) as temp_file: + return self._analyze_certificate(temp_file.name, file_enriched) except Exception as e: - logger.exception(e, message="Error in certificate analyzer") + logger.exception(e, message="Error processing certificate file", file_object_id=object_id) + return None def create_enrichment_module() -> EnrichmentModule: diff --git a/libs/file_enrichment_modules/file_enrichment_modules/chromium_cookies/analyzer.py b/libs/file_enrichment_modules/file_enrichment_modules/chromium_cookies/analyzer.py new file mode 100644 index 0000000..fb4dc49 --- /dev/null +++ b/libs/file_enrichment_modules/file_enrichment_modules/chromium_cookies/analyzer.py @@ -0,0 +1,257 @@ +# enrichment_modules/chromium_cookies/analyzer.py +import csv +import sqlite3 +import tempfile +from datetime import UTC, datetime +from typing import TYPE_CHECKING + +import yara_x +from chromium import convert_chromium_timestamp, process_chromium_cookies +from common.logger import get_logger +from common.models import EnrichmentResult, Transform +from common.state_helpers import get_file_enriched, get_file_enriched_async +from common.storage import StorageMinio +from file_enrichment_modules.module_loader import EnrichmentModule + +if TYPE_CHECKING: + import asyncio + + from nemesis_dpapi import DpapiManager + +logger = get_logger(__name__) + + +class ChromeCookiesParser(EnrichmentModule): + def __init__(self): + super().__init__("chrome_cookies_parser") + self.storage = StorageMinio() + + # the workflows this module should automatically run in + self.workflows = ["default"] + + self.dpapi_manager: DpapiManager = None # type: ignore + self.loop: asyncio.AbstractEventLoop = None # type: ignore + + # Yara rule to check for Chrome Cookies tables + self.yara_rule = yara_x.compile(""" +rule Chrome_Cookies_Tables +{ + meta: + description = "Detects Chrome/Chromium cookies database tables" + + strings: + $cookies_table = "CREATE TABLE cookies" + $cookies_index = "CREATE UNIQUE INDEX cookies_unique_index" + + condition: + all of them +} + """) + + def should_process(self, object_id: str, file_path: str | None = None) -> bool: + """Determine if this module should run. + + Args: + object_id: The object ID of the file + file_path: Optional path to already downloaded file + """ + + file_enriched = get_file_enriched(object_id) + + if "sqlite 3.x database" not in file_enriched.magic_type.lower(): + return False + + if file_path: + # Use provided file path + with open(file_path, "rb") as f: + file_bytes = f.read() + else: + # Fallback to downloading the file itself + file_bytes = self.storage.download_bytes(file_enriched.object_id) + + if file_enriched.is_plaintext: + return False + + # Verify Chrome cookies tables using Yara + should_run = len(self.yara_rule.scan(file_bytes).matching_rules) > 0 + + return should_run + + async def process(self, object_id: str, file_path: str | None = None) -> EnrichmentResult | None: + """Do the file enrichment. + + Args: + object_id: The object ID of the file + file_path: Optional path to already downloaded file + + Returns: + EnrichmentResult or None if processing fails + """ + return await self._process_async(object_id, file_path) + + async def _process_async(self, object_id: str, file_path: str | None = None) -> EnrichmentResult | None: + """Process Chrome Cookies database. + + Args: + object_id: The object ID of the file + file_path: Optional path to already downloaded file + """ + try: + file_enriched = await get_file_enriched_async(object_id) + enrichment_result = EnrichmentResult(module_name=self.name, dependencies=self.dependencies) + transforms = [] + + # Use the chromium library to process and insert into database + process_chromium_cookies(object_id, file_path) + + # Configure SQLite to handle non-UTF8 data for report generation + def adapt_bytes(b): + return b.hex() if b is not None else None + + def convert_bytes(hex_str): + return bytes.fromhex(hex_str) if hex_str is not None else None + + sqlite3.register_adapter(bytes, adapt_bytes) + sqlite3.register_converter("BLOB", convert_bytes) + + if file_path: + # Use provided file path + conn = sqlite3.connect(file_path, detect_types=sqlite3.PARSE_DECLTYPES) + else: + # Fallback to downloading the file itself + with self.storage.download(file_enriched.object_id) as temp_file: + conn = sqlite3.connect(temp_file.name, detect_types=sqlite3.PARSE_DECLTYPES) + + # Set text factory to handle non-UTF8 strings + conn.text_factory = lambda x: x.decode("utf-8", errors="replace") + cursor = conn.cursor() + + # Generate summary report + report_lines = [] + + # Cookies summary + cursor.execute("SELECT COUNT(*) FROM cookies") + cookie_count = cursor.fetchone()[0] + report_lines.append("# Chrome Cookies Summary") + report_lines.append(f"\nTotal cookies: {cookie_count}") + + # Count of non-expired cookies at time of processing + cursor.execute( + """ + SELECT COUNT(*) FROM cookies + WHERE expires_utc IS NULL OR expires_utc > ? + """, + (int((datetime.now(UTC).timestamp() - 11644473600) * 1000000),), + ) + non_expired_count = cursor.fetchone()[0] + report_lines.append(f"Non-expired cookies (at time of processing): {non_expired_count}") + + # Most recently accessed cookies + cursor.execute(""" + SELECT host_key, name, last_access_utc + FROM cookies + ORDER BY last_access_utc DESC + LIMIT 10 + """) + report_lines.append("\n## Most Recently Accessed Cookies") + report_lines.append("\n| Last Access Time | Host | Cookie Name |") + report_lines.append("| ---------------- | ---- | ----------- |") + for host_key, name, last_access_utc in cursor.fetchall(): + last_access_iso = convert_chromium_timestamp(last_access_utc, True) + # Escape pipe characters to prevent table formatting issues + safe_host = host_key.replace("|", "\\|") if host_key else "" + safe_name = name.replace("|", "\\|") if name else "" + report_lines.append(f"| {last_access_iso} | {safe_host} | {safe_name} |") + + # Create summary report transform + with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8") as tmp_report: + tmp_report.write("\n".join(report_lines)) + tmp_report.flush() + report_object_id = self.storage.upload_file(tmp_report.name) + + transforms.append( + Transform( + type="finding_summary", + object_id=f"{report_object_id}", + metadata={ + "file_name": f"{file_enriched.file_name}.md", + "display_type_in_dashboard": "markdown", + "default_display": True, + }, + ) + ) + + # Export cookies table + cursor.execute(""" + SELECT creation_utc, host_key, source_port, path, name, expires_utc, + last_access_utc, last_update_utc, is_secure, is_httponly, + is_persistent, samesite + FROM cookies + """) + with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8", newline="") as tmp_cookies: + writer = csv.writer(tmp_cookies) + writer.writerow( + [ + "creation_utc", + "host_key", + "source_port", + "path", + "name", + "expires_utc", + "last_access_utc", + "last_update_utc", + "is_secure", + "is_httponly", + "is_persistent", + "samesite", + ] + ) + for row in cursor: + # Convert Chromium timestamps to ISO format + creation_utc = convert_chromium_timestamp(row[0], True) if row[0] else None + expires_utc = convert_chromium_timestamp(row[5], True) if row[5] else None + last_access_utc = convert_chromium_timestamp(row[6], True) if row[6] else None + last_update_utc = convert_chromium_timestamp(row[7], True) if row[7] else None + + writer.writerow( + [ + creation_utc, + row[1], + row[2], + row[3], + row[4], + expires_utc, + last_access_utc, + last_update_utc, + row[8], + row[9], + row[10], + row[11], + ] + ) + tmp_cookies.flush() + cookies_object_id = self.storage.upload_file(tmp_cookies.name) + + transforms.append( + Transform( + type="chromium_cookies", + object_id=f"{cookies_object_id}", + metadata={ + "file_name": f"{file_enriched.file_name}_chromium_cookies.csv", + "offer_as_download": True, + }, + ) + ) + + conn.close() + enrichment_result.transforms = transforms + return enrichment_result + + except Exception as e: + logger.exception( + e, message="Error processing Chrome Cookies database", object_id=object_id, file_path=file_enriched.path + ) + + +def create_enrichment_module() -> EnrichmentModule: + return ChromeCookiesParser() diff --git a/libs/file_enrichment_modules/file_enrichment_modules/chromium_history/analyzer.py b/libs/file_enrichment_modules/file_enrichment_modules/chromium_history/analyzer.py index 518a2ba..dc46f1e 100644 --- a/libs/file_enrichment_modules/file_enrichment_modules/chromium_history/analyzer.py +++ b/libs/file_enrichment_modules/file_enrichment_modules/chromium_history/analyzer.py @@ -2,17 +2,16 @@ import csv import sqlite3 import tempfile -from datetime import UTC, datetime, timedelta -import structlog import yara_x +from chromium import convert_chromium_timestamp, process_chromium_history +from common.logger import get_logger from common.models import EnrichmentResult, Transform from common.state_helpers import get_file_enriched from common.storage import StorageMinio - from file_enrichment_modules.module_loader import EnrichmentModule -logger = structlog.get_logger(module=__name__) +logger = get_logger(__name__) class ChromeHistoryParser(EnrichmentModule): @@ -39,166 +38,186 @@ rule Chrome_Downloads_Tables } """) - def should_process(self, object_id: str) -> bool: - """Determine if this module should run.""" + def should_process(self, object_id: str, file_path: str | None = None) -> bool: + """Determine if this module should run. + + Args: + object_id: The object ID of the file + file_path: Optional path to already downloaded file + """ + file_enriched = get_file_enriched(object_id) # Check if filename is exactly "History" and SQLite magic type if not (file_enriched.file_name == "History" and "sqlite 3.x database" in file_enriched.magic_type.lower()): return False + if file_enriched.is_plaintext: + return False + + if file_path: + # Use provided file path + with open(file_path, "rb") as f: + file_bytes = f.read() + else: + # Fallback to downloading the file itself + file_bytes = self.storage.download_bytes(file_enriched.object_id) + # Verify Chrome history tables using Yara - file_bytes = self.storage.download_bytes(file_enriched.object_id) should_run = len(self.yara_rule.scan(file_bytes).matching_rules) > 0 - logger.debug(f"ChromeHistoryParser should_run: {should_run}") return should_run - def _chrome_time_to_iso(self, chrome_time: int) -> str: - """Convert Chrome timestamp to ISO 8601.""" - if not chrome_time: - return "" - # Chrome stores timestamps as microseconds since 1601-01-01 UTC - epoch = datetime(1601, 1, 1, tzinfo=UTC) - dt = epoch + timedelta(microseconds=chrome_time) - return dt.isoformat() + def process(self, object_id: str, file_path: str | None = None) -> EnrichmentResult | None: + """Process Chrome History database. - def process(self, object_id: str) -> EnrichmentResult | None: - """Process Chrome History database.""" + Args: + object_id: The object ID of the file + file_path: Optional path to already downloaded file + """ try: file_enriched = get_file_enriched(object_id) enrichment_result = EnrichmentResult(module_name=self.name, dependencies=self.dependencies) transforms = [] - with self.storage.download(file_enriched.object_id) as temp_file: - # Configure SQLite to handle non-UTF8 data - def adapt_bytes(b): - return b.hex() if b is not None else None + # Use the chromium library to process and insert into the database + process_chromium_history(object_id, file_path) - def convert_bytes(hex_str): - return bytes.fromhex(hex_str) if hex_str is not None else None + # Configure SQLite to handle non-UTF8 data for report generation + def adapt_bytes(b): + return b.hex() if b is not None else None - sqlite3.register_adapter(bytes, adapt_bytes) - sqlite3.register_converter("BLOB", convert_bytes) + def convert_bytes(hex_str): + return bytes.fromhex(hex_str) if hex_str is not None else None - conn = sqlite3.connect(temp_file.name, detect_types=sqlite3.PARSE_DECLTYPES) - # Set text factory to handle non-UTF8 strings - conn.text_factory = lambda x: x.decode("utf-8", errors="replace") - cursor = conn.cursor() + sqlite3.register_adapter(bytes, adapt_bytes) + sqlite3.register_converter("BLOB", convert_bytes) - # Generate summary report - report_lines = [] + if file_path: + # Use provided file path + conn = sqlite3.connect(file_path, detect_types=sqlite3.PARSE_DECLTYPES) + else: + # Fallback to downloading the file itself + with self.storage.download(file_enriched.object_id) as temp_file: + conn = sqlite3.connect(temp_file.name, detect_types=sqlite3.PARSE_DECLTYPES) - # URLs summary - cursor.execute("SELECT COUNT(*) FROM urls") - url_count = cursor.fetchone()[0] - report_lines.append("# Chrome History Summary") - report_lines.append(f"\nTotal URLs visited: {url_count}") + # Set text factory to handle non-UTF8 strings + conn.text_factory = lambda x: x.decode("utf-8", errors="replace") + cursor = conn.cursor() - # Top 10 visited URLs - cursor.execute(""" - SELECT url, visit_count - FROM urls - ORDER BY visit_count DESC - LIMIT 10 - """) - report_lines.append("\n## Top 10 Most Visited URLs") - report_lines.append("\n| URL | Visit Count |") - report_lines.append("| --- | ----------- |") - for url, count in cursor.fetchall(): - report_lines.append(f"| {url} | {count} |") + # Generate summary report + report_lines = [] - # Downloads summary - cursor.execute("SELECT COUNT(*) FROM downloads") - download_count = cursor.fetchone()[0] - report_lines.append(f"\nTotal Downloads: {download_count}") + # URLs summary + cursor.execute("SELECT COUNT(*) FROM urls") + url_count = cursor.fetchone()[0] + report_lines.append("# Chrome History Summary") + report_lines.append(f"\nTotal URLs visited: {url_count}") - # Recent downloads - cursor.execute(""" - SELECT target_path, tab_url, end_time - FROM downloads - ORDER BY end_time DESC - LIMIT 10 - """) - report_lines.append("\n## Most Recent Downloads") - report_lines.append("\n| Time | Path | Source URL |") - report_lines.append("| ---- | ---- | ---------- |") - for path, url, end_time in cursor.fetchall(): - end_time_iso = self._chrome_time_to_iso(end_time) - # Escape pipe characters in paths and URLs to prevent table formatting issues - safe_path = path.replace("|", "\\|") if path else "" - safe_url = url.replace("|", "\\|") if url else "" - report_lines.append(f"| {end_time_iso} | {safe_path} | {safe_url} |") + # Top 10 visited URLs + cursor.execute(""" + SELECT url, visit_count + FROM urls + ORDER BY visit_count DESC + LIMIT 10 + """) + report_lines.append("\n## Top 10 Most Visited URLs") + report_lines.append("\n| URL | Visit Count |") + report_lines.append("| --- | ----------- |") + for url, count in cursor.fetchall(): + report_lines.append(f"| {url} | {count} |") - # Create summary report transform - with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8") as tmp_report: - tmp_report.write("\n".join(report_lines)) - tmp_report.flush() - object_id = self.storage.upload_file(tmp_report.name) + # Downloads summary + cursor.execute("SELECT COUNT(*) FROM downloads") + download_count = cursor.fetchone()[0] + report_lines.append(f"\nTotal Downloads: {download_count}") - transforms.append( - Transform( - type="finding_summary", - object_id=f"{object_id}", - metadata={ - "file_name": f"{file_enriched.file_name}.md", - "display_type_in_dashboard": "markdown", - "default_display": True, - }, - ) + # Recent downloads + cursor.execute(""" + SELECT target_path, tab_url, end_time + FROM downloads + ORDER BY end_time DESC + LIMIT 10 + """) + report_lines.append("\n## Most Recent Downloads") + report_lines.append("\n| Time | Path | Source URL |") + report_lines.append("| ---- | ---- | ---------- |") + for path, url, end_time in cursor.fetchall(): + end_time_iso = convert_chromium_timestamp(end_time, True) + # Escape pipe characters in paths and URLs to prevent table formatting issues + safe_path = path.replace("|", "\\|") if path else "" + safe_url = url.replace("|", "\\|") if url else "" + report_lines.append(f"| {end_time_iso} | {safe_path} | {safe_url} |") + + # Create summary report transform + with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8") as tmp_report: + tmp_report.write("\n".join(report_lines)) + tmp_report.flush() + report_object_id = self.storage.upload_file(tmp_report.name) + + transforms.append( + Transform( + type="finding_summary", + object_id=f"{report_object_id}", + metadata={ + "file_name": f"{file_enriched.file_name}.md", + "display_type_in_dashboard": "markdown", + "default_display": True, + }, ) + ) - # Export URLs table - cursor.execute(""" - SELECT url, title, visit_count, last_visit_time - FROM urls - """) - with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8", newline="") as tmp_urls: - writer = csv.writer(tmp_urls) - writer.writerow(["url", "title", "visit_count", "last_visit_time"]) - for row in cursor: - writer.writerow([row[0], row[1], row[2], self._chrome_time_to_iso(row[3])]) - tmp_urls.flush() - object_id = self.storage.upload_file(tmp_urls.name) + # Export URLs table + cursor.execute(""" + SELECT url, title, visit_count, last_visit_time + FROM urls + """) + with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8", newline="") as tmp_urls: + writer = csv.writer(tmp_urls) + writer.writerow(["url", "title", "visit_count", "last_visit_time"]) + for row in cursor: + writer.writerow([row[0], row[1], row[2], convert_chromium_timestamp(row[3], True)]) + tmp_urls.flush() + urls_object_id = self.storage.upload_file(tmp_urls.name) - transforms.append( - Transform( - type="chromium_urls", - object_id=f"{object_id}", - metadata={ - "file_name": f"{file_enriched.file_name}_chromium_urls.csv", - "offer_as_download": True, - }, - ) + transforms.append( + Transform( + type="chromium_urls", + object_id=f"{urls_object_id}", + metadata={ + "file_name": f"{file_enriched.file_name}_chromium_urls.csv", + "offer_as_download": True, + }, ) + ) - # Export downloads table - cursor.execute(""" - SELECT target_path, total_bytes, end_time, tab_url, mime_type - FROM downloads - """) - with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8", newline="") as tmp_downloads: - writer = csv.writer(tmp_downloads) - writer.writerow(["target_path", "total_bytes", "end_time", "tab_url", "mime_type"]) - for row in cursor: - writer.writerow([row[0], row[1], self._chrome_time_to_iso(row[2]), row[3], row[4]]) - tmp_downloads.flush() - object_id = self.storage.upload_file(tmp_downloads.name) + # Export downloads table + cursor.execute(""" + SELECT target_path, total_bytes, end_time, tab_url, mime_type + FROM downloads + """) + with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8", newline="") as tmp_downloads: + writer = csv.writer(tmp_downloads) + writer.writerow(["target_path", "total_bytes", "end_time", "tab_url", "mime_type"]) + for row in cursor: + writer.writerow([row[0], row[1], convert_chromium_timestamp(row[2], True), row[3], row[4]]) + tmp_downloads.flush() + downloads_object_id = self.storage.upload_file(tmp_downloads.name) - transforms.append( - Transform( - type="chromium_downloads", - object_id=f"{object_id}", - metadata={ - "file_name": f"{file_enriched.file_name}_chromium_downloads.csv", - "offer_as_download": True, - }, - ) + transforms.append( + Transform( + type="chromium_downloads", + object_id=f"{downloads_object_id}", + metadata={ + "file_name": f"{file_enriched.file_name}_chromium_downloads.csv", + "offer_as_download": True, + }, ) + ) - conn.close() - enrichment_result.transforms = transforms - return enrichment_result + conn.close() + enrichment_result.transforms = transforms + return enrichment_result except Exception as e: logger.exception(e, message="Error processing Chrome History database") diff --git a/libs/file_enrichment_modules/file_enrichment_modules/chromium_localstate/analyzer.py b/libs/file_enrichment_modules/file_enrichment_modules/chromium_localstate/analyzer.py new file mode 100644 index 0000000..080c27f --- /dev/null +++ b/libs/file_enrichment_modules/file_enrichment_modules/chromium_localstate/analyzer.py @@ -0,0 +1,110 @@ +# enrichment_modules/chromium_logins/analyzer.py + +import asyncio +from typing import TYPE_CHECKING + +import yara_x +from chromium import process_chromium_local_state +from common.logger import get_logger +from common.models import EnrichmentResult +from common.state_helpers import get_file_enriched +from common.storage import StorageMinio +from file_enrichment_modules.module_loader import EnrichmentModule +from nemesis_dpapi import DpapiManager + +if TYPE_CHECKING: + from nemesis_dpapi import DpapiManager + +logger = get_logger(__name__) + + +class ChromeLocalStateParser(EnrichmentModule): + def __init__(self): + super().__init__("chrome_local_state_parser") + self.storage = StorageMinio() + + # the workflows this module should automatically run in + self.workflows = ["default"] + + self.dpapi_manager: DpapiManager = None # type: ignore + self.loop: asyncio.AbstractEventLoop = None # type: ignore + + # Yara rule to check for Chrome Login Data tables + self.yara_rule = yara_x.compile(""" +rule Chrome_Local_State +{ + meta: + description = "Detects Chrome/Chromium Local State json" + + strings: + $local_state_1 = "\\"os_crypt\\"" + $local_state_2 = "\\"encrypted_key\\"" + $local_state_3 = "\\"user_experience_metrics\\"" + + condition: + all of them +} + """) + + def should_process(self, object_id: str, file_path: str | None = None) -> bool: + """Determine if this module should run. + + Args: + object_id: The object ID of the file + file_path: Optional path to already downloaded file + """ + + file_enriched = get_file_enriched(object_id) + + # Check if file is < 5 megs and JSON magic type + if not ((file_enriched.size < 5000000) and ("json" in file_enriched.magic_type.lower())): + return False + + if file_path: + # Use provided file path + with open(file_path, "rb") as f: + file_bytes = f.read() + else: + # Fallback to downloading the file itself + file_bytes = self.storage.download_bytes(file_enriched.object_id) + + # Verify Chrome Local State using Yara + should_run = len(self.yara_rule.scan(file_bytes).matching_rules) > 0 + + return should_run + + async def process(self, object_id: str, file_path: str | None = None) -> EnrichmentResult | None: + """Process Chrome Local State files. + + Args: + object_id: The object ID of the file + file_path: Optional path to already downloaded file + """ + return await self._process_async(object_id, file_path) + + async def _process_async(self, object_id: str, file_path: str | None = None) -> EnrichmentResult | None: + """Async helper for process method.""" + + try: + # Use the chromium library to process and insert into database + state_key_data = await process_chromium_local_state(self.dpapi_manager, object_id, file_path) + + if state_key_data: + # Debug: Check for coroutines in state_key_data + import inspect + + for key, value in state_key_data.items(): + if inspect.iscoroutine(value): + logger.error(f"FOUND COROUTINE IN state_key_data['{key}']!") + + enrichment = EnrichmentResult(module_name=self.name) + enrichment.results = {"parsed": state_key_data} + + return enrichment + + except Exception as e: + logger.exception(e, message="Error processing Chrome Local State file") + + +def create_enrichment_module() -> EnrichmentModule: + return ChromeLocalStateParser() diff --git a/libs/file_enrichment_modules/file_enrichment_modules/chromium_logins/analyzer.py b/libs/file_enrichment_modules/file_enrichment_modules/chromium_logins/analyzer.py new file mode 100644 index 0000000..bcd406b --- /dev/null +++ b/libs/file_enrichment_modules/file_enrichment_modules/chromium_logins/analyzer.py @@ -0,0 +1,232 @@ +# enrichment_modules/chromium_logins/analyzer.py +import asyncio +import csv +import sqlite3 +import tempfile +from typing import TYPE_CHECKING + +import yara_x +from chromium import convert_chromium_timestamp, process_chromium_logins +from common.logger import get_logger +from common.models import EnrichmentResult, Transform +from common.state_helpers import get_file_enriched, get_file_enriched_async +from common.storage import StorageMinio +from file_enrichment_modules.module_loader import EnrichmentModule + +if TYPE_CHECKING: + from nemesis_dpapi import DpapiManager + +logger = get_logger(__name__) + + +class ChromeLoginsParser(EnrichmentModule): + def __init__(self): + super().__init__("chrome_logins_parser") + self.storage = StorageMinio() + + # the workflows this module should automatically run in + self.workflows = ["default"] + + self.dpapi_manager: DpapiManager = None # type: ignore + self.loop: asyncio.AbstractEventLoop = None # type: ignore + + # Yara rule to check for Chrome Login Data tables + self.yara_rule = yara_x.compile(""" +rule Chrome_Logins_Tables +{ + meta: + description = "Detects Chrome/Chromium logins database tables" + + strings: + $logins_table = "CREATE TABLE logins " + $logins_table2 = "CREATE TABLE insecure_credentials" + + condition: + all of them +} + """) + + def should_process(self, object_id: str, file_path: str | None = None) -> bool: + """Determine if this module should run. + + Args: + object_id: The object ID of the file + file_path: Optional path to already downloaded file + """ + + file_enriched = get_file_enriched(object_id) + + if "sqlite 3.x database" not in file_enriched.magic_type.lower(): + return False + + if file_enriched.is_plaintext: + return False + + if file_path: + # Use provided file path + with open(file_path, "rb") as f: + file_bytes = f.read() + else: + # Fallback to downloading the file itself + file_bytes = self.storage.download_bytes(file_enriched.object_id) + + # Verify Chrome Login Data tables using Yara + should_run = len(self.yara_rule.scan(file_bytes).matching_rules) > 0 + + return should_run + + async def process(self, object_id: str, file_path: str | None = None) -> EnrichmentResult | None: + """Do the file enrichment. + + Args: + object_id: The object ID of the file + file_path: Optional path to already downloaded file + + Returns: + EnrichmentResult or None if processing fails + """ + return await self._process_async(object_id, file_path) + + async def _process_async(self, object_id: str, file_path: str | None = None) -> EnrichmentResult | None: + """Process Chrome Login Data database. + + Args: + object_id: The object ID of the file + file_path: Optional path to already downloaded file + """ + try: + file_enriched = await get_file_enriched_async(object_id) + enrichment_result = EnrichmentResult(module_name=self.name, dependencies=self.dependencies) + transforms = [] + + # Use the chromium library to process and insert into database + process_chromium_logins(object_id, file_path) + + # Configure SQLite to handle non-UTF8 data for report generation + def adapt_bytes(b): + return b.hex() if b is not None else None + + def convert_bytes(hex_str): + return bytes.fromhex(hex_str) if hex_str is not None else None + + sqlite3.register_adapter(bytes, adapt_bytes) + sqlite3.register_converter("BLOB", convert_bytes) + + if file_path: + # Use provided file path + conn = sqlite3.connect(file_path, detect_types=sqlite3.PARSE_DECLTYPES) + else: + # Fallback to downloading the file itself + with self.storage.download(file_enriched.object_id) as temp_file: + conn = sqlite3.connect(temp_file.name, detect_types=sqlite3.PARSE_DECLTYPES) + + # Set text factory to handle non-UTF8 strings + conn.text_factory = lambda x: x.decode("utf-8", errors="replace") + cursor = conn.cursor() + + # Generate summary report + report_lines = [] + + # Logins summary + cursor.execute("SELECT COUNT(*) FROM logins") + login_count = cursor.fetchone()[0] + report_lines.append("# Chrome Logins Summary") + report_lines.append(f"\nTotal logins: {login_count}") + + # Count of logins with non-empty password_value + cursor.execute("SELECT COUNT(*) FROM logins WHERE password_value IS NOT NULL") + password_count = cursor.fetchone()[0] + report_lines.append(f"\nLogins with saved passwords: {password_count}") + + # Most recently used logins + cursor.execute(""" + SELECT origin_url, username_value, date_last_used + FROM logins + ORDER BY date_last_used DESC + LIMIT 10 + """) + report_lines.append("\n## Most Recently Used Logins") + report_lines.append("\n| Last Used Time | Origin URL | Username |") + report_lines.append("| -------------- | ---------- | -------- |") + for origin_url, username_value, date_last_used in cursor.fetchall(): + last_used_iso = convert_chromium_timestamp(date_last_used, True) + # Escape pipe characters to prevent table formatting issues + safe_origin = origin_url.replace("|", "\\|") if origin_url else "" + safe_username = username_value.replace("|", "\\|") if username_value else "" + report_lines.append(f"| {last_used_iso} | {safe_origin} | {safe_username} |") + + # Create summary report transform + with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8") as tmp_report: + tmp_report.write("\n".join(report_lines)) + tmp_report.flush() + report_object_id = self.storage.upload_file(tmp_report.name) + + transforms.append( + Transform( + type="finding_summary", + object_id=f"{report_object_id}", + metadata={ + "file_name": f"{file_enriched.file_name}.md", + "display_type_in_dashboard": "markdown", + "default_display": True, + }, + ) + ) + + # Export logins table + cursor.execute(""" + SELECT origin_url, username_value, signon_realm, date_created, + date_last_used, date_password_modified, times_used + FROM logins + """) + with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8", newline="") as tmp_logins: + writer = csv.writer(tmp_logins) + writer.writerow( + [ + "origin_url", + "username_value", + "signon_realm", + "date_created", + "date_last_used", + "date_password_modified", + "times_used", + ] + ) + for row in cursor: + # Convert Chromium timestamps to ISO format + date_created = convert_chromium_timestamp(row[3], True) if row[3] else None + date_last_used = convert_chromium_timestamp(row[4], True) if row[4] else None + date_password_modified = convert_chromium_timestamp(row[5], True) if row[5] else None + + writer.writerow( + [row[0], row[1], row[2], date_created, date_last_used, date_password_modified, row[6]] + ) + tmp_logins.flush() + logins_object_id = self.storage.upload_file(tmp_logins.name) + + transforms.append( + Transform( + type="chromium_logins", + object_id=f"{logins_object_id}", + metadata={ + "file_name": f"{file_enriched.file_name}_chromium_logins.csv", + "offer_as_download": True, + }, + ) + ) + + conn.close() + enrichment_result.transforms = transforms + return enrichment_result + + except Exception as e: + logger.exception( + e, + message="Error processing Chrome Login Data database", + object_id=object_id, + file_path=file_enriched.path, + ) + + +def create_enrichment_module() -> EnrichmentModule: + return ChromeLoginsParser() diff --git a/libs/file_enrichment_modules/file_enrichment_modules/cng_file/analyzer.py b/libs/file_enrichment_modules/file_enrichment_modules/cng_file/analyzer.py new file mode 100644 index 0000000..ca7e918 --- /dev/null +++ b/libs/file_enrichment_modules/file_enrichment_modules/cng_file/analyzer.py @@ -0,0 +1,417 @@ +"""CNG file enrichment module. + +This module processes Windows CNG (Cryptography Next Generation) key files, +parsing their structure and attempting to decrypt DPAPI-protected components. +""" + +from typing import TYPE_CHECKING +from uuid import UUID + +import psycopg +import yara_x +from chromium.local_state import retry_decrypt_state_keys_for_chromekey +from common.db import get_postgres_connection_str +from common.logger import get_logger +from common.models import EnrichmentResult +from common.state_helpers import get_file_enriched, get_file_enriched_async +from common.storage import StorageMinio +from file_enrichment_modules.cng_file.cng_parser import ( + check_bcrypt_key_blob, + extract_dpapi_blob_from_cng_property, + extract_final_key_material, + parse_cng_stream, +) +from file_enrichment_modules.module_loader import EnrichmentModule +from nemesis_dpapi import Blob, BlobDecryptionError, DpapiManager, MasterKeyNotDecryptedError, MasterKeyNotFoundError +from psycopg.rows import dict_row + +if TYPE_CHECKING: + import asyncio + +logger = get_logger(__name__) + + +class CngFileAnalyzer(EnrichmentModule): + def __init__(self, standalone: bool = False): + super().__init__("cng_analyzer") + self.storage = StorageMinio() + self.dpapi_manager: DpapiManager = None # type: ignore + self.loop: asyncio.AbstractEventLoop = None # type: ignore + self.workflows = ["default"] + self._conninfo = get_postgres_connection_str() + + # Yara rule to identify CNG files + self.yara_rule = yara_x.compile(""" +rule is_cng_file +{ + strings: + // CNG file header pattern: + // DWORD version (typically 0x00000001) + // DWORD headerLength (typically 0x00000000) + // DWORD type (0x22000000 for key files) + $cng_header = { 01 00 00 00 00 00 00 00 22 00 00 00 } + + // UTF-16LE "Private Key Properties" string commonly found in CNG files + $priv_key_props = { 50 00 72 00 69 00 76 00 61 00 74 00 65 00 20 00 4B 00 65 00 79 00 20 00 50 00 72 00 6F 00 70 00 65 00 72 00 74 00 69 00 65 00 73 00 } + + // UTF-16LE "Private Key" string + $priv_key = { 50 00 72 00 69 00 76 00 61 00 74 00 65 00 20 00 4B 00 65 00 79 00 } + + // Modified timestamp property name in UTF-16LE + $modified = { 4D 00 6F 00 64 00 69 00 66 00 69 00 65 00 64 00 } + + condition: + // CNG header at start of file + characteristic UTF-16LE strings + $cng_header at 0 and ($priv_key_props or ($priv_key and $modified)) +} +""") + + def should_process(self, object_id: str, file_path: str | None = None) -> bool: + """Check if this file should be processed as a CNG file. + + Args: + object_id: The object ID of the file + file_path: Optional path to already downloaded file + """ + file_enriched = get_file_enriched(object_id) + + # CNG files are typically small (< 10KB) + if file_enriched.size > 10000: + return False + + if file_path: + with open(file_path, "rb") as f: + file_bytes = f.read() + else: + file_bytes = self.storage.download_bytes(file_enriched.object_id) + + should_run = len(self.yara_rule.scan(file_bytes).matching_rules) > 0 + return should_run + + async def process(self, object_id: str, file_path: str | None = None) -> EnrichmentResult | None: + """Process CNG file and extract/decrypt contents. + + Args: + object_id: The object ID of the file + file_path: Optional path to already downloaded file + + Returns: + EnrichmentResult or None if processing fails + """ + return await self._process_async(object_id, file_path) + + async def _process_async(self, object_id: str, file_path: str | None = None) -> EnrichmentResult | None: + """Process CNG file asynchronously. + + Args: + object_id: The object ID of the file + file_path: Optional path to already downloaded file + """ + + try: + file_enriched = await get_file_enriched_async(object_id) + enrichment_result = EnrichmentResult(module_name=self.name) + + logger.info(f"Processing CNG file: {file_enriched.path} ({file_enriched.object_id})") + + # Parse the CNG file + if file_path: + with open(file_path, "rb") as f: + cng_file = parse_cng_stream(f) + else: + with self.storage.download(file_enriched.object_id) as temp_file: + with open(temp_file.name, "rb") as f: + cng_file = parse_cng_stream(f) + + if not cng_file: + logger.error(f"Failed to parse CNG file: {file_enriched.path}") + return None + + logger.info( + f"Parsed CNG file '{cng_file.name}': " + f"version={cng_file.version}, type={cng_file.type}, " + f"has_public_key={cng_file.public_key is not None}, " + f"has_private_key={cng_file.private_key is not None}, " + f"has_private_props={cng_file.private_properties is not None}" + ) + + # Display public properties + if cng_file.public_properties: + logger.info(f"Found {len(cng_file.public_properties)} public properties:") + for prop in cng_file.public_properties: + logger.info(f" - {prop.name}: {len(prop.data)} bytes") + + # Attempt to decrypt private properties (DPAPI blob) + if cng_file.private_properties: + logger.info("Attempting to decrypt private properties DPAPI blob...") + await self._decrypt_private_properties(cng_file.private_properties) + + # Attempt to decrypt private key + private_key_result = None + if cng_file.private_key: + logger.info("Attempting to decrypt private key...") + private_key_result = await self._decrypt_private_key(file_enriched, cng_file.private_key, cng_file.name) + + enrichment_result.results = { + "cng_file_name": cng_file.name, + "version": cng_file.version, + "type": cng_file.type, + "has_public_key": cng_file.public_key is not None, + "has_private_key": cng_file.private_key is not None, + "has_private_properties": cng_file.private_properties is not None, + "public_properties_count": len(cng_file.public_properties), + } + + # Add private key decryption results if available + if private_key_result: + enrichment_result.results["private_key_masterkey_guid"] = private_key_result["masterkey_guid"] + enrichment_result.results["private_key_is_decrypted"] = private_key_result["is_decrypted"] + if private_key_result.get("decrypted_key_hex"): + enrichment_result.results["private_key_decrypted_hex"] = private_key_result["decrypted_key_hex"] + + return enrichment_result + + except Exception as e: + logger.exception(e, message="Error in CNG file processing") + return None + + async def _decrypt_private_properties(self, private_props_blob: bytes) -> None: + """Attempt to decrypt private properties DPAPI blob. + + Args: + private_props_blob: Raw DPAPI blob bytes + """ + + # ref https://github.com/gentilkiwi/mimikatz/blob/152b208916c27d7d1fc32d10e64879721c4d06af/modules/kull_m_key.h#L12 + # can't forget the null terminator ;) + cng_key_properties_entropy = b"6jnkd5J3ZdQDtrsu\x00" + + try: + # Parse as DPAPI blob + blob = Blob.from_bytes(private_props_blob) + logger.info(f"Private properties blob uses masterkey: {blob.masterkey_guid}") + + # Attempt decryption + try: + decrypted_props = await self.dpapi_manager.decrypt_blob(blob, entropy=cng_key_properties_entropy) + logger.info(f"Successfully decrypted private properties! Size: {len(decrypted_props)} bytes") + + # Try to parse decrypted properties + from file_enrichment_modules.cng_file.cng_parser import parse_cng_properties + + properties = parse_cng_properties(decrypted_props) + if properties: + logger.info(f"Found {len(properties)} decrypted private properties:") + for prop in properties: + logger.info(f" - {prop.name}: {len(prop.data)} bytes") + + except (MasterKeyNotDecryptedError, MasterKeyNotFoundError) as e: + logger.debug( + f"Cannot decrypt private properties: masterkey {blob.masterkey_guid} not available", + reason=type(e).__name__, + ) + except BlobDecryptionError as e: + logger.warning(f"Failed to decrypt private properties blob: {e}", masterkey_guid=blob.masterkey_guid) + + except Exception as e: + logger.warning(f"Error processing private properties as DPAPI blob: {e}") + + async def _store_chrome_key( + self, file_enriched, masterkey_guid: UUID, encrypted_bytes: bytes, decrypted_bytes: bytes | None = None + ) -> None: + """Store Chrome key data in the database. + + Args: + file_enriched: File enrichment metadata + masterkey_guid: GUID of the masterkey used to encrypt the key + encrypted_bytes: Raw DPAPI blob bytes + decrypted_bytes: Final 32-byte decrypted key material (if available) + """ + try: + with psycopg.connect(self._conninfo, row_factory=dict_row) as conn: + with conn.cursor() as cur: + cur.execute( + """ + INSERT INTO chromium.chrome_keys ( + originating_object_id, + agent_id, + source, + project, + key_masterkey_guid, + key_bytes_enc, + key_bytes_dec, + key_is_decrypted + ) VALUES (%s, %s, %s, %s, %s, %s, %s, %s) + ON CONFLICT (key_masterkey_guid) DO UPDATE SET + originating_object_id = EXCLUDED.originating_object_id, + agent_id = EXCLUDED.agent_id, + project = EXCLUDED.project, + key_masterkey_guid = EXCLUDED.key_masterkey_guid, + key_bytes_enc = EXCLUDED.key_bytes_enc, + key_bytes_dec = EXCLUDED.key_bytes_dec, + key_is_decrypted = EXCLUDED.key_is_decrypted + """, + ( + file_enriched.object_id, + file_enriched.agent_id, + file_enriched.source, + file_enriched.project, + masterkey_guid, + encrypted_bytes, + decrypted_bytes, + decrypted_bytes is not None, + ), + ) + conn.commit() + + logger.info( + f"Stored Chrome key for source {file_enriched.source}, " + f"masterkey {masterkey_guid}, decrypted={decrypted_bytes is not None}" + ) + + # If we successfully decrypted the chromekey, try to decrypt any waiting state_keys + if decrypted_bytes is not None: + try: + state_keys_result = await retry_decrypt_state_keys_for_chromekey( + file_enriched.source, decrypted_bytes + ) + logger.info( + "Completed retroactive state_key decryption for newly decrypted chromekey", + source=file_enriched.source, + masterkey_guid=masterkey_guid, + state_keys_result=state_keys_result, + ) + except Exception as e: + logger.warning( + "Error retrying state_keys after chromekey decryption in CNG analyzer", + source=file_enriched.source, + error=str(e), + ) + + except Exception as e: + logger.error(f"Failed to store Chrome key: {e}") + + async def _decrypt_private_key( + self, file_enriched, private_key_data: bytes, cng_file_name: str = "" + ) -> dict | None: + """Attempt to decrypt private key data and store in database. + + Args: + file_enriched: File enrichment metadata + private_key_data: Raw private key bytes (direct DPAPI blob) + cng_file_name: Name of the CNG file (to check if it's "Google Chromekey1") + + Returns: + Dict with masterkey_guid, is_decrypted, and decrypted_key_hex (if decrypted) + """ + try: + # ref https://github.com/gentilkiwi/mimikatz/blob/152b208916c27d7d1fc32d10e64879721c4d06af/modules/kull_m_key.h#L13 + # can't forget the null terminator ;) + cng_key_blob_entropy = b"xT5rZW5qVVbrvpuA\x00" + + # Try to parse as DPAPI blob directly (CNG private keys are direct DPAPI blobs) + try: + blob = Blob.from_bytes(private_key_data) + logger.debug(f"Private key is DPAPI encrypted with masterkey: {blob.masterkey_guid}") + import base64 + + logger.debug(f"blob: {base64.b64encode(blob.encrypted_data).decode('utf-8')}") + + # Attempt decryption + decrypted_key = None + final_key_material = None + + try: + decrypted_key = await self.dpapi_manager.decrypt_blob(blob, entropy=cng_key_blob_entropy) + logger.info(f"Successfully decrypted private key! Size: {len(decrypted_key)} bytes") + + # Check for BCRYPT_KEY_DATA_BLOB_HEADER + if check_bcrypt_key_blob(decrypted_key): + # Extract final 32-byte key material + final_key_material = extract_final_key_material(decrypted_key) + if final_key_material: + logger.debug("Extracted final 32-byte key material for database storage") + + except (MasterKeyNotDecryptedError, MasterKeyNotFoundError) as e: + logger.debug( + f"Cannot decrypt private key: masterkey {blob.masterkey_guid} not available", + reason=type(e).__name__, + ) + except BlobDecryptionError as e: + logger.warning(f"Failed to decrypt private key blob: {e}", masterkey_guid=blob.masterkey_guid) + + # Store in database only if this is Google Chromekey1 + if cng_file_name == "Google Chromekey1": + await self._store_chrome_key( + file_enriched=file_enriched, + masterkey_guid=blob.masterkey_guid, + encrypted_bytes=private_key_data, + decrypted_bytes=final_key_material, + ) + + # Return results + result = {"masterkey_guid": str(blob.masterkey_guid), "is_decrypted": final_key_material is not None} + if final_key_material: + result["decrypted_key_hex"] = final_key_material.hex() + + return result + + except Exception as e: + # If direct parsing fails, try extracting from property wrapper + logger.debug(f"Direct DPAPI parsing failed ({e}), trying property extraction...") + dpapi_blob_data = extract_dpapi_blob_from_cng_property(private_key_data) + + if dpapi_blob_data and dpapi_blob_data != private_key_data: + logger.debug(f"Extracted {len(dpapi_blob_data)} bytes from property wrapper") + try: + blob = Blob.from_bytes(dpapi_blob_data) + logger.debug(f"Extracted blob uses masterkey: {blob.masterkey_guid}") + + # Attempt decryption + final_key_material = None + try: + decrypted_key = await self.dpapi_manager.decrypt_blob(blob, entropy=cng_key_blob_entropy) + logger.info( + f"Successfully decrypted extracted private key! Size: {len(decrypted_key)} bytes" + ) + + if check_bcrypt_key_blob(decrypted_key): + # Extract final 32-byte key material + final_key_material = extract_final_key_material(decrypted_key) + if final_key_material: + logger.info("Extracted final 32-byte key material for database storage") + + except Exception as decrypt_error: + logger.warning(f"Failed to decrypt extracted blob: {decrypt_error}") + + # Store in database only if this is Google Chromekey1 + if cng_file_name == "Google Chromekey1": + await self._store_chrome_key( + file_enriched=file_enriched, + masterkey_guid=blob.masterkey_guid, + encrypted_bytes=dpapi_blob_data, + decrypted_bytes=final_key_material, + ) + + # Return results + result = { + "masterkey_guid": str(blob.masterkey_guid), + "is_decrypted": final_key_material is not None, + } + if final_key_material: + result["decrypted_key_hex"] = final_key_material.hex() + + return result + + except Exception as e2: + logger.warning(f"Failed to process extracted blob: {e2}") + + except Exception as e: + logger.warning(f"Error processing private key: {e}") + + return None + + +def create_enrichment_module(standalone: bool = False) -> EnrichmentModule: + """Factory function that creates the analyzer in either standalone or service mode.""" + return CngFileAnalyzer(standalone=standalone) diff --git a/libs/file_enrichment_modules/file_enrichment_modules/cng_file/cng_parser.py b/libs/file_enrichment_modules/file_enrichment_modules/cng_file/cng_parser.py new file mode 100644 index 0000000..ac0eaa9 --- /dev/null +++ b/libs/file_enrichment_modules/file_enrichment_modules/cng_file/cng_parser.py @@ -0,0 +1,360 @@ +"""Windows CNG file parser and decryptor. + +This module parses Windows CNG (Cryptography Next Generation) key files +and attempts to decrypt their contents using DPAPI. + +Specifically, we're only (currently) focused on the CNG file that holds the +KEY_DATA_BLOB_MAGIC "Google Chromekey1" AES key for v3 of Chromium +App-Bound Encryption. +""" + +import struct +from dataclasses import dataclass +from pathlib import Path +from typing import BinaryIO + +from common.logger import get_logger + +logger = get_logger(__name__) + + +# Ref - https://pkg.go.dev/github.com/ElMostafaIdrassi/goncrypt#section-readme +BCRYPT_KEY_DATA_BLOB_MAGIC = 0x4D42444B # 'KDBM' +BCRYPT_RSAPUBLIC_MAGIC = 0x31415352 # 'RSA1' +BCRYPT_RSAPRIVATE_MAGIC = 0x32415352 # 'RSA2' +BCRYPT_RSAFULLPRIVATE_BLOB = 0x33415352 # 'RSA3' + + +@dataclass +class CngPropertyHeader: + """CNG property header structure.""" + + name_length: int + property_length: int + name: str + data: bytes + + +@dataclass +class CngKeyFile: + """Parsed CNG key file structure.""" + + version: int + header_length: int + type: int + name_length: int + public_length: int + private_length: int + privprop_length: int + unknown: int + name: str + public_properties: list[CngPropertyHeader] + public_key: bytes | None + private_properties: bytes | None # DPAPI blob + private_key: bytes | None # May be DPAPI encrypted + + +# Ref - https://learn.microsoft.com/en-us/windows/win32/api/bcrypt/ns-bcrypt-bcrypt_key_data_blob_header +@dataclass +class BcryptKeyDataBlobHeader: + """BCRYPT_KEY_DATA_BLOB_HEADER structure.""" + + magic: int + version: int + key_data_length: int + + +def parse_cng_properties(data: bytes, offset: int = 0) -> list[CngPropertyHeader]: + """Parse CNG property headers from binary data. + + Args: + data: Binary data containing properties + offset: Starting offset in data + + Returns: + List of parsed CNG property headers + """ + properties = [] + pos = offset + + while pos < len(data): + if pos + 8 > len(data): + break + + # Read property header: name_length (4) + property_length (4) + name_len, prop_len = struct.unpack(" len(data): + break + + name = data[pos : pos + name_len].decode("utf-16le", errors="ignore").rstrip("\x00") + pos += name_len + + # Read property data + if pos + prop_len > len(data): + break + + prop_data = data[pos : pos + prop_len] + pos += prop_len + + properties.append(CngPropertyHeader(name_length=name_len, property_length=prop_len, name=name, data=prop_data)) + + return properties + + +def extract_dpapi_blob_from_cng_property(data: bytes) -> bytes | None: + """Extract DPAPI blob from a CNG property structure. + + The private key in CNG files is wrapped in a property structure: + - dwStructLen (4 bytes) + - type (4 bytes) + - unk (4 bytes) + - dwNameLen (4 bytes) + - dwPropertyLen (4 bytes) + - pName (variable, UTF-16LE) + - pProperty (dwPropertyLen bytes) <- This contains the DPAPI blob + + Args: + data: Raw private key data from CNG file + + Returns: + Extracted DPAPI blob or None if parsing fails + """ + if len(data) < 20: # Minimum header size + return None + + try: + # Parse CNG property header + struct_len, prop_type, unk, name_len, property_len = struct.unpack(" len(data): + logger.error( + f"Property data extends beyond buffer: offset={property_offset}, len={property_len}, data_len={len(data)}" + ) + return None + + # Extract the property data (should be DPAPI blob) + dpapi_blob = data[property_offset : property_offset + property_len] + + # Verify it looks like a DPAPI blob (starts with version 0x00000001) + if len(dpapi_blob) >= 4: + version = struct.unpack(" CngKeyFile | None: + """Parse a Windows CNG key file. + + Args: + file_path: Path to the CNG file + + Returns: + Parsed CngKeyFile or None if parsing fails + """ + try: + with open(file_path, "rb") as f: + return parse_cng_stream(f) + except Exception as e: + logger.error(f"Error parsing CNG file {file_path}: {e}") + return None + + +def parse_cng_stream(stream: BinaryIO) -> CngKeyFile | None: + """Parse a Windows CNG key file from a stream. + + Args: + stream: Binary stream containing CNG data + + Returns: + Parsed CngKeyFile or None if parsing fails + """ + try: + # Read main header (44 bytes) + # Structure: version(4) + unk(4) + name_len(4) + type(4) + + # public_len(4) + privprop_len(4) + privkey_len(4) + unkArray[16] + header_data = stream.read(44) + if len(header_data) < 44: + logger.error("CNG file too short for header") + return None + + # Parse header fields + (version, unk, name_len, key_type, public_len, privprop_len, privkey_len) = struct.unpack( + " 0: + name_bytes = stream.read(name_len) + if len(name_bytes) < name_len: + logger.error(f"Unexpected EOF reading key name: expected {name_len}, got {len(name_bytes)}") + return None + name = name_bytes.decode("utf-16le", errors="ignore").rstrip("\x00") + else: + name = "" + name_bytes = b"" + + # Read public properties + public_properties = [] + if public_len > 0: + public_data = stream.read(public_len) + if len(public_data) < public_len: + logger.error("Unexpected EOF reading public properties") + return None + public_properties = parse_cng_properties(public_data) + + # No separate public key section in this format + public_key = None + + # Read private properties DPAPI blob (privprop_len bytes) + private_properties = None + if privprop_len > 0: + private_properties = stream.read(privprop_len) + if len(private_properties) < privprop_len: + logger.error( + f"Unexpected EOF reading private properties: expected {privprop_len}, got {len(private_properties)}" + ) + return None + + # Read private key DPAPI blob (privkey_len bytes) + private_key = None + if privkey_len > 0: + private_key = stream.read(privkey_len) + if len(private_key) < privkey_len: + logger.error(f"Unexpected EOF reading private key: expected {privkey_len}, got {len(private_key)}") + return None + + return CngKeyFile( + version=version, + header_length=unk, + type=key_type, + name_length=name_len, + public_length=public_len, + private_length=privkey_len, + privprop_length=privprop_len, + unknown=unk, + name=name, + public_properties=public_properties, + public_key=public_key, + private_properties=private_properties, + private_key=private_key, + ) + + except Exception as e: + logger.error(f"Error parsing CNG stream: {e}") + return None + + +def parse_bcrypt_key_data_blob(data: bytes) -> BcryptKeyDataBlobHeader | None: + """Parse BCRYPT_KEY_DATA_BLOB_HEADER from decrypted data. + + Args: + data: Decrypted private key data + + Returns: + Parsed header or None if invalid + """ + if len(data) < 12: + return None + + magic, version, key_len = struct.unpack(" BcryptKeyDataBlobHeader | None: + """Check if decrypted data contains BCRYPT_KEY_DATA_BLOB and log details. + + Args: + key_data: Decrypted or plaintext key data + + Returns: + Parsed BCRYPT_KEY_DATA_BLOB_HEADER if found, None otherwise + """ + header = parse_bcrypt_key_data_blob(key_data) + + if header: + logger.debug( + f"Found BCRYPT_KEY_DATA_BLOB_HEADER! " + f"Magic: 0x{header.magic:08X} (KDBM), " + f"Version: {header.version}, " + f"Key length: {header.key_data_length} bytes" + ) + + # Extract final 32 bytes + final_key = extract_final_key_material(key_data) + if final_key: + logger.info(f"Extracted final 32-byte key material: {final_key.hex()}") + else: + logger.warning("Failed to extract final 32-byte key material") + else: + logger.debug( + f"Key data does not contain BCRYPT_KEY_DATA_BLOB_HEADER " + f"(magic: 0x{struct.unpack(' bytes | None: + """Extract final 32-byte key material from BCRYPT_KEY_DATA_BLOB. + + Args: + decrypted_data: Decrypted private key data with KDBM header + + Returns: + Final 32 bytes of key material or None if invalid + """ + header = parse_bcrypt_key_data_blob(decrypted_data) + if not header: + return None + + # Key data follows the 12-byte header + if len(decrypted_data) < 12 + header.key_data_length: + logger.error(f"Decrypted data too short: expected {12 + header.key_data_length}, got {len(decrypted_data)}") + return None + + # Extract the last 32 bytes of the key data + key_data_start = 12 + key_data_end = 12 + header.key_data_length + key_data = decrypted_data[key_data_start:key_data_end] + + if len(key_data) < 32: + logger.error(f"Key data too short for 32-byte extraction: {len(key_data)} bytes") + return None + + return key_data[-32:] diff --git a/libs/file_enrichment_modules/file_enrichment_modules/container/analyzer.py b/libs/file_enrichment_modules/file_enrichment_modules/container/analyzer.py index 9cbe1c4..ffa0167 100644 --- a/libs/file_enrichment_modules/file_enrichment_modules/container/analyzer.py +++ b/libs/file_enrichment_modules/file_enrichment_modules/container/analyzer.py @@ -5,15 +5,14 @@ import zipfile from datetime import UTC, datetime import py7zr -import structlog from common.helpers import is_container +from common.logger import get_logger from common.models import EnrichmentResult, Transform from common.state_helpers import get_file_enriched from common.storage import StorageMinio - from file_enrichment_modules.module_loader import EnrichmentModule -logger = structlog.get_logger(module=__name__) +logger = get_logger(__name__) class ContainerAnalyzer(EnrichmentModule): @@ -22,8 +21,13 @@ class ContainerAnalyzer(EnrichmentModule): self.storage = StorageMinio() self.workflows = ["default"] - def should_process(self, object_id: str) -> bool: - """Determine if this module should run.""" + def should_process(self, object_id: str, file_path: str | None = None) -> bool: + """Determine if this module should run. + + Args: + object_id: The object ID of the file + file_path: Optional path to already downloaded file (not used by container analyzer) + """ file_enriched = get_file_enriched(object_id) return is_container(file_enriched.mime_type) @@ -55,80 +59,105 @@ class ContainerAnalyzer(EnrichmentModule): with tarfile.open(file_path) as tf: return [(member.name, member.size) for member in tf.getmembers() if member.isfile()] - def process(self, object_id: str) -> EnrichmentResult | None: - """Process container file and list its contents without extraction.""" + def _analyze_container(self, file_path: str, file_enriched) -> EnrichmentResult | None: + """Analyze container file and generate enrichment result. + + Args: + file_path: Path to the container file to analyze + file_enriched: File enrichment data + + Returns: + EnrichmentResult or None if analysis fails + """ + enrichment_result = EnrichmentResult(module_name=self.name, dependencies=self.dependencies) + + try: + # Analyze based on file type + if zipfile.is_zipfile(file_path): + files = self._analyze_zip(file_path) + elif py7zr.is_7zfile(file_path): + files = self._analyze_7z(file_path) + elif tarfile.is_tarfile(file_path): + files = self._analyze_tar(file_path) + else: + logger.warning(f"Unsupported container format for {file_enriched.file_name}") + return None + + # Generate the report + report_lines = [] + report_lines.append(f"# Container Contents: {file_enriched.file_name}") + report_lines.append(f"\nAnalysis timestamp: {datetime.now(UTC).isoformat()}") + + # Calculate summary + total_size = sum(size for _, size in files) + file_count = len(files) + + # Add summary + report_lines.append("\n## Summary") + report_lines.append(f"- Total files: {file_count}") + report_lines.append(f"- Total size: {self._format_size(total_size)}") + + git_repo_count = 0 + for filepath, _size in sorted(files): + if filepath.endswith(".git/config") or filepath.endswith(".git\\config"): + git_repo_count += 1 + if git_repo_count > 0: + report_lines.append(f"- Contains {git_repo_count} .git repos") + + # Add file listing + report_lines.append("\n## File Listing") + report_lines.append("\n| File Path | Size |") + report_lines.append("| --------- | ---- |") + + # Add each file to the table + for filepath, size in sorted(files): + # Escape any pipe characters in the path + safe_path = filepath.replace("|", "\\|") + report_lines.append(f"| {safe_path} | {self._format_size(size)} |") + + # Create the transform + with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8") as tmp_report: + tmp_report.write("\n".join(report_lines)) + tmp_report.flush() + transform_object_id = self.storage.upload_file(tmp_report.name) + + enrichment_result.transforms = [ + Transform( + type="container_contents", + object_id=f"{transform_object_id}", + metadata={ + "file_name": f"{file_enriched.file_name}_contents.md", + "display_type_in_dashboard": "markdown", + "default_display": True, + }, + ) + ] + + except Exception as e: + logger.exception(e, message=f"Error analyzing container contents for {file_enriched.file_name}") + return None + + return enrichment_result + + def process(self, object_id: str, file_path: str | None = None) -> EnrichmentResult | None: + """Process container file and list its contents without extraction. + + Args: + object_id: The object ID of the file + file_path: Optional path to already downloaded file + + Returns: + EnrichmentResult or None if processing fails + """ try: file_enriched = get_file_enriched(object_id) - enrichment_result = EnrichmentResult(module_name=self.name, dependencies=self.dependencies) - with self.storage.download(file_enriched.object_id) as temp_file: - # Analyze based on file type - try: - if zipfile.is_zipfile(temp_file.name): - files = self._analyze_zip(temp_file.name) - elif py7zr.is_7zfile(temp_file.name): - files = self._analyze_7z(temp_file.name) - elif tarfile.is_tarfile(temp_file.name): - files = self._analyze_tar(temp_file.name) - else: - logger.warning(f"Unsupported container format for {file_enriched.file_name}") - return None - - # Generate the report - report_lines = [] - report_lines.append(f"# Container Contents: {file_enriched.file_name}") - report_lines.append(f"\nAnalysis timestamp: {datetime.now(UTC).isoformat()}") - - # Calculate summary - total_size = sum(size for _, size in files) - file_count = len(files) - - # Add summary - report_lines.append("\n## Summary") - report_lines.append(f"- Total files: {file_count}") - report_lines.append(f"- Total size: {self._format_size(total_size)}") - - git_repo_count = 0 - for filepath, size in sorted(files): - if filepath.endswith(".git/config") or filepath.endswith(".git\\config"): - git_repo_count += 1 - if git_repo_count > 0: - report_lines.append(f"- Contains {git_repo_count} .git repos") - - # Add file listing - report_lines.append("\n## File Listing") - report_lines.append("\n| File Path | Size |") - report_lines.append("| --------- | ---- |") - - # Add each file to the table - for filepath, size in sorted(files): - # Escape any pipe characters in the path - safe_path = filepath.replace("|", "\\|") - report_lines.append(f"| {safe_path} | {self._format_size(size)} |") - - # Create the transform - with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8") as tmp_report: - tmp_report.write("\n".join(report_lines)) - tmp_report.flush() - transform_object_id = self.storage.upload_file(tmp_report.name) - - enrichment_result.transforms = [ - Transform( - type="container_contents", - object_id=f"{transform_object_id}", - metadata={ - "file_name": f"{file_enriched.file_name}_contents.md", - "display_type_in_dashboard": "markdown", - "default_display": True, - }, - ) - ] - - except Exception as e: - logger.exception(e, message=f"Error analyzing container contents for {file_enriched.file_name}") - return None - - return enrichment_result + # Use provided file_path if available, otherwise download + if file_path: + return self._analyze_container(file_path, file_enriched) + else: + with self.storage.download(file_enriched.object_id) as temp_file: + return self._analyze_container(temp_file.name, file_enriched) except Exception as e: logger.exception(e, message="Error in container analyzer") diff --git a/libs/file_enrichment_modules/file_enrichment_modules/container_contents/analyzer.py b/libs/file_enrichment_modules/file_enrichment_modules/container_contents/analyzer.py index a80683b..de75275 100644 --- a/libs/file_enrichment_modules/file_enrichment_modules/container_contents/analyzer.py +++ b/libs/file_enrichment_modules/file_enrichment_modules/container_contents/analyzer.py @@ -2,17 +2,16 @@ import os import tempfile -import structlog from common.helpers import is_container +from common.logger import get_logger from common.models import EnrichmentResult, File, Transform from common.state_helpers import get_file_enriched from common.storage import StorageMinio from dapr.clients import DaprClient - from file_enrichment_modules.container_contents.containers import ContainerExtractor from file_enrichment_modules.module_loader import EnrichmentModule -logger = structlog.get_logger(module=__name__) +logger = get_logger(__name__) class ContainerContentsAnalyzer(EnrichmentModule): @@ -23,7 +22,7 @@ class ContainerContentsAnalyzer(EnrichmentModule): # Configuration for container extraction self.extracted_archive_size_limit = 1_073_741_824 # 1GB default - def should_process(self, object_id: str) -> bool: + def should_process(self, object_id: str, file_path: str | None = None) -> bool: """Determine if this module should run.""" file_enriched = get_file_enriched(object_id) @@ -67,7 +66,7 @@ class ContainerContentsAnalyzer(EnrichmentModule): return "\n".join(summary_lines) - def process(self, object_id: str) -> EnrichmentResult | None: + def process(self, object_id: str, file_path: str | None = None) -> EnrichmentResult | None: """Process container file and extract its contents.""" try: file_enriched = get_file_enriched(object_id) @@ -77,12 +76,6 @@ class ContainerContentsAnalyzer(EnrichmentModule): # Initialize DaprClient and ContainerExtractor with DaprClient() as dapr_client: - container_extractor = ContainerExtractor( - self.storage, - dapr_client, - self.extracted_archive_size_limit, - ) - # Create a subclass to capture extracted files class TrackingContainerExtractor(ContainerExtractor): def publish_file_message(self, file_message: File): diff --git a/libs/file_enrichment_modules/file_enrichment_modules/container_contents/containers.py b/libs/file_enrichment_modules/file_enrichment_modules/container_contents/containers.py index 1a9a032..9b9ebaf 100644 --- a/libs/file_enrichment_modules/file_enrichment_modules/container_contents/containers.py +++ b/libs/file_enrichment_modules/file_enrichment_modules/container_contents/containers.py @@ -11,11 +11,11 @@ import zlib from io import SEEK_END import py7zr -import structlog +from common.logger import get_logger from common.models import File, FileEnriched from dapr.clients import DaprClient -logger = structlog.get_logger(module=__name__) +logger = get_logger(__name__) class FileNotSupportedException(Exception): @@ -437,6 +437,7 @@ class ContainerExtractor: file_message = File( object_id=str(object_id), agent_id=file_enriched.agent_id, + source=file_enriched.source, project=file_enriched.project, timestamp=file_enriched.timestamp, expiration=file_enriched.expiration, diff --git a/libs/file_enrichment_modules/file_enrichment_modules/dotnet/analyzer.py b/libs/file_enrichment_modules/file_enrichment_modules/dotnet/analyzer.py index 5c2f485..1fc3322 100644 --- a/libs/file_enrichment_modules/file_enrichment_modules/dotnet/analyzer.py +++ b/libs/file_enrichment_modules/file_enrichment_modules/dotnet/analyzer.py @@ -5,15 +5,14 @@ from pathlib import Path from typing import Union import dnfile -import structlog -from common.models import EnrichmentResult, File, FileObject, Finding, FindingCategory, FindingOrigin, Transform +from common.logger import get_logger +from common.models import DotNetInput, EnrichmentResult from common.state_helpers import get_file_enriched from common.storage import StorageMinio from dapr.clients import DaprClient - from file_enrichment_modules.module_loader import EnrichmentModule -logger = structlog.get_logger(module=__name__) +logger = get_logger(__name__) def get_typerefs(assembly): @@ -107,37 +106,6 @@ def parse_dotnet_assembly(filename: Union[str, Path]) -> dict: return result -def dict_to_markdown(data): - """Generates a markdown report from inspect_assembly data.""" - report = [] - - for section, content in data.items(): - if content and content != {} and content != []: - report.append(f"# {section}") - - if isinstance(content, dict): - for key, items in content.items(): - report.append(f"\n### {key}") - for item in items: - method = item.get("MethodName", "Unknown - Error obtaining method") - report.append(f"- Location in Assembly: `{method}`") - - for key, value in item.items(): - if key == "MethodName" or not value: - continue - report.append(f"- {key}: {value}") - - elif isinstance(content, list): - for item in content: - report.append(f"- {item}") - - return "\n".join(report) - - -def get_non_null_sections(data): - return {k: v for k, v in data.items() if v and v != {} and v != []} - - class DotNetAnalyzer(EnrichmentModule): def __init__(self): super().__init__("dotnet_analyzer", dependencies=["pe"]) @@ -145,109 +113,38 @@ class DotNetAnalyzer(EnrichmentModule): # the workflows this module should automatically run in self.workflows = ["default"] - def should_process(self, object_id: str) -> bool: + def should_process(self, object_id: str, file_path: str | None = None) -> bool: """Determine if this module should run.""" # get the current `file_enriched` from the database backend file_enriched = get_file_enriched(object_id) should_run = "mono/.net assembly" in file_enriched.magic_type.lower() - logger.debug(f"DotNetAnalyzer should_run: {should_run}") + return should_run - def process(self, object_id: str) -> EnrichmentResult | None: + def process(self, object_id: str, file_path: str | None = None) -> EnrichmentResult | None: """Process file using the dotnet service.""" try: # get the current `file_enriched` FileEnriched object from the database backend file_enriched = get_file_enriched(object_id) + # publish a `dotnet-input` message for the process-heavy decompilation and InspectAssembly analysis in `dotnet_service` + dotnet_input = DotNetInput(object_id=object_id) + with DaprClient() as client: + client.publish_event( + pubsub_name="pubsub", + topic_name="dotnet-input", + data=json.dumps(dotnet_input.model_dump()), + data_content_type="application/json", + ) + enrichment = EnrichmentResult(module_name=self.name, dependencies=self.dependencies) - # Step 1 - Get results from local parsing - with self.storage.download(file_enriched.object_id) as temp_file: - enrichment.results = {"parsed": parse_dotnet_assembly(temp_file.name)} - - # Step 2 - call the DotNet API using Dapr SDK - with DaprClient() as dapr_client: - response = dapr_client.invoke_method( - app_id="dotnet-api", - method_name=f"file/{file_enriched.object_id}", - http_verb="get", - timeout=180, - ) - service_results = json.loads(response.data) - - # Step 3 - handle any decompilation results - if ( - "decompilation" in service_results - and "object_id" in service_results["decompilation"] - and service_results["decompilation"]["object_id"] - ): - decompiled_object_id = service_results["decompilation"]["object_id"] - - # enrichment_result - decompilation = Transform( - type="decompilation", - object_id=service_results["decompilation"]["object_id"], - metadata={ - "file_name": f"{file_enriched.file_name}.zip", - "offer_as_download": True, - "display_title": "Decompiled Source Code", - }, - ) - enrichment.transforms = [decompilation] - - file_message = File( - object_id=decompiled_object_id, - agent_id=file_enriched.agent_id, - project=file_enriched.project, - timestamp=file_enriched.timestamp, - expiration=file_enriched.expiration, - path=f"{file_enriched.path}/decompiled.zip", - originating_object_id=file_enriched.object_id, - nesting_level=(file_enriched.nesting_level or 0) + 1, - ) - - with DaprClient() as dapr_client: - data = json.dumps(file_message.model_dump(exclude_unset=True, mode="json")) - dapr_client.publish_event( - pubsub_name="pubsub", - topic_name="file", - data=data, - data_content_type="application/json", - ) - - logger.info( - "Submitted decompiled source ZIP to Nemesis", - decompiled_object_id=decompiled_object_id, - originating_object_id=file_enriched.object_id, - ) - - # Step 5 - handle any deserialization results - if "inspect_assembly" in service_results and service_results["inspect_assembly"]: - logger.debug(f"service_results['inspect_assembly']: {service_results['inspect_assembly']}") - inspect_assembly = get_non_null_sections(service_results["inspect_assembly"]) - if inspect_assembly: - # Store the raw enrichment result - enrichment.results["inspect_assembly"] = inspect_assembly - - # Generate a markdown finding summary - summary_markdown = dict_to_markdown(inspect_assembly) - display_data = FileObject( - type="finding_summary", - metadata={"summary": summary_markdown}, - ) - - finding = Finding( - category=FindingCategory.VULNERABILITY, - finding_name="dotnet_vulns", - origin_type=FindingOrigin.ENRICHMENT_MODULE, - origin_name=self.name, - object_id=file_enriched.object_id, - severity=9, - raw_data=inspect_assembly, - data=[display_data], - ) - - enrichment.findings = [finding] + # Get results from local parsing + if file_path: + enrichment.results = {"parsed": parse_dotnet_assembly(file_path)} + else: + with self.storage.download(file_enriched.object_id) as temp_file: + enrichment.results = {"parsed": parse_dotnet_assembly(temp_file.name)} return enrichment diff --git a/libs/file_enrichment_modules/file_enrichment_modules/dpapi/analyzer.py b/libs/file_enrichment_modules/file_enrichment_modules/dpapi/analyzer.py deleted file mode 100644 index 7e8fc8c..0000000 --- a/libs/file_enrichment_modules/file_enrichment_modules/dpapi/analyzer.py +++ /dev/null @@ -1,104 +0,0 @@ -# enrichment_modules/dpapi/analyzer.py -import asyncio - -import structlog -import yara_x -from common.models import EnrichmentResult, FileObject, Finding, FindingCategory, FindingOrigin -from common.state_helpers import get_file_enriched -from common.storage import StorageMinio -from dapr.clients import DaprClient - -from file_enrichment_modules.dpapi.dpapi_helpers import carve_dpapi_blobs_from_file -from file_enrichment_modules.module_loader import EnrichmentModule - -logger = structlog.get_logger(module=__name__) - - -class DPAPIAnalyzer(EnrichmentModule): - def __init__(self, standalone: bool = False): - super().__init__("dpapi_analyzer") - self.storage = StorageMinio() - self.dapr_client = DaprClient() - self.size_limit = 50000000 # only check the first 50 megs for DPAPI blobs, for performance - self.max_blobs = 100 - # the workflows this module should automatically run in - self.workflows = ["default"] - - # Yara rule to check for DPAPI blob content - self.yara_rule = yara_x.compile(""" -rule has_dpapi_blob -{ - strings: - $dpapi_header = { 01 00 00 00 D0 8C 9D DF 01 15 D1 11 8C 7A 00 C0 4F C2 97 EB } - $dpapi_header_b64_1 = "AAAA0Iyd3wEV0RGMegDAT8KX6" - $dpapi_header_b64_2 = "AQAAANCMnd8BFdERjHoAwE/Cl+" - $dpapi_header_b64_3 = "EAAADQjJ3fARXREYx6AMBPwpfr" - condition: - $dpapi_header or $dpapi_header_b64_1 or $dpapi_header_b64_2 or $dpapi_header_b64_3 -} - """) - - def should_process(self, object_id: str) -> bool: - file_enriched = get_file_enriched(object_id) - if file_enriched.size > self.size_limit: - logger.warning( - f"[dpapi_analyzer] file {file_enriched.path} ({file_enriched.object_id} / {file_enriched.size} bytes) exceeds the size limit of {self.size_limit} bytes, only analyzing the first {self.size_limit} bytes" - ) - - num_bytes = file_enriched.size if file_enriched.size < self.size_limit else self.size_limit - file_bytes = self.storage.download_bytes(file_enriched.object_id, length=num_bytes) - - should_run = len(self.yara_rule.scan(file_bytes).matching_rules) > 0 - logger.debug(f"[dpapi_analyzer] should_run: {should_run}") - return should_run - - def process(self, object_id: str) -> EnrichmentResult | None: - """Process file in either workflow or standalone mode.""" - try: - file_enriched = get_file_enriched(object_id) - - enrichment_result = EnrichmentResult(module_name=self.name) - - # TODO: handle carving _large_ dpapi blobs + uploading to the datalake - - with self.storage.download(file_enriched.object_id) as temp_file: - blobs = asyncio.run( - carve_dpapi_blobs_from_file(temp_file.name, file_enriched.object_id, self.max_blobs) - ) - masterkey_guids = sorted(set([blob["dpapi_master_key_guid"] for blob in blobs if blob["success"]])) - - if blobs: - summary_markdown = f""" -# DPAPI Blobs Found : {len(blobs)} -# Masterkey GUIDs -List of unique masterkey GUIDs associated with the found blobs: -```text -{"\n".join(masterkey_guids)} -``` -""" - enrichment_result.results = {"blobs": blobs} - - display_data = FileObject(type="finding_summary", metadata={"summary": summary_markdown}) - - finding = Finding( - category=FindingCategory.EXTRACTED_DATA, - finding_name="dpapi_data", - origin_type=FindingOrigin.ENRICHMENT_MODULE, - origin_name=self.name, - object_id=file_enriched.object_id, - severity=5, - raw_data=enrichment_result.results, - data=[display_data], - ) - - enrichment_result.findings = [finding] - - return enrichment_result - - except Exception as e: - logger.exception(e, message="Error in DPAPI process()") - - -def create_enrichment_module(standalone: bool = False) -> EnrichmentModule: - """Factory function that creates the analyzer in either standalone or service mode.""" - return DPAPIAnalyzer(standalone=standalone) diff --git a/libs/file_enrichment_modules/file_enrichment_modules/dpapi_blob/analyzer.py b/libs/file_enrichment_modules/file_enrichment_modules/dpapi_blob/analyzer.py new file mode 100644 index 0000000..9f79d1e --- /dev/null +++ b/libs/file_enrichment_modules/file_enrichment_modules/dpapi_blob/analyzer.py @@ -0,0 +1,307 @@ +# enrichment_modules/dpapi/analyzer.py +import asyncio +import base64 +import csv +import tempfile + +import yara_x +from common.logger import get_logger +from common.models import EnrichmentResult, FileObject, Finding, FindingCategory, FindingOrigin, Transform +from common.state_helpers import get_file_enriched, get_file_enriched_async +from common.storage import StorageMinio +from dapr.clients import DaprClient +from file_enrichment_modules.dpapi_blob.dpapi_helpers import carve_dpapi_blobs_from_file +from file_enrichment_modules.module_loader import EnrichmentModule +from nemesis_dpapi import Blob, BlobDecryptionError, DpapiManager, MasterKeyNotDecryptedError, MasterKeyNotFoundError + +logger = get_logger(__name__) + + +class DpapiBlobAnalyzer(EnrichmentModule): + def __init__(self, standalone: bool = False): + super().__init__("dpapi_analyzer") + self.storage = StorageMinio() + self.dapr_client = DaprClient() + self.size_limit = 50000000 # only check the first 50 megs for DPAPI blobs, for performance + self.max_blobs = 100 + self.dpapi_manager: DpapiManager = None # type: ignore + self.loop: asyncio.AbstractEventLoop = None # type: ignore + # the workflows this module should automatically run in + self.workflows = ["default"] + + # Yara rule to check for DPAPI blob content + self.yara_rule = yara_x.compile(""" +rule has_dpapi_blob +{ + strings: + $dpapi_header = { 01 00 00 00 D0 8C 9D DF 01 15 D1 11 8C 7A 00 C0 4F C2 97 EB } + $dpapi_header_b64_1 = "AAAA0Iyd3wEV0RGMegDAT8KX6" + $dpapi_header_b64_2 = "AQAAANCMnd8BFdERjHoAwE/Cl+" + $dpapi_header_b64_3 = "EAAADQjJ3fARXREYx6AMBPwpfr" + condition: + $dpapi_header or $dpapi_header_b64_1 or $dpapi_header_b64_2 or $dpapi_header_b64_3 +} +""") + + def _format_hex_dump(self, data: bytes, offset: int = 0) -> str: + """Generate hexdump-style output for blob data. + + Args: + data: The bytes to format + offset: Starting offset for display + + Returns: + Formatted hex dump string + """ + lines = [] + for i in range(0, len(data), 16): + chunk = data[i:i+16] + hex_part = ' '.join(f'{b:02x}' for b in chunk) + # Pad hex part to align ASCII + hex_part = hex_part.ljust(48) + ascii_part = ''.join(chr(b) if 32 <= b < 127 else '.' for b in chunk) + lines.append(f'{offset+i:08x} {hex_part} {ascii_part}') + return '\n'.join(lines) + + def should_process(self, object_id: str, file_path: str | None = None) -> bool: + """Check if this file should be processed by scanning for DPAPI blobs. + + Args: + object_id: The object ID of the file + file_path: Optional path to already downloaded file + """ + file_enriched = get_file_enriched(object_id) + logger.debug(f"File {object_id} should be processed by DPAPI blob analyzer") + if file_enriched.size > self.size_limit: + logger.debug( + f"[dpapi_analyzer] file {file_enriched.path} ({file_enriched.object_id} / {file_enriched.size} bytes) exceeds the size limit of {self.size_limit} bytes, only analyzing the first {self.size_limit} bytes" + ) + + if file_path: + # Use provided file path - read only the needed bytes + with open(file_path, "rb") as f: + num_bytes = min(file_enriched.size, self.size_limit) + file_bytes = f.read(num_bytes) + else: + # Fallback to downloading the file itself + num_bytes = file_enriched.size if file_enriched.size < self.size_limit else self.size_limit + file_bytes = self.storage.download_bytes(file_enriched.object_id, length=num_bytes) + + should_run = len(self.yara_rule.scan(file_bytes).matching_rules) > 0 + return should_run + + async def process(self, object_id: str, file_path: str | None = None) -> EnrichmentResult | None: + """Process file and perform DPAPI blob analysis. + + Args: + object_id: The object ID of the file + file_path: Optional path to already downloaded file + + Returns: + EnrichmentResult or None if processing fails + """ + logger.info(f"Starting async for DPAPI blob analysis for object_id {object_id}") + return await self._process_async(object_id, file_path) + + async def _process_async(self, object_id: str, file_path: str | None = None) -> EnrichmentResult | None: + """Process file in either workflow or standalone mode. + + Args: + object_id: The object ID of the file + file_path: Optional path to already downloaded file + """ + + try: + logger.info(f"Starting DPAPI blob analysis for object_id {object_id}") + file_enriched = await get_file_enriched_async(object_id) + logger.info(f"Retrieved enriched file data for object_id {object_id}") + + enrichment_result = EnrichmentResult(module_name=self.name) + + # TODO: handle carving _large_ dpapi blobs + uploading to the datalake + + if file_path: + # Use provided file path (if file already downloaded) + carved_blobs = await carve_dpapi_blobs_from_file(file_path, file_enriched.object_id, self.max_blobs) + + else: + # Fallback to downloading the file itself + with self.storage.download(file_enriched.object_id) as temp_file: + carved_blobs = await carve_dpapi_blobs_from_file( + temp_file.name, file_enriched.object_id, self.max_blobs + ) + + # Track decrypted blobs and their data + decrypted_blobs = [] + + for carved_blob in carved_blobs: + try: + dpapi_blob_raw = carved_blob["dpapi_blob_raw"] + carved_blob["is_decrypted"] = False + carved_blob["decrypted_data"] = None + # Calculate blob length from raw data + carved_blob["blob_length"] = len(dpapi_blob_raw) if dpapi_blob_raw else 0 + + carved_blob_dec = await self.dpapi_manager.decrypt_blob(Blob.from_bytes(dpapi_blob_raw)) + + if carved_blob_dec: + carved_blob["is_decrypted"] = True + carved_blob["decrypted_data"] = carved_blob_dec + decrypted_blobs.append(carved_blob) + logger.info( + "Successfully decrypted blob", + masterkey_guid=carved_blob["dpapi_master_key_guid"], + # b64_dec_blob=base64.b64encode(carved_blob_dec).decode("utf-8"), + ) + # TODO: do something with the decrypted blob? + except BlobDecryptionError as e: + logger.warning( + f"Could not decrypt local state DPAPI blob with its masterkey. Error: {e}", + masterkey_guid=carved_blob["dpapi_master_key_guid"], + error_type=type(e).__name__, + ) + except (MasterKeyNotDecryptedError, MasterKeyNotFoundError) as e: + logger.debug( + f"Blob with GUID masterkey {carved_blob['dpapi_master_key_guid']} not decrypted.", + reason=type(e).__name__, + ) + except Exception as e: + logger.warning( + f"Unhandled error while decrypting DPAPI blob with masterkey. Error: {e}", + masterkey_guid=carved_blob["dpapi_master_key_guid"], + error_type=type(e).__name__, + ) + + # Remove raw blob data to avoid serialization issues + del carved_blob["dpapi_blob_raw"] + + masterkey_guids = sorted({blob["dpapi_master_key_guid"] for blob in carved_blobs if blob["success"]}) + + if carved_blobs: + transforms = [] + + # Create CSV transform for all blobs (up to 10000) + blobs_for_csv = carved_blobs[:10000] + with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8", newline="") as tmp_csv: + writer = csv.writer(tmp_csv) + + # Write header + writer.writerow(["masterkey_guid", "blob_offset", "blob_length", "is_decrypted", "base64_content"]) + + # Write blob data + for blob in blobs_for_csv: + base64_content = "" + # Include base64 content if blob is 1000 bytes or less + if blob.get("decrypted_data") and len(blob["decrypted_data"]) <= 1000: + base64_content = base64.b64encode(blob["decrypted_data"]).decode("utf-8") + + writer.writerow([ + blob["dpapi_master_key_guid"], + blob.get("blob_offset", 0), + blob.get("blob_length", 0), + blob.get("is_decrypted", False), + base64_content, + ]) + + tmp_csv.flush() + csv_object_id = self.storage.upload_file(tmp_csv.name) + + transforms.append( + Transform( + type="dpapi_blobs.csv", + object_id=f"{csv_object_id}", + metadata={ + "file_name": f"{file_enriched.file_name}_dpapi_blobs.csv", + "offer_as_download": True, + }, + ) + ) + + # Create markdown transform for decrypted blobs (up to 1000) + if decrypted_blobs: + report_lines = [] + report_lines.append(f"# Decrypted DPAPI Blobs: {file_enriched.file_name}") + report_lines.append(f"\nTotal decrypted blobs: {len(decrypted_blobs)}") + + blobs_for_markdown = decrypted_blobs[:1000] + + for idx, blob in enumerate(blobs_for_markdown, 1): + report_lines.append(f"\n## Blob {idx}") + report_lines.append(f"- **Masterkey GUID**: `{blob['dpapi_master_key_guid']}`") + report_lines.append(f"- **Offset**: {blob.get('blob_offset', 0)}") + report_lines.append(f"- **Length**: {blob.get('blob_length', 0)} bytes") + + if blob.get("decrypted_data"): + if len(blob["decrypted_data"]) <= 1000: + report_lines.append("```") + report_lines.append(self._format_hex_dump(blob["decrypted_data"])) + report_lines.append("```") + else: + report_lines.append("\n*Blob is > 1000 bytes*") + + # Add truncation notice if needed + if len(decrypted_blobs) > 1000: + report_lines.append("\n---") + report_lines.append("\n**Note**: Over 1000 blobs carved, output truncated") + + with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8") as tmp_md: + tmp_md.write("\n".join(report_lines)) + tmp_md.flush() + md_object_id = self.storage.upload_file(tmp_md.name) + + transforms.append( + Transform( + type="dpapi_decrypted_blobs", + object_id=f"{md_object_id}", + metadata={ + "file_name": f"{file_enriched.file_name}_decrypted_blobs.md", + "display_type_in_dashboard": "markdown", + "default_display": True, + }, + ) + ) + + enrichment_result.transforms = transforms + + summary_markdown = f""" +# DPAPI Blobs Found : {len(carved_blobs)} +# Masterkey GUIDs +List of unique masterkey GUIDs associated with the found blobs: +```text +{"\n".join(masterkey_guids)} +``` +""" + # Clean up decrypted_data before storing in results to avoid serialization issues + results_blobs = [] + for blob in carved_blobs: + blob_copy = blob.copy() + if "decrypted_data" in blob_copy: + del blob_copy["decrypted_data"] + results_blobs.append(blob_copy) + + enrichment_result.results = {"blobs": results_blobs} + + display_data = FileObject(type="finding_summary", metadata={"summary": summary_markdown}) + + finding = Finding( + category=FindingCategory.EXTRACTED_DATA, + finding_name="dpapi_data", + origin_type=FindingOrigin.ENRICHMENT_MODULE, + origin_name=self.name, + object_id=file_enriched.object_id, + severity=5, + raw_data=enrichment_result.results, + data=[display_data], + ) + + enrichment_result.findings = [finding] + + return enrichment_result + + except Exception as e: + logger.exception(e, message="Error in DPAPI process()") + + +def create_enrichment_module(standalone: bool = False) -> EnrichmentModule: + """Factory function that creates the analyzer in either standalone or service mode.""" + return DpapiBlobAnalyzer(standalone=standalone) diff --git a/libs/file_enrichment_modules/file_enrichment_modules/dpapi/dpapi_helpers.py b/libs/file_enrichment_modules/file_enrichment_modules/dpapi_blob/dpapi_helpers.py similarity index 59% rename from libs/file_enrichment_modules/file_enrichment_modules/dpapi/dpapi_helpers.py rename to libs/file_enrichment_modules/file_enrichment_modules/dpapi_blob/dpapi_helpers.py index 276f8d5..8b3f123 100644 --- a/libs/file_enrichment_modules/file_enrichment_modules/dpapi/dpapi_helpers.py +++ b/libs/file_enrichment_modules/file_enrichment_modules/dpapi_blob/dpapi_helpers.py @@ -1,18 +1,19 @@ import base64 -from typing import Optional -import structlog +from common.logger import get_logger from impacket.dpapi import DPAPI_BLOB from impacket.uuid import bin_to_string from pydantic import BaseModel -logger = structlog.get_logger(module=__name__) +logger = get_logger(__name__) class ParsedDpapiBlob(BaseModel): - dpapi_master_key_guid: Optional[str] = str - dpapi_data_b64: Optional[str] = None + dpapi_master_key_guid: str | None = "" + dpapi_data_b64: str | None = None + dpapi_blob_raw: bytes | None = None success: bool = False # true/false if parsing was successful or not + blob_offset: int = 0 # offset in the file where the blob was found async def parse_dpapi_blob(blob_bytes: bytes) -> ParsedDpapiBlob: @@ -20,28 +21,33 @@ async def parse_dpapi_blob(blob_bytes: bytes) -> ParsedDpapiBlob: parsed_blob = ParsedDpapiBlob() - try: - # it's a bit tricky to carve _just_ the DPAPI blob, but this is how: - blob = DPAPI_BLOB(blob_bytes) - if blob.rawData is not None: - blob.rawData = blob.rawData[: len(blob.getData())] - parsed_blob.dpapi_master_key_guid = bin_to_string(blob["GuidMasterKey"]).lower() - parsed_blob.dpapi_data_b64 = base64.b64encode(blob.rawData).decode("utf-8") - parsed_blob.success = True - except Exception as e: - await logger.awarning(f"Error in parse_dpapi_blob: {e}") + # it's a bit tricky to carve _just_ the DPAPI blob, but this is how: + blob = DPAPI_BLOB(blob_bytes) + if blob.rawData is not None: + blob.rawData = blob.rawData[: len(blob.getData())] + parsed_blob.dpapi_master_key_guid = bin_to_string(blob["GuidMasterKey"]).lower() + parsed_blob.dpapi_data_b64 = base64.b64encode(blob.rawData).decode("utf-8") + parsed_blob.dpapi_blob_raw = blob.rawData + parsed_blob.success = True return parsed_blob async def carve_dpapi_blobs_from_bytes( - raw_bytes: bytes, file_name: str = "", object_id: str = "" + raw_bytes: bytes, file_name: str = "", object_id: str = "", base_offset: int = 0 ) -> list[ParsedDpapiBlob]: """ Helper that _just_ carves raw DPAPI blobs from bytes, returning a list of dicts {dpapi_master_key_guid, dpapi_data_b64} + + Args: + raw_bytes: The bytes to search for DPAPI blobs + file_name: Optional file name for logging + object_id: Optional object ID for logging + base_offset: The base offset in the original file (for chunked reading) """ - dpapi_blobs = list() + dpapi_blobs = [] + seen_blobs = set() # Track unique blobs by their base64 data dpapi_signature = b"\x01\x00\x00\x00\xd0\x8c\x9d\xdf\x01\x15\xd1\x11\x8c\x7a\x00\xc0\x4f\xc2\x97\xeb" # The following are potential base64 representations of the DPAPI provider GUID @@ -57,20 +63,25 @@ async def carve_dpapi_blobs_from_bytes( blob = await parse_dpapi_blob(raw_bytes[current_pos:]) if not blob.success: if file_name != "" and object_id != "": - await logger.awarning( - "carve_dpapi_blobs_from_bytes: blob.rawData is None", file_name=file_name, object_id=object_id + logger.warning( + "carve_dpapi_blobs_from_bytes: blob.rawData is None", + file_name=file_name, + object_id=object_id, ) else: - await logger.awarning("carve_dpapi_blobs_from_bytes: blob.rawData is None") + logger.warning("carve_dpapi_blobs_from_bytes: blob.rawData is None") current_pos += 1 elif blob.dpapi_data_b64: + blob.blob_offset = base_offset + current_pos current_pos += len(base64.b64decode(blob.dpapi_data_b64)) - dpapi_blobs.append(blob) + if blob.dpapi_data_b64 not in seen_blobs: + seen_blobs.add(blob.dpapi_data_b64) + dpapi_blobs.append(blob) except Exception as e: if file_name != "": - await logger.awarning(f"exception parsing file {file_name} for dpapi blobs: {e}") + logger.warning(f"exception parsing file {file_name} for dpapi blobs: {e}") else: - await logger.awarning(f"exception parsing bytes for dpapi blobs: {e}") + logger.warning(f"exception parsing bytes for dpapi blobs: {e}") return dpapi_blobs loc = raw_bytes.find(dpapi_signature, current_pos) @@ -88,18 +99,23 @@ async def carve_dpapi_blobs_from_bytes( try: dpapi_blob_raw = base64.b64decode(raw_bytes[loc:end_loc]) blob = await parse_dpapi_blob(dpapi_blob_raw) + blob.blob_offset = base_offset + loc current_pos += end_loc - loc if not blob.success: - await logger.awarning( - "carve_dpapi_blobs: blob.rawData is None", file_name=file_name, object_id=object_id + logger.warning( + "carve_dpapi_blobs: blob.rawData is None", + file_name=file_name, + object_id=object_id, ) elif blob.dpapi_data_b64: - dpapi_blobs.append(blob) + if blob.dpapi_data_b64 not in seen_blobs: + seen_blobs.add(blob.dpapi_data_b64) + dpapi_blobs.append(blob) except Exception as e: if file_name != "": - await logger.awarning(f"exception parsing file {file_name} for b64dpapi blobs: {e}") + logger.warning(f"exception parsing file {file_name} for b64dpapi blobs: {e}") else: - await logger.awarning(f"exception parsing bytes for dpapi blobs: {e}") + logger.warning(f"exception parsing bytes for b64dpapi blobs: {e}") return dpapi_blobs loc = raw_bytes.find(dpapi_b64_signature, current_pos) @@ -112,13 +128,15 @@ async def carve_dpapi_blobs_from_file(file_name: str, object_id: str = "", max_b returning a list of dicts {dpapi_master_key_guid, dpapi_data_b64} """ - dpapi_blobs = list() + dpapi_blobs = [] chunk_size = 512000 + current_offset = 0 with open(file_name, "rb") as f: # chunking to handle large files while chunk := f.read(chunk_size): - blobs = await carve_dpapi_blobs_from_bytes(chunk, file_name, object_id) + blobs = await carve_dpapi_blobs_from_bytes(chunk, file_name, object_id, base_offset=current_offset) dpapi_blobs += [blob.model_dump() for blob in blobs[:max_blobs]] + current_offset += len(chunk) return dpapi_blobs diff --git a/libs/file_enrichment_modules/file_enrichment_modules/dpapi_masterkey/analyzer.py b/libs/file_enrichment_modules/file_enrichment_modules/dpapi_masterkey/analyzer.py new file mode 100644 index 0000000..dc4e50d --- /dev/null +++ b/libs/file_enrichment_modules/file_enrichment_modules/dpapi_masterkey/analyzer.py @@ -0,0 +1,260 @@ +# enrichment_modules/dpapi_masterkey/analyzer.py +import posixpath +import re +from typing import TYPE_CHECKING + +import psycopg +from common.db import get_postgres_connection_str +from common.helpers import get_drive_from_path +from common.logger import get_logger +from common.models import EnrichmentResult +from common.state_helpers import get_file_enriched, get_file_enriched_async +from common.storage import StorageMinio +from file_enrichment_modules.module_loader import EnrichmentModule +from file_linking.helpers import add_file_linking +from nemesis_dpapi import DpapiManager, MasterKey, MasterKeyFile, MasterKeyType +from psycopg.rows import dict_row + +if TYPE_CHECKING: + import asyncio + + from nemesis_dpapi import DpapiManager + + +logger = get_logger(__name__) + + +class DPAPIMasterkeyAnalyzer(EnrichmentModule): + def __init__(self, standalone: bool = False): + super().__init__("dpapi_masterkey") + self.storage = StorageMinio() + self.dpapi_manager: DpapiManager = None # type: ignore + self.loop: asyncio.AbstractEventLoop = None # type: ignore + + # the workflows this module should automatically run in + self.workflows = ["default"] + + # GUID regex pattern + self.guid_pattern = re.compile( + r"^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$", + re.IGNORECASE, + ) + self._conninfo = get_postgres_connection_str() + + def should_process(self, object_id: str, file_path: str | None = None) -> bool: + """Check if this file should be processed as a DPAPI masterkey file. + + Args: + object_id: The object ID of the file + file_path: Optional path to already downloaded file + """ + file_enriched = get_file_enriched(object_id) + + # Check file size - masterkey files are typically small (usually less than 2KB) + if file_enriched.size > 2048: + return False + + if file_enriched.is_plaintext: + return False + + # Check if filename matches GUID pattern + file_name_lower = file_enriched.file_name.lower() if file_enriched.file_name else "" + return self.guid_pattern.match(file_name_lower) is not None + + def _find_existing_hive(self, file_enriched, target_hive_path: str) -> str | None: + """Find an existing hive by path.""" + try: + with psycopg.connect(self._conninfo, row_factory=dict_row) as conn: + with conn.cursor() as cur: + # Look for existing hive by path + cur.execute( + """ + SELECT object_id + FROM files_enriched + WHERE source = %s + AND LOWER(path) = LOWER(%s) + ORDER BY timestamp DESC + LIMIT 1 + """, + (file_enriched.source, target_hive_path), + ) + + result = cur.fetchone() + if result: + return str(result["object_id"]) # Convert UUID to string + + # Fallback query: look for registry files by magic_type and enrichment results + # Extract the hive type from the target path (e.g., SECURITY from .../Windows/System32/Config/SECURITY) + target_hive_type = posixpath.basename(target_hive_path).upper() + + cur.execute( + """ + SELECT fe.object_id + FROM files_enriched fe + JOIN enrichments e ON fe.object_id = e.object_id + WHERE fe.source = %s + AND fe.magic_type = 'MS Windows registry file, NT/2000 or above' + AND e.module_name = 'registry_hive' + AND e.result_data->'results'->'hive_type' = %s + ORDER BY fe.timestamp DESC + LIMIT 1 + """, + (file_enriched.source, f'"{target_hive_type}"'), + ) + + result = cur.fetchone() + if result: + return str(result["object_id"]) # Convert UUID to string + + except Exception as e: + logger.error(f"Failed to find existing hive {target_hive_path}: {e}") + + return None + + def _get_existing_hive_path(self, file_enriched, standard_path: str) -> str: + """Get the actual path of an existing hive, or return the standard path if not found.""" + # First try to find an existing hive + object_id = self._find_existing_hive(file_enriched, standard_path) + + if object_id: + # Found an existing hive, get its actual path from the database + try: + with psycopg.connect(self._conninfo, row_factory=dict_row) as conn: + with conn.cursor() as cur: + cur.execute( + """ + SELECT path + FROM files_enriched + WHERE object_id = %s + LIMIT 1 + """, + (object_id,), + ) + + result = cur.fetchone() + if result and result["path"]: + logger.debug(f"Found existing hive at {result['path']} instead of {standard_path}") + return result["path"] + except Exception as e: + logger.error(f"Failed to get path for existing hive {object_id}: {e}") + + # Fall back to standard path if not found or on error + return standard_path + + async def _create_proactive_file_linkings(self, file_enriched): + """Create proactive file linkings for the related registry hives.""" + if not file_enriched.source or not file_enriched.path: + return + + drive = get_drive_from_path(file_enriched.path) or "" + # if not drive: + # logger.warning(f"Could not extract drive from path: {file_enriched.path}") + # return + + try: + # Link to SYSTEM and SECURITY hives, needed to decrypt the SYSTEM masterkeys + system_standard_path = f"{drive}/Windows/System32/Config/SYSTEM" + security_standard_path = f"{drive}/Windows/System32/Config/SECURITY" + + system_path = self._get_existing_hive_path(file_enriched, system_standard_path) + security_path = self._get_existing_hive_path(file_enriched, security_standard_path) + + await add_file_linking( + source=file_enriched.source, + source_file_path=file_enriched.path, + linked_file_path=system_path, + link_type="system_hive", + collection_reason="Needed to decrypt the DPAPI_SYSTEM secret", + ) + + await add_file_linking( + source=file_enriched.source, + source_file_path=file_enriched.path, + linked_file_path=security_path, + link_type="security_hive", + collection_reason="Needed to decrypt the DPAPI_SYSTEM secret", + ) + + except Exception as e: + logger.error(f"Failed to create proactive file linkings: {e}") + + async def process(self, object_id: str, file_path: str | None = None) -> EnrichmentResult | None: + """Process masterkey file and add to DPAPI manager. + + Args: + object_id: The object ID of the file + file_path: Optional path to already downloaded file + + Returns: + EnrichmentResult or None if processing fails + """ + return await self._process_async(object_id, file_path) + + async def _process_async(self, object_id: str, file_path: str | None = None) -> EnrichmentResult | None: + """Process masterkey file and add to DPAPI manager. + + Args: + object_id: The object ID of the file + file_path: Optional path to already downloaded file + """ + + try: + file_enriched = await get_file_enriched_async(object_id) + file_enriched = await get_file_enriched_async(object_id) + enrichment_result = EnrichmentResult(module_name=self.name) + + # Parse the masterkey file + if file_path: + # Use provided file path + masterkey_file = MasterKeyFile.from_file(file_path) + if masterkey_file.policy.value & 2: + await self._create_proactive_file_linkings(file_enriched) + else: + # Download the file and parse it + with self.storage.download(file_enriched.object_id) as temp_file: + masterkey_file = MasterKeyFile.from_file(temp_file.name) + + backup_key = masterkey_file.domain_backup_key + mk = MasterKey( + guid=masterkey_file.masterkey_guid, + encrypted_key_usercred=masterkey_file.master_key, + encrypted_key_backup=backup_key.raw_bytes if backup_key else None, + backup_key_guid=backup_key.guid_key if backup_key else None, + masterkey_type=MasterKeyType.from_path(file_enriched.path), + ) + + # The DPAPI manager handles all decryption automatically + await self.dpapi_manager.upsert_masterkey(mk) + + # Check if it was decrypted + stored_mks = await self.dpapi_manager.get_masterkeys(guid=masterkey_file.masterkey_guid) + was_decrypted = len(stored_mks) > 0 and stored_mks[0].is_decrypted + + if was_decrypted: + logger.info(f"Successfully processed and decrypted masterkey {masterkey_file.masterkey_guid}") + else: + logger.debug( + f"Successfully processed masterkey {masterkey_file.masterkey_guid} (not decrypted - may need additional keys)" + ) + + # Prepare results data + results_data = { + "masterkey_guid": str(masterkey_file.masterkey_guid), + "version": masterkey_file.version, + "policy": masterkey_file.policy.value if masterkey_file.policy else 0, + "has_master_key": masterkey_file.master_key is not None, + "has_local_key": masterkey_file.local_key is not None, + "has_backup_key": masterkey_file.backup_key is not None, + "has_domain_backup_key": masterkey_file.domain_backup_key is not None, + } + + enrichment_result.results = results_data + return enrichment_result + + except Exception as e: + logger.exception(e, message="Error in DPAPI masterkey process()") + + +def create_enrichment_module(standalone: bool = False) -> EnrichmentModule: + """Factory function that creates the analyzer in either standalone or service mode.""" + return DPAPIMasterkeyAnalyzer(standalone=standalone) diff --git a/libs/file_enrichment_modules/file_enrichment_modules/exif_metadata/analyzer.py b/libs/file_enrichment_modules/file_enrichment_modules/exif_metadata/analyzer.py new file mode 100644 index 0000000..d10be8e --- /dev/null +++ b/libs/file_enrichment_modules/file_enrichment_modules/exif_metadata/analyzer.py @@ -0,0 +1,330 @@ +# enrichment_modules/exif_metadata/analyzer.py +import tempfile +import textwrap + +import yaml +from common.logger import get_logger +from common.models import EnrichmentResult, Transform +from common.state_helpers import get_file_enriched +from common.storage import StorageMinio +from file_enrichment_modules.module_loader import EnrichmentModule +from PIL import Image +from PIL.ExifTags import GPSTAGS, TAGS + +logger = get_logger(__name__) + +# Supported file extensions +SUPPORTED_EXTENSIONS = { + ".jpg", + ".jpeg", # JPEG + ".tif", + ".tiff", # TIFF + ".cr2", + ".cr3", # Canon RAW + ".nef", # Nikon RAW + ".arw", # Sony RAW + ".dng", # Adobe Digital Negative +} + + +def convert_gps_to_degrees(value): + """Convert GPS coordinates to decimal degrees.""" + try: + d, m, s = value + return float(d) + float(m) / 60.0 + float(s) / 3600.0 + except (TypeError, ValueError, ZeroDivisionError): + return None + + +def extract_gps_info(exif_data): + """Extract and convert GPS information from EXIF data.""" + gps_info = {} + + if "GPSInfo" not in exif_data: + return gps_info + + gps_data = exif_data["GPSInfo"] + + # Extract GPS coordinates + gps_latitude = gps_data.get("GPSLatitude") + gps_latitude_ref = gps_data.get("GPSLatitudeRef") + gps_longitude = gps_data.get("GPSLongitude") + gps_longitude_ref = gps_data.get("GPSLongitudeRef") + + if gps_latitude and gps_latitude_ref and gps_longitude and gps_longitude_ref: + lat = convert_gps_to_degrees(gps_latitude) + lon = convert_gps_to_degrees(gps_longitude) + + if lat is not None and lon is not None: + # Apply direction references + if gps_latitude_ref == "S": + lat = -lat + if gps_longitude_ref == "W": + lon = -lon + + gps_info["Latitude"] = lat + gps_info["Longitude"] = lon + gps_info["Coordinates"] = f"{lat}, {lon}" + gps_info["Maps URL"] = f"https://www.google.com/maps?q={lat},{lon}" + + # Extract altitude + gps_altitude = gps_data.get("GPSAltitude") + gps_altitude_ref = gps_data.get("GPSAltitudeRef", 0) + if gps_altitude is not None: + altitude = float(gps_altitude) + if gps_altitude_ref == 1: + altitude = -altitude + gps_info["Altitude"] = f"{altitude}m" + + # Extract timestamp + gps_datestamp = gps_data.get("GPSDateStamp") + gps_timestamp = gps_data.get("GPSTimeStamp") + if gps_datestamp and gps_timestamp: + try: + h, m, s = gps_timestamp + gps_info["Timestamp"] = f"{gps_datestamp} {int(h):02d}:{int(m):02d}:{int(s):02d} UTC" + except (TypeError, ValueError): + pass + + return gps_info + + +def convert_exif_value(value): + """Convert EXIF values to JSON-serializable types.""" + # Handle PIL-specific types first + if hasattr(value, "__class__") and "IFDRational" in value.__class__.__name__: + # IFDRational is a fraction type - convert to float + try: + return float(value) + except: + return str(value) + elif isinstance(value, bytes): + try: + return value.decode("utf-8", errors="ignore") + except: + return str(value) + elif isinstance(value, (tuple, list)): + return [convert_exif_value(v) for v in value] + elif isinstance(value, dict): + return {k: convert_exif_value(v) for k, v in value.items()} + elif isinstance(value, (int, float, str, bool, type(None))): + return value + elif hasattr(value, "__dict__"): + return str(value) + return value + + +def extract_exif_data(image): + """Extract EXIF data from PIL Image object.""" + exif_dict = {} + + try: + exif_data = image.getexif() + + if not exif_data: + return exif_dict + + # Extract basic EXIF tags + for tag_id, value in exif_data.items(): + tag_name = TAGS.get(tag_id, tag_id) + exif_dict[tag_name] = convert_exif_value(value) + + # Extract GPS info if present + if "GPSInfo" in exif_dict: + gps_data = {} + gps_raw = exif_data.get_ifd(0x8825) # GPS IFD + + for tag_id, value in gps_raw.items(): + tag_name = GPSTAGS.get(tag_id, tag_id) + gps_data[tag_name] = convert_exif_value(value) + + exif_dict["GPSInfo"] = gps_data + + except Exception as e: + logger.warning(f"Error extracting EXIF data: {e}") + + return exif_dict + + +def format_exif_display(exif_data): + """Format EXIF data for human-readable display.""" + if not exif_data: + return "No EXIF data found in image." + + display_dict = {} + + # Camera Information + camera_info = {} + for key in ["Make", "Model", "Software", "LensModel", "LensMake"]: + if key in exif_data: + camera_info[key] = exif_data[key] + if camera_info: + display_dict["Camera Information"] = camera_info + + # Image Settings + image_settings = {} + for key in [ + "ExposureTime", + "FNumber", + "ISO", + "ISOSpeedRatings", + "FocalLength", + "Flash", + "WhiteBalance", + "ExposureProgram", + "MeteringMode", + "ExposureBiasValue", + ]: + if key in exif_data: + image_settings[key] = exif_data[key] + if image_settings: + display_dict["Image Settings"] = image_settings + + # Date and Time + datetime_info = {} + for key in [ + "DateTime", + "DateTimeOriginal", + "DateTimeDigitized", + "OffsetTime", + "OffsetTimeOriginal", + "OffsetTimeDigitized", + ]: + if key in exif_data: + datetime_info[key] = exif_data[key] + if datetime_info: + display_dict["Date and Time"] = datetime_info + + # GPS Information + gps_info = extract_gps_info(exif_data) + if gps_info: + display_dict["GPS Information"] = gps_info + + # Image Dimensions + image_dims = {} + for key in [ + "ImageWidth", + "ImageLength", + "ExifImageWidth", + "ExifImageHeight", + "Orientation", + "ResolutionUnit", + "XResolution", + "YResolution", + ]: + if key in exif_data: + image_dims[key] = exif_data[key] + if image_dims: + display_dict["Image Dimensions"] = image_dims + + # Other Information + other_info = {} + for key in ["Artist", "Copyright", "ImageDescription", "UserComment"]: + if key in exif_data: + other_info[key] = exif_data[key] + if other_info: + display_dict["Other Information"] = other_info + + # Convert to YAML for nice formatting + yaml_output = yaml.dump(display_dict, indent=3, sort_keys=False, width=132, allow_unicode=True) + return textwrap.indent(yaml_output, " ") + + +class ExifMetadataExtractor(EnrichmentModule): + def __init__(self): + super().__init__("exif_metadata") + self.storage = StorageMinio() + # the workflows this module should automatically run in + self.workflows = ["default"] + + def should_process(self, object_id: str, file_path: str | None = None) -> bool: + """Determine if this module should run.""" + file_enriched = get_file_enriched(object_id) + + # Check if file extension is supported + extension = file_enriched.extension.lower() if file_enriched.extension else "" + if extension not in SUPPORTED_EXTENSIONS: + return False + + # Additional check via magic type for common formats + magic_lower = file_enriched.magic_type.lower() + return any(fmt in magic_lower for fmt in ["jpeg", "tiff", "image"]) + + def _analyze_exif(self, file_path: str, file_enriched) -> EnrichmentResult | None: + """Analyze EXIF metadata and generate enrichment result. + + Args: + file_path: Path to the image file to analyze + file_enriched: File enrichment data + + Returns: + EnrichmentResult or None if analysis fails + """ + enrichment_result = EnrichmentResult(module_name=self.name, dependencies=self.dependencies) + + try: + # Open image and extract EXIF + with Image.open(file_path) as img: + exif_data = extract_exif_data(img) + + if not exif_data: + logger.info(f"No EXIF data found in {file_enriched.file_name}") + return None + + # Store raw EXIF data + enrichment_result.results = exif_data + + # Create human-readable display file + with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8") as tmp_display_file: + display = format_exif_display(exif_data) + tmp_display_file.write(display) + tmp_display_file.flush() + + object_id = self.storage.upload_file(tmp_display_file.name) + + displayable_parsed = Transform( + type="displayable_parsed", + object_id=f"{object_id}", + metadata={ + "file_name": f"{file_enriched.file_name}.exif.txt", + "display_type_in_dashboard": "monaco", + "default_display": True, + }, + ) + + enrichment_result.transforms = [displayable_parsed] + + return enrichment_result + + except Exception as e: + logger.exception(e, message=f"Error analyzing EXIF data for {file_enriched.file_name}") + return None + + def process(self, object_id: str, file_path: str | None = None) -> EnrichmentResult | None: + """Process file. + + Args: + object_id: The object ID of the file + file_path: Optional path to already downloaded file + + Returns: + EnrichmentResult or None if processing fails + """ + try: + # get the current `file_enriched` FileEnriched object from the database backend + file_enriched = get_file_enriched(object_id) + + # Use provided file_path if available, otherwise download + if file_path: + return self._analyze_exif(file_path, file_enriched) + else: + with self.storage.download(file_enriched.object_id) as temp_file: + return self._analyze_exif(temp_file.name, file_enriched) + + except Exception as e: + logger.exception(e, message="Error processing file", file_object_id=object_id) + return None + + +def create_enrichment_module() -> EnrichmentModule: + return ExifMetadataExtractor() diff --git a/libs/file_enrichment_modules/file_enrichment_modules/filename/analyzer.py b/libs/file_enrichment_modules/file_enrichment_modules/filename/analyzer.py index 814f0ef..a5fe2e3 100644 --- a/libs/file_enrichment_modules/file_enrichment_modules/filename/analyzer.py +++ b/libs/file_enrichment_modules/file_enrichment_modules/filename/analyzer.py @@ -1,10 +1,9 @@ -import structlog +from common.logger import get_logger from common.models import EnrichmentResult, FileObject, Finding, FindingCategory, FindingOrigin from common.state_helpers import get_file_enriched - from file_enrichment_modules.module_loader import EnrichmentModule -logger = structlog.get_logger(module=__name__) +logger = get_logger(__name__) class FilenameScanner(EnrichmentModule): @@ -46,18 +45,26 @@ class FilenameScanner(EnrichmentModule): "phpinfo", ] - def should_process(self, object_id: str) -> bool: - """Always returns True as filename scanning should run on all files.""" + def should_process(self, object_id: str, file_path: str | None = None) -> bool: + """Always returns True as filename scanning should run on all files. + + Args: + object_id: The object ID of the file + file_path: Optional path to already downloaded file (not used by filename scanner) + """ return True - def process(self, object_id: str) -> EnrichmentResult | None: - """Process file by checking its filename for sensitive terms.""" + def process(self, object_id: str, file_path: str | None = None) -> EnrichmentResult | None: + """Process file by checking its filename for sensitive terms. + + Args: + object_id: The object ID of the file + file_path: Optional path to already downloaded file (not used by filename scanner) + """ try: # Get the current file_enriched from the database backend file_enriched = get_file_enriched(object_id) - logger.debug(f"scanning filename of object_id: {object_id}, filename: {file_enriched.file_name}") - matches = [] filename_lower = file_enriched.file_name.lower() diff --git a/libs/file_enrichment_modules/file_enrichment_modules/filezilla/analyzer.py b/libs/file_enrichment_modules/file_enrichment_modules/filezilla/analyzer.py index 12bbd6b..42c6946 100644 --- a/libs/file_enrichment_modules/file_enrichment_modules/filezilla/analyzer.py +++ b/libs/file_enrichment_modules/file_enrichment_modules/filezilla/analyzer.py @@ -4,15 +4,14 @@ import tempfile import xml.etree.ElementTree as ET from pathlib import Path -import structlog import yara_x +from common.logger import get_logger from common.models import EnrichmentResult, FileObject, Finding, FindingCategory, FindingOrigin, Transform from common.state_helpers import get_file_enriched from common.storage import StorageMinio - from file_enrichment_modules.module_loader import EnrichmentModule -logger = structlog.get_logger(module=__name__) +logger = get_logger(__name__) class FileZillaParser(EnrichmentModule): @@ -22,6 +21,8 @@ class FileZillaParser(EnrichmentModule): # the workflows this module should automatically run in self.workflows = ["default"] + self.size_limit = 50000000 # only check the first 50 megs, for efficiency + # Yara rule to detect FileZilla configuration files self.yara_rule = yara_x.compile(""" rule Detect_FileZilla_Config { @@ -57,7 +58,7 @@ rule Detect_FileZilla_Config { } """) - def should_process(self, object_id: str) -> bool: + def should_process(self, object_id: str, file_path: str | None = None) -> bool: """Determine if this module should run based on file type.""" file_enriched = get_file_enriched(object_id) @@ -72,11 +73,19 @@ rule Detect_FileZilla_Config { ): return False - # Run Yara check - file_bytes = self.storage.download_bytes(file_enriched.object_id) + # Check using Yara rule as a fallback + if file_path: + # Use provided file path + with open(file_path, "rb") as f: + num_bytes = min(file_enriched.size, self.size_limit) + file_bytes = f.read(num_bytes) + else: + # Fallback to downloading the file itself + num_bytes = file_enriched.size if file_enriched.size < self.size_limit else self.size_limit + file_bytes = self.storage.download_bytes(file_enriched.object_id, length=num_bytes) + should_run = len(self.yara_rule.scan(file_bytes).matching_rules) > 0 - logger.warning(f"FileZillaParser should_run: {should_run}") return should_run def _decode_password(self, password_elem) -> str: @@ -221,101 +230,129 @@ rule Detect_FileZilla_Config { return True return False - def process(self, object_id: str) -> EnrichmentResult | None: - """Process FileZilla configuration file and extract server details.""" + def _analyze_filezilla(self, file_path: str, file_enriched) -> EnrichmentResult | None: + """Analyze FileZilla configuration file and generate enrichment result. + + Args: + file_path: Path to the FileZilla config file to analyze + file_enriched: File enrichment data + + Returns: + EnrichmentResult or None if analysis fails + """ + enrichment_result = EnrichmentResult(module_name=self.name, dependencies=self.dependencies) + + try: + content = Path(file_path).read_text(encoding="utf-8") + + # Parse the XML and extract server configurations + servers = self._parse_filezilla_xml(content) + + if servers: + # Create finding summary + summary_markdown = self._create_finding_summary(servers, file_enriched.file_name) + + # Create display data + display_data = FileObject(type="finding_summary", metadata={"summary": summary_markdown}) + + # Determine if this should be a credential finding or just informational + has_creds = self._has_credentials(servers) + + # Create finding + finding = Finding( + category=FindingCategory.CREDENTIAL if has_creds else FindingCategory.MISC, + finding_name="filezilla_config_detected" if not has_creds else "filezilla_credentials_detected", + origin_type=FindingOrigin.ENRICHMENT_MODULE, + origin_name=self.name, + object_id=file_enriched.object_id, + severity=7 if has_creds else 4, + raw_data={"servers": servers, "file_type": file_enriched.file_name}, + data=[display_data], + ) + + enrichment_result.findings = [finding] + enrichment_result.results = {"servers": servers, "server_count": len(servers)} + + # Create a displayable version of the results + with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8") as tmp_display_file: + display = f"FileZilla Configuration Analysis - {file_enriched.file_name}\n" + display += "=" * (35 + len(file_enriched.file_name)) + "\n\n" + display += f"Total Servers: {len(servers)}\n\n" + + for i, server in enumerate(servers, 1): + display += f"Server {i}: {server['name']}\n" + display += f" Host: {server['host']}:{server['port']}\n" + display += f" Protocol: {server['protocol']}\n" + display += f" Username: {server['username']}\n" + display += f" Password: {server['password']}\n" + display += f" Logon Type: {server['logon_type']}\n" + + # Add account if present + if server.get("account"): + display += f" Account: {server['account']}\n" + + # Add additional details if present + if server["bypass_proxy"]: + display += " Proxy: Bypassed\n" + if server["pasv_mode"] != "MODE_DEFAULT": + display += f" PASV Mode: {server['pasv_mode']}\n" + if server["encoding"] != "Auto": + display += f" Encoding: {server['encoding']}\n" + if server.get("timezone_offset", "0") != "0": + display += f" Timezone: {server['timezone_offset']}\n" + if server.get("max_connections", "0") != "0": + display += f" Max Conns: {server['max_connections']}\n" + if server.get("comments"): + display += f" Comments: {server['comments']}\n" + if server.get("local_dir"): + display += f" Local Dir: {server['local_dir']}\n" + if server.get("remote_dir"): + display += f" Remote Dir: {server['remote_dir']}\n" + if server.get("sync_browsing"): + display += " Sync Browse: Enabled\n" + + display += "\n" + "-" * 50 + "\n\n" + + tmp_display_file.write(display) + tmp_display_file.flush() + + display_object_id = self.storage.upload_file(tmp_display_file.name) + + displayable_parsed = Transform( + type="displayable_parsed", + object_id=f"{display_object_id}", + metadata={ + "file_name": f"{file_enriched.file_name}_analysis.txt", + "display_type_in_dashboard": "monaco", + "default_display": True, + }, + ) + enrichment_result.transforms = [displayable_parsed] + + return enrichment_result + except Exception as e: + logger.exception(e, message=f"Error analyzing FileZilla config for {file_enriched.file_name}") + return None + + def process(self, object_id: str, file_path: str | None = None) -> EnrichmentResult | None: + """Process FileZilla configuration file and extract server details. + + Args: + object_id: The object ID of the file + file_path: Optional path to already downloaded file + + Returns: + EnrichmentResult or None if processing fails + """ try: file_enriched = get_file_enriched(object_id) - enrichment_result = EnrichmentResult(module_name=self.name, dependencies=self.dependencies) - # Download and read the file - with self.storage.download(file_enriched.object_id) as temp_file: - content = Path(temp_file.name).read_text(encoding="utf-8") - - # Parse the XML and extract server configurations - servers = self._parse_filezilla_xml(content) - - if servers: - # Create finding summary - summary_markdown = self._create_finding_summary(servers, file_enriched.file_name) - - # Create display data - display_data = FileObject(type="finding_summary", metadata={"summary": summary_markdown}) - - # Determine if this should be a credential finding or just informational - has_creds = self._has_credentials(servers) - - # Create finding - finding = Finding( - category=FindingCategory.CREDENTIAL if has_creds else FindingCategory.MISC, - finding_name="filezilla_config_detected" if not has_creds else "filezilla_credentials_detected", - origin_type=FindingOrigin.ENRICHMENT_MODULE, - origin_name=self.name, - object_id=file_enriched.object_id, - severity=7 if has_creds else 4, - raw_data={"servers": servers, "file_type": file_enriched.file_name}, - data=[display_data], - ) - - enrichment_result.findings = [finding] - enrichment_result.results = {"servers": servers, "server_count": len(servers)} - - # Create a displayable version of the results - with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8") as tmp_display_file: - display = f"FileZilla Configuration Analysis - {file_enriched.file_name}\n" - display += "=" * (35 + len(file_enriched.file_name)) + "\n\n" - display += f"Total Servers: {len(servers)}\n\n" - - for i, server in enumerate(servers, 1): - display += f"Server {i}: {server['name']}\n" - display += f" Host: {server['host']}:{server['port']}\n" - display += f" Protocol: {server['protocol']}\n" - display += f" Username: {server['username']}\n" - display += f" Password: {server['password']}\n" - display += f" Logon Type: {server['logon_type']}\n" - - # Add account if present - if server.get("account"): - display += f" Account: {server['account']}\n" - - # Add additional details if present - if server["bypass_proxy"]: - display += " Proxy: Bypassed\n" - if server["pasv_mode"] != "MODE_DEFAULT": - display += f" PASV Mode: {server['pasv_mode']}\n" - if server["encoding"] != "Auto": - display += f" Encoding: {server['encoding']}\n" - if server.get("timezone_offset", "0") != "0": - display += f" Timezone: {server['timezone_offset']}\n" - if server.get("max_connections", "0") != "0": - display += f" Max Conns: {server['max_connections']}\n" - if server.get("comments"): - display += f" Comments: {server['comments']}\n" - if server.get("local_dir"): - display += f" Local Dir: {server['local_dir']}\n" - if server.get("remote_dir"): - display += f" Remote Dir: {server['remote_dir']}\n" - if server.get("sync_browsing"): - display += " Sync Browse: Enabled\n" - - display += "\n" + "-" * 50 + "\n\n" - - tmp_display_file.write(display) - tmp_display_file.flush() - - display_object_id = self.storage.upload_file(tmp_display_file.name) - - displayable_parsed = Transform( - type="displayable_parsed", - object_id=f"{display_object_id}", - metadata={ - "file_name": f"{file_enriched.file_name}_analysis.txt", - "display_type_in_dashboard": "monaco", - "default_display": True, - }, - ) - enrichment_result.transforms = [displayable_parsed] - - return enrichment_result + # Use provided file_path if available, otherwise download + if file_path: + return self._analyze_filezilla(file_path, file_enriched) + else: + with self.storage.download(file_enriched.object_id) as temp_file: + return self._analyze_filezilla(temp_file.name, file_enriched) except Exception as e: logger.exception(e, message="Error processing FileZilla configuration file") diff --git a/libs/file_enrichment_modules/file_enrichment_modules/gitcredentials/analyzer.py b/libs/file_enrichment_modules/file_enrichment_modules/gitcredentials/analyzer.py index 0ed74c8..e945a2b 100644 --- a/libs/file_enrichment_modules/file_enrichment_modules/gitcredentials/analyzer.py +++ b/libs/file_enrichment_modules/file_enrichment_modules/gitcredentials/analyzer.py @@ -4,14 +4,13 @@ import tempfile import textwrap from pathlib import Path -import structlog +from common.logger import get_logger from common.models import EnrichmentResult, FileObject, Finding, FindingCategory, FindingOrigin, Transform from common.state_helpers import get_file_enriched from common.storage import StorageMinio - from file_enrichment_modules.module_loader import EnrichmentModule -logger = structlog.get_logger(module=__name__) +logger = get_logger(__name__) # Port of https://github.com/NetSPI/PowerHuntShares/blob/46238ba37dc85f65f2c1d7960f551ea3d80c236a/Scripts/ConfigParsers/parser-gitcredentials.ps1 # Original Author: Scott Sutherland, NetSPI (@_nullbind / nullbind) @@ -25,14 +24,16 @@ class GitCredentialsParser(EnrichmentModule): # the workflows this module should automatically run in self.workflows = ["default"] - def should_process(self, object_id: str) -> bool: + def should_process(self, object_id: str, file_path: str | None = None) -> bool: """Determine if this module should run based on file type.""" + file_enriched = get_file_enriched(object_id) + # Check if file is a Git credentials file should_run = file_enriched.is_plaintext and ( file_enriched.file_name.lower() in [".git-credentials", ".gitcredentials"] ) - logger.debug(f"GitCredentialsParser should_run: {should_run}, file_name: {file_enriched.file_name}") + return should_run def _parse_credentials(self, content: str) -> list[dict]: @@ -73,72 +74,101 @@ class GitCredentialsParser(EnrichmentModule): return summary - def process(self, object_id: str) -> EnrichmentResult | None: - """Process Git credentials file and extract credentials.""" + def _analyze_gitcredentials(self, file_path: str, file_enriched) -> EnrichmentResult | None: + """Analyze Git credentials file and generate enrichment result. + + Args: + file_path: Path to the Git credentials file to analyze + file_enriched: File enrichment data + + Returns: + EnrichmentResult or None if analysis fails + """ + enrichment_result = EnrichmentResult(module_name=self.name, dependencies=self.dependencies) + + try: + content = Path(file_path).read_text(encoding="utf-8") + + # Parse the credentials + credentials = self._parse_credentials(content) + + if credentials: + # Create finding summary + summary_markdown = self._create_finding_summary(credentials) + + # Create display data + display_data = FileObject(type="finding_summary", metadata={"summary": summary_markdown}) + + # Create finding + finding = Finding( + category=FindingCategory.CREDENTIAL, + finding_name="git_credentials_detected", + origin_type=FindingOrigin.ENRICHMENT_MODULE, + origin_name=self.name, + object_id=file_enriched.object_id, + severity=7, + raw_data={"credentials": credentials}, + data=[display_data], + ) + + # Add finding to enrichment result + enrichment_result.findings = [finding] + enrichment_result.results = {"credentials": credentials} + + # Create a displayable version of the results + with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8") as tmp_display_file: + yaml_output = [] + yaml_output.append("Git Credentials Analysis") + yaml_output.append("========================\n") + + for i, cred in enumerate(credentials, 1): + yaml_output.append(f"Credential Set {i}:") + for key, value in cred.items(): + yaml_output.append(f" {key}: {value}") + yaml_output.append("") # Add empty line between sets + + display = textwrap.indent("\n".join(yaml_output), " ") + tmp_display_file.write(display) + tmp_display_file.flush() + + object_id = self.storage.upload_file(tmp_display_file.name) + + displayable_parsed = Transform( + type="displayable_parsed", + object_id=f"{object_id}", + metadata={ + "file_name": f"{file_enriched.file_name}_analysis.txt", + "display_type_in_dashboard": "monaco", + "default_display": True, + }, + ) + enrichment_result.transforms = [displayable_parsed] + + return enrichment_result + + except Exception as e: + logger.exception(e, message=f"Error analyzing Git credentials for {file_enriched.file_name}") + return None + + def process(self, object_id: str, file_path: str | None = None) -> EnrichmentResult | None: + """Process Git credentials file and extract credentials. + + Args: + object_id: The object ID of the file + file_path: Optional path to already downloaded file + + Returns: + EnrichmentResult or None if processing fails + """ try: file_enriched = get_file_enriched(object_id) - enrichment_result = EnrichmentResult(module_name=self.name, dependencies=self.dependencies) - # Download and read the file - with self.storage.download(file_enriched.object_id) as temp_file: - content = Path(temp_file.name).read_text(encoding="utf-8") - - # Parse the credentials - credentials = self._parse_credentials(content) - - if credentials: - # Create finding summary - summary_markdown = self._create_finding_summary(credentials) - - # Create display data - display_data = FileObject(type="finding_summary", metadata={"summary": summary_markdown}) - - # Create finding - finding = Finding( - category=FindingCategory.CREDENTIAL, - finding_name="git_credentials_detected", - origin_type=FindingOrigin.ENRICHMENT_MODULE, - origin_name=self.name, - object_id=file_enriched.object_id, - severity=7, - raw_data={"credentials": credentials}, - data=[display_data], - ) - - # Add finding to enrichment result - enrichment_result.findings = [finding] - enrichment_result.results = {"credentials": credentials} - - # Create a displayable version of the results - with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8") as tmp_display_file: - yaml_output = [] - yaml_output.append("Git Credentials Analysis") - yaml_output.append("========================\n") - - for i, cred in enumerate(credentials, 1): - yaml_output.append(f"Credential Set {i}:") - for key, value in cred.items(): - yaml_output.append(f" {key}: {value}") - yaml_output.append("") # Add empty line between sets - - display = textwrap.indent("\n".join(yaml_output), " ") - tmp_display_file.write(display) - tmp_display_file.flush() - - object_id = self.storage.upload_file(tmp_display_file.name) - - displayable_parsed = Transform( - type="displayable_parsed", - object_id=f"{object_id}", - metadata={ - "file_name": f"{file_enriched.file_name}_analysis.txt", - "display_type_in_dashboard": "monaco", - "default_display": True, - }, - ) - enrichment_result.transforms = [displayable_parsed] - - return enrichment_result + # Use provided file_path if available, otherwise download + if file_path: + return self._analyze_gitcredentials(file_path, file_enriched) + else: + with self.storage.download(file_enriched.object_id) as temp_file: + return self._analyze_gitcredentials(temp_file.name, file_enriched) except Exception as e: logger.exception(e, message="Error processing Git credentials file") diff --git a/libs/file_enrichment_modules/file_enrichment_modules/kdbx/analyzer.py b/libs/file_enrichment_modules/file_enrichment_modules/kdbx/analyzer.py index ad438fe..97291e1 100644 --- a/libs/file_enrichment_modules/file_enrichment_modules/kdbx/analyzer.py +++ b/libs/file_enrichment_modules/file_enrichment_modules/kdbx/analyzer.py @@ -4,15 +4,14 @@ import tempfile import uuid from typing import Any -import structlog +from common.logger import get_logger from common.models import EnrichmentResult, FileObject, Finding, FindingCategory, FindingOrigin, Transform from common.state_helpers import get_file_enriched from common.storage import StorageMinio - from file_enrichment_modules.kdbx.keepass2john import process_database from file_enrichment_modules.module_loader import EnrichmentModule -logger = structlog.get_logger(module=__name__) +logger = get_logger(__name__) def get_encryption_algorithm_name(uuid_str: str) -> str: @@ -78,9 +77,22 @@ def parse_kdbx_file(file_path: str) -> dict[str, Any]: # Parse header fields header_data = {} while True: - field_id = struct.unpack(" dict[str, Any]: # Read version if len(data) < 2: return result + version = struct.unpack(" bool: + def should_process(self, object_id: str, file_path: str | None = None) -> bool: # Get the current file_enriched from the database backend file_enriched = get_file_enriched(object_id) if file_enriched.magic_type: - should_run = "keepass" in file_enriched.magic_type.lower() and "kdbx" in file_enriched.magic_type.lower() + return "keepass" in file_enriched.magic_type.lower() and "kdbx" in file_enriched.magic_type.lower() else: - should_run = False + return False - logger.debug(f"KDBXAnalyzer should_run: {should_run}") - return should_run + def _analyze_kdbx(self, file_path: str, file_enriched) -> EnrichmentResult | None: + """Analyze KDBX file and generate enrichment result. - def process(self, object_id: str) -> EnrichmentResult | None: - # get the current `file_enriched` from the database backend - file_enriched = get_file_enriched(object_id) + Args: + file_path: Path to the KDBX file to analyze + file_enriched: File enrichment data - with self.storage.download(file_enriched.object_id) as file: - analysis = parse_kdbx_file(file.name) + Returns: + EnrichmentResult or None if analysis fails + """ - enrichment_result = EnrichmentResult(module_name=self.name) - enrichment_result.results = analysis + analysis = parse_kdbx_file(file_path) - if "encryption_hash" in enrichment_result.results and enrichment_result.results["encryption_hash"]: - encryption_hash = enrichment_result.results["encryption_hash"] + enrichment_result = EnrichmentResult(module_name=self.name) + enrichment_result.results = analysis - # Create summary with additional metadata - summary_parts = ["# Encrypted KeePass Database\n"] - summary_parts.append("The database is encrypted. Attempt to crack it using the following hash:\n") - summary_parts.append(f"```\n{encryption_hash}\n```\n") + if "encryption_hash" in enrichment_result.results and enrichment_result.results["encryption_hash"]: + encryption_hash = enrichment_result.results["encryption_hash"] - # Add metadata if available - if analysis.get("format_version"): - summary_parts.append( - f"**Format Version:** {analysis['major_version']}.{analysis['minor_version']} \n" - ) - if analysis.get("encryption_algorithm"): - summary_parts.append(f"**Encryption Algorithm:** {analysis['encryption_algorithm']} \n") - if analysis.get("kdf_algorithm"): - summary_parts.append(f"**KDF Algorithm:** {analysis['kdf_algorithm']} \n") - if analysis.get("kdf_rounds"): - summary_parts.append(f"**KDF Rounds:** {analysis['kdf_rounds']:,} \n") - if analysis.get("kdf_memory"): - summary_parts.append(f"**KDF Memory:** {analysis['kdf_memory']:,} bytes \n") - if analysis.get("compression_algorithm"): - summary_parts.append(f"**Compression:** {analysis['compression_algorithm']} \n") + # Create summary with additional metadata + summary_parts = ["# Encrypted KeePass Database\n"] + summary_parts.append("The database is encrypted. Attempt to crack it using the following hash:\n") + summary_parts.append(f"```\n{encryption_hash}\n```\n") - summary_markdown = "".join(summary_parts) + # Add metadata if available + if analysis.get("format_version"): + summary_parts.append(f"**Format Version:** {analysis['major_version']}.{analysis['minor_version']} \n") + if analysis.get("encryption_algorithm"): + summary_parts.append(f"**Encryption Algorithm:** {analysis['encryption_algorithm']} \n") + if analysis.get("kdf_algorithm"): + summary_parts.append(f"**KDF Algorithm:** {analysis['kdf_algorithm']} \n") + if analysis.get("kdf_rounds"): + summary_parts.append(f"**KDF Rounds:** {analysis['kdf_rounds']:,} \n") + if analysis.get("kdf_memory"): + summary_parts.append(f"**KDF Memory:** {analysis['kdf_memory']:,} bytes \n") + if analysis.get("compression_algorithm"): + summary_parts.append(f"**Compression:** {analysis['compression_algorithm']} \n") - display_data = FileObject(type="finding_summary", metadata={"summary": summary_markdown}) + summary_markdown = "".join(summary_parts) - finding = Finding( - category=FindingCategory.EXTRACTED_HASH, - finding_name="encrypted_kdbx", - origin_type=FindingOrigin.ENRICHMENT_MODULE, - origin_name=self.name, - object_id=file_enriched.object_id, - severity=5, - raw_data={"encryption_hash": encryption_hash}, - data=[display_data], + display_data = FileObject(type="finding_summary", metadata={"summary": summary_markdown}) + + finding = Finding( + category=FindingCategory.EXTRACTED_HASH, + finding_name="encrypted_kdbx", + origin_type=FindingOrigin.ENRICHMENT_MODULE, + origin_name=self.name, + object_id=file_enriched.object_id, + severity=5, + raw_data={"encryption_hash": encryption_hash}, + data=[display_data], + ) + + with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8") as tmp_display_file: + tmp_display_file.write(summary_markdown) + tmp_display_file.flush() + + object_id = self.storage.upload_file(tmp_display_file.name) + + displayable_parsed = Transform( + type="displayable_parsed", + object_id=f"{object_id}", + metadata={ + "file_name": f"{file_enriched.file_name}.md", + "display_type_in_dashboard": "markdown", + "default_display": True, + }, ) - with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8") as tmp_display_file: - tmp_display_file.write(summary_markdown) - tmp_display_file.flush() + enrichment_result.transforms = [displayable_parsed] + enrichment_result.findings = [finding] - object_id = self.storage.upload_file(tmp_display_file.name) + return enrichment_result - displayable_parsed = Transform( - type="displayable_parsed", - object_id=f"{object_id}", - metadata={ - "file_name": f"{file_enriched.file_name}.md", - "display_type_in_dashboard": "markdown", - "default_display": True, - }, - ) + def process(self, object_id: str, file_path: str | None = None) -> EnrichmentResult | None: + """Process KDBX file and extract encryption information. - enrichment_result.transforms = [displayable_parsed] - enrichment_result.findings = [finding] + Args: + object_id: The object ID of the file + file_path: Optional path to already downloaded file - return enrichment_result + Returns: + EnrichmentResult or None if processing fails + """ + try: + # get the current `file_enriched` from the database backend + file_enriched = get_file_enriched(object_id) + + # Use provided file_path if available, otherwise download + if file_path: + return self._analyze_kdbx(file_path, file_enriched) + else: + with self.storage.download(file_enriched.object_id) as file: + return self._analyze_kdbx(file.name, file_enriched) + + except Exception as e: + logger.exception(e, message="Error processing KDBX file") + return None def create_enrichment_module() -> EnrichmentModule: diff --git a/libs/file_enrichment_modules/file_enrichment_modules/kdbx/keepass2john.py b/libs/file_enrichment_modules/file_enrichment_modules/kdbx/keepass2john.py index 3e71c97..5df8b4a 100644 --- a/libs/file_enrichment_modules/file_enrichment_modules/kdbx/keepass2john.py +++ b/libs/file_enrichment_modules/file_enrichment_modules/kdbx/keepass2john.py @@ -7,12 +7,12 @@ Original C version by Dhiru Kholia and contributors (https://github.com/openwall GPL license """ -import sys -import os -import struct -import hashlib import argparse import base64 +import hashlib +import os +import struct +import sys import xml.etree.ElementTree as ET from pathlib import Path @@ -29,6 +29,7 @@ FILE_VERSION_32 = 0x00040001 FILE_VERSION_32_4 = 0x00040000 FILE_VERSION_32_4_1 = 0x00040001 + # Header field IDs for KDBX 3/4 class HeaderFieldID: END_OF_HEADER = 0 @@ -45,6 +46,7 @@ class HeaderFieldID: KDF_PARAMETERS = 11 PUBLIC_CUSTOM_DATA = 12 + # Inner header field IDs for KDBX 4 class InnerHeaderFieldID: END_OF_HEADER = 0 @@ -52,50 +54,58 @@ class InnerHeaderFieldID: INNER_RANDOM_STREAM_KEY = 2 BINARY = 3 + # Cipher UUIDs -CIPHER_AES = b'\x31\xc1\xf2\xe6\xbf\x71\x43\x50\xbe\x58\x05\x21\x6a\xfc\x5a\xff' -CIPHER_TWOFISH = b'\xad\x68\xf2\x9f\x57\x6f\x4b\xb9\xa3\x6a\xd4\x7a\xf9\x65\x34\x6c' -CIPHER_CHACHA20 = b'\xd6\x03\x8a\x2b\x8b\x6f\x4c\xb5\xa5\x24\x33\x9a\x31\xdb\xb5\x9a' +CIPHER_AES = b"\x31\xc1\xf2\xe6\xbf\x71\x43\x50\xbe\x58\x05\x21\x6a\xfc\x5a\xff" +CIPHER_TWOFISH = b"\xad\x68\xf2\x9f\x57\x6f\x4b\xb9\xa3\x6a\xd4\x7a\xf9\x65\x34\x6c" +CIPHER_CHACHA20 = b"\xd6\x03\x8a\x2b\x8b\x6f\x4c\xb5\xa5\x24\x33\x9a\x31\xdb\xb5\x9a" # KDF UUIDs -KDF_AES = 0xc9d9f39a -KDF_ARGON2D = 0xef636ddf -KDF_ARGON2ID = 0x9e298b19 +KDF_AES = 0xC9D9F39A +KDF_ARGON2D = 0xEF636DDF +KDF_ARGON2ID = 0x9E298B19 DEBUG = False + def read_uint32_le(fp): """Read a 32-bit little-endian unsigned integer.""" data = fp.read(4) if len(data) != 4: raise EOFError("Unexpected end of file") - return struct.unpack('> 8}.{version & 0xff}", file=sys.stderr) + print(f"VariantDictionary version {version >> 8}.{version & 0xFF}", file=sys.stderr) if (version >> 8) != 1: raise ValueError(f"Unsupported VariantDictionary version ({version:04x})") @@ -258,33 +273,33 @@ def parse_variant_dictionary(data): pos += 1 # Read key name length - key_len = struct.unpack(' 0 else b'' + data = fp.read(size) if size > 0 else b"" if field_id == HeaderFieldID.END_OF_HEADER: end_reached = True @@ -360,7 +378,7 @@ def process_database(filename, keyfile=None): transform_seed = data elif field_id == HeaderFieldID.TRANSFORM_ROUNDS: if len(data) >= 4: - transform_rounds = struct.unpack('= 4: - kdf_uuid = struct.unpack('>I', uuid_bytes[:4])[0] # Big-endian for UUID + kdf_uuid = struct.unpack(">I", uuid_bytes[:4])[0] # Big-endian for UUID except Exception as e: if DEBUG: print(f"Error parsing KDF parameters: {e}", file=sys.stderr) @@ -401,8 +419,9 @@ def process_database(filename, keyfile=None): warn(f"{filename}: transformRounds can't be 0") return None - if (version < FILE_VERSION_32_4 and - (not master_seed or not transform_seed or not initialization_vectors or not expected_start_bytes)): + if version < FILE_VERSION_32_4 and ( + not master_seed or not transform_seed or not initialization_vectors or not expected_start_bytes + ): warn(f"{filename}: parsing failed, missing required fields") return None @@ -418,7 +437,7 @@ def process_database(filename, keyfile=None): warn(f"{filename}: error reading encrypted data!") return None - result = f"{dbname}:$keepass$*2*{transform_rounds}*{algorithm}*" + result = f"dbname:$keepass$*2*{transform_rounds}*{algorithm}*" result += bytes_to_hex(master_seed) + "*" result += bytes_to_hex(transform_seed) + "*" result += bytes_to_hex(initialization_vectors) + "*" @@ -448,7 +467,9 @@ def process_database(filename, keyfile=None): warn(f"{filename}: error reading header HMAC!") return None - result = f"{dbname}:$keepass$*{kdbx_ver}*{transform_rounds}*{kdf_uuid:08x}*{argon2_m}*{argon2_v}*{argon2_p}*" + result = ( + f"{dbname}:$keepass$*{kdbx_ver}*{transform_rounds}*{kdf_uuid:08x}*{argon2_m}*{argon2_v}*{argon2_p}*" + ) result += bytes_to_hex(master_seed) + "*" result += bytes_to_hex(transform_seed) + "*" result += bytes_to_hex(header_data) + "*" @@ -466,11 +487,12 @@ def process_database(filename, keyfile=None): warn(f"{filename}: Error processing database: {e}") return None + def main(): """Main entry point.""" - parser = argparse.ArgumentParser(description='Extract hash from KeePass database files for John the Ripper') - parser.add_argument('-k', '--keyfile', help='Path to keyfile') - parser.add_argument('databases', nargs='+', help='KeePass database files (.kdbx)') + parser = argparse.ArgumentParser(description="Extract hash from KeePass database files for John the Ripper") + parser.add_argument("-k", "--keyfile", help="Path to keyfile") + parser.add_argument("databases", nargs="+", help="KeePass database files (.kdbx)") args = parser.parse_args() @@ -481,5 +503,6 @@ def main(): print(process_database(database, args.keyfile)) -if __name__ == '__main__': - main() \ No newline at end of file + +if __name__ == "__main__": + main() diff --git a/libs/file_enrichment_modules/file_enrichment_modules/keytab/analyzer.py b/libs/file_enrichment_modules/file_enrichment_modules/keytab/analyzer.py index eb55e94..16fbdae 100644 --- a/libs/file_enrichment_modules/file_enrichment_modules/keytab/analyzer.py +++ b/libs/file_enrichment_modules/file_enrichment_modules/keytab/analyzer.py @@ -4,81 +4,14 @@ import tempfile from datetime import UTC, datetime from struct import unpack -import structlog import yara_x +from common.logger import get_logger from common.models import EnrichmentResult, FileObject, Finding, FindingCategory, FindingOrigin, Transform from common.state_helpers import get_file_enriched from common.storage import StorageMinio -from impacket.structure import Structure - from file_enrichment_modules.module_loader import EnrichmentModule -logger = structlog.get_logger(module=__name__) - - -# Keytab structure classes -class KeyTab(Structure): - structure = (("file_format_version", "H=517"), ("keytab_entry", ":")) - - def fromString(self, data): - self.entries = [] - Structure.fromString(self, data) - data = self["keytab_entry"] - while len(data) != 0: - ktentry = KeyTabEntry(data) - data = data[len(ktentry.getData()) :] - self.entries.append(ktentry) - - def getData(self): - self["keytab_entry"] = b"".join([entry.getData() for entry in self.entries]) - data = Structure.getData(self) - return data - - -class OctetString(Structure): - structure = (("len", ">H-value"), ("value", ":")) - - -class KeyTabContentRest(Structure): - structure = ( - ("name_type", ">I=1"), - ("timestamp", ">I=0"), - ("vno8", "B=2"), - ("keytype", ">H"), - ("keylen", ">H-key"), - ("key", ":"), - ) - - -class KeyTabContent(Structure): - structure = ( - ("num_components", ">h"), - ("realmlen", ">h-realm"), - ("realm", ":"), - ("components", ":"), - ("restdata", ":"), - ) - - def fromString(self, data): - self.components = [] - Structure.fromString(self, data) - data = self["components"] - for i in range(self["num_components"]): - ktentry = OctetString(data) - data = data[ktentry["len"] + 2 :] - self.components.append(ktentry) - self.restfields = KeyTabContentRest(data) - - def getData(self): - self["num_components"] = len(self.components) - self["components"] = b"".join([component.getData() for component in self.components]) - self["restdata"] = self.restfields.getData() - data = Structure.getData(self) - return data - - -class KeyTabEntry(Structure): - structure = (("size", ">I-content"), ("content", ":", KeyTabContent)) +logger = get_logger(__name__) class KeytabAnalyzer(EnrichmentModule): @@ -89,6 +22,8 @@ class KeytabAnalyzer(EnrichmentModule): # the workflows this module should automatically run in self.workflows = ["default"] + self.size_limit = 50000000 # only check the first 50 megs for DPAPI blobs, for performance + # Key types mapping for readable output self.key_types = { 1: "DES-CBC-CRC", @@ -121,7 +56,7 @@ rule Keytab_File } """) - def should_process(self, object_id: str) -> bool: + def should_process(self, object_id: str, file_path: str | None = None) -> bool: """Determine if this module should run.""" file_enriched = get_file_enriched(object_id) @@ -129,385 +64,361 @@ rule Keytab_File if file_enriched.file_name.lower().endswith(".keytab"): return True - # Check using Yara rule as a fallback - file_bytes = self.storage.download_bytes(file_enriched.object_id) - should_run = len(self.yara_rule.scan(file_bytes).matching_rules) > 0 + if file_path: + # Use provided file path - read only the needed bytes + with open(file_path, "rb") as f: + num_bytes = min(file_enriched.size, self.size_limit) + file_bytes = f.read(num_bytes) + else: + # Fallback to downloading the file itself + num_bytes = file_enriched.size if file_enriched.size < self.size_limit else self.size_limit + file_bytes = self.storage.download_bytes(file_enriched.object_id, length=num_bytes) - logger.debug(f"KeytabAnalyzer should_run: {should_run}") + should_run = len(self.yara_rule.scan(file_bytes).matching_rules) > 0 return should_run - def _parse_keytab(self, file_data): - """Parse a keytab file and extract key information with robust error handling.""" - entries = [] - - # Check for minimum keytab file size - if len(file_data) < 4: - entries.append({"error": "File too small to be a valid keytab"}) - return entries - - # Verify keytab version at the beginning of the file + def _parse_keytab_entry(self, entry_data): + """Parse a single keytab entry.""" try: - version = unpack("H", file_data[0:2])[0] - if version != 0x502: - entries.append({"error": f"Unexpected keytab version: 0x{version:x}"}) - # Continue processing anyway as best effort - except Exception as e: - entries.append({"error": f"Failed to parse keytab version: {str(e)}"}) - - # Try the standard parsing approach first - try: - keytab = KeyTab() - keytab.fromString(file_data) - - for entry in keytab.entries: - try: - content = entry["content"] - - # Extract realm - realm = content["realm"].decode("utf-8", errors="replace") - - # Extract principal components - principal_components = [] - for component in content.components: - principal_components.append(component["value"].decode("utf-8", errors="replace")) - - principal = "/".join(principal_components) - - # Extract key information - key_type = content.restfields["keytype"] - key_type_name = self.key_types.get(key_type, f"Unknown ({key_type})") - key = content.restfields["key"] - key_hex = binascii.hexlify(key).decode("ascii") - - # Extract timestamp if available - timestamp = content.restfields["timestamp"] - if timestamp > 0: - timestamp_dt = datetime.fromtimestamp(timestamp, tz=UTC).isoformat() - else: - timestamp_dt = "N/A" - - # Create entry information - entry_info = { - "realm": realm, - "principal": principal, - "key_type": key_type, - "key_type_name": key_type_name, - "key_length": len(key) * 8, # Length in bits - "key": key_hex, - "timestamp": timestamp_dt, - "kvno": content.restfields["vno8"], - } - - entries.append(entry_info) - - except Exception as e: - logger.error(f"Error parsing keytab entry: {str(e)}") - entries.append({"error": f"Error in entry: {str(e)}"}) - - # If we got here and have entries, return them - if entries: - return entries - - except Exception as e: - logger.error(f"Standard parsing approach failed: {str(e)}") - # Continue to fallback parsing methods - - # Fallback: Manual parsing for common keytab format - try: - # Skip the 2-byte header - data = file_data[2:] offset = 0 - while offset < len(data): - try: - # Each entry starts with its size - if offset + 4 > len(data): - break + # Number of components (2 bytes) + if offset + 2 > len(entry_data): + return None + num_components = unpack(">h", entry_data[offset : offset + 2])[0] + offset += 2 - entry_size = unpack(">I", data[offset : offset + 4])[0] + # Realm length and value + if offset + 2 > len(entry_data): + return None + realm_len = unpack(">h", entry_data[offset : offset + 2])[0] + offset += 2 - # Sanity check on entry size - if entry_size <= 0 or entry_size > len(data) - offset: - offset += 4 # Skip this problematic entry - continue + if offset + realm_len > len(entry_data): + return None + realm = entry_data[offset : offset + realm_len].decode("utf-8", errors="replace") + offset += realm_len - # Get the raw entry data - entry_data = data[offset + 4 : offset + 4 + entry_size] - offset += 4 + entry_size + # Components (principal parts) + components = [] + for _i in range(num_components): + if offset + 2 > len(entry_data): + return None + comp_len = unpack(">h", entry_data[offset : offset + 2])[0] + offset += 2 - # Try to extract key data from the entry - # This is a simplified approach focusing on finding key material - if len(entry_data) >= 20: # Minimum size for a meaningful entry - # Look for key type and key data markers - for i in range(len(entry_data) - 8): - # Check for patterns that might indicate key type and length fields - if i + 8 <= len(entry_data): - try: - possible_key_type = unpack(">H", entry_data[i : i + 2])[0] - possible_key_len = unpack(">H", entry_data[i + 2 : i + 4])[0] + if offset + comp_len > len(entry_data): + return None + component = entry_data[offset : offset + comp_len].decode("utf-8", errors="replace") + components.append(component) + offset += comp_len - # Validate key type and length - if possible_key_type in self.key_types and 8 <= possible_key_len <= 64: - if i + 4 + possible_key_len <= len(entry_data): - key_data = entry_data[i + 4 : i + 4 + possible_key_len] - key_hex = binascii.hexlify(key_data).decode("ascii") + principal = "/".join(components) - entry_info = { - "realm": "Unknown (manual extraction)", - "principal": "Unknown (manual extraction)", - "key_type": possible_key_type, - "key_type_name": self.key_types.get( - possible_key_type, f"Unknown ({possible_key_type})" - ), - "key_length": possible_key_len * 8, - "key": key_hex, - "timestamp": "Unknown (manual extraction)", - "kvno": 0, # Unknown in this fallback method - "note": "Extracted using fallback method - limited metadata available", - } - entries.append(entry_info) - except Exception: - # Continue searching through the entry data - continue - except Exception as e: - logger.error(f"Error in fallback parsing of entry at offset {offset}: {str(e)}") - # Continue to next potential entry + # Name type (4 bytes) + if offset + 4 > len(entry_data): + return None + name_type = unpack(">I", entry_data[offset : offset + 4])[0] + offset += 4 - # If we found some entries using the fallback method - if entries: - return entries + # Timestamp (4 bytes) + if offset + 4 > len(entry_data): + return None + timestamp = unpack(">I", entry_data[offset : offset + 4])[0] + offset += 4 + + # KVNO (1 byte) + if offset + 1 > len(entry_data): + return None + kvno = entry_data[offset] + offset += 1 + + # Key type (2 bytes) + if offset + 2 > len(entry_data): + return None + key_type = unpack(">H", entry_data[offset : offset + 2])[0] + offset += 2 + + # Key length (2 bytes) + if offset + 2 > len(entry_data): + return None + key_length = unpack(">H", entry_data[offset : offset + 2])[0] + offset += 2 + + # Key data + if offset + key_length > len(entry_data): + return None + key_data = entry_data[offset : offset + key_length] + + # Format timestamp + if timestamp > 0: + timestamp_dt = datetime.fromtimestamp(timestamp, tz=UTC).isoformat() + else: + timestamp_dt = "N/A" + + return { + "realm": realm, + "principal": principal, + "key_type": key_type, + "key_type_name": self.key_types.get(key_type, f"Unknown ({key_type})"), + "key_length": key_length * 8, # Convert to bits + "key": binascii.hexlify(key_data).decode("ascii"), + "timestamp": timestamp_dt, + "kvno": kvno, + "name_type": name_type, + } except Exception as e: - logger.error(f"Fallback parsing method failed: {str(e)}") + logger.error(f"Error parsing keytab entry: {e}") + return None - # If we got here with no entries, check for hex patterns that might be keys - if not entries: + def _parse_keytab_manual(self, file_data): + """Manual keytab parsing implementation.""" + entries = [] + + # Check version + if len(file_data) < 2: + return [{"error": "File too small"}] + + version = unpack(">H", file_data[0:2])[0] + if version != 0x0502: + entries.append({"error": f"Unexpected keytab version: 0x{version:x}"}) + return entries + + offset = 2 + + while offset < len(file_data): try: - # Last-resort attempt: look for hex patterns that might be keys - # Common key sizes: RC4 (16 bytes), AES-128 (16 bytes), AES-256 (32 bytes) - key_candidates = [] + # Read entry size + if offset + 4 > len(file_data): + break - # Convert to hex for pattern searching - hex_data = binascii.hexlify(file_data).decode("ascii") + entry_size = unpack(">I", file_data[offset : offset + 4])[0] + offset += 4 - # Look for 32-character (16 bytes) and 64-character (32 bytes) hex sequences - # that might be keys (excluding long sequences of zeros or repeated characters) - for length in [32, 64]: # Hex characters, representing 16 or 32 bytes - for i in range(0, len(hex_data) - length, 2): - segment = hex_data[i : i + length] + if entry_size == 0 or offset + entry_size > len(file_data): + break - # Skip if it's all zeros or a single repeated character - if segment == "0" * length or all(c == segment[0] for c in segment): - continue + entry_data = file_data[offset : offset + entry_size] + offset += entry_size - # Check for sufficient entropy in the potential key - unique_chars = len(set(segment)) - if unique_chars > 10: # Require some entropy - key_candidates.append(segment) + # Parse entry + entry = self._parse_keytab_entry(entry_data) + if entry: + entries.append(entry) - # Add found potential keys - for i, key in enumerate(key_candidates): - entries.append( - { - "realm": "Unknown (hex pattern extraction)", - "principal": f"Potential key {i + 1}", - "key_type": 0, - "key_type_name": "Unknown (hex pattern extraction)", - "key_length": len(key) * 4, # Hex characters × 4 bits - "key": key, - "timestamp": "Unknown (hex pattern extraction)", - "kvno": 0, - "note": "Potential key extracted by hex pattern matching - use with caution", - } - ) except Exception as e: - logger.error(f"Pattern-based extraction failed: {str(e)}") - - # If we still found nothing, add an error entry - if not entries: - entries.append({"error": "Failed to parse keytab file using all available methods"}) + logger.error(f"Error parsing keytab entry at offset {offset}: {e}") + break return entries - def process(self, object_id: str) -> EnrichmentResult | None: - """Process keytab file.""" + def _parse_keytab(self, file_data): + """Parse a keytab file and extract key information.""" + return self._parse_keytab_manual(file_data) + + def _analyze_keytab_file(self, file_path: str, file_enriched) -> EnrichmentResult | None: + """Analyze keytab file and generate enrichment result. + + Args: + file_path: Path to the keytab file to analyze + file_enriched: File enrichment data + + Returns: + EnrichmentResult or None if analysis fails + """ + enrichment_result = EnrichmentResult(module_name=self.name, dependencies=self.dependencies) + transforms = [] + findings = [] + + try: + # Read the keytab file + with open(file_path, "rb") as f: + file_data = f.read() + + # Parse the keytab file + keytab_entries = self._parse_keytab(file_data) + + # Generate summary report + report_lines = [] + report_lines.append("# Keytab Analysis Summary") + report_lines.append(f"\nFile name: {file_enriched.file_name}") + + # Count valid entries vs error entries + valid_entries = [entry for entry in keytab_entries if "error" not in entry] + error_entries = [entry for entry in keytab_entries if "error" in entry] + + report_lines.append(f"Total entries found: {len(keytab_entries)}") + report_lines.append(f"Valid entries: {len(valid_entries)}") + if error_entries: + report_lines.append(f"Entries with errors: {len(error_entries)}") + + # Add extraction method note if present + extraction_methods = set() + for entry in valid_entries: + if "note" in entry and "extracted" in entry["note"].lower(): + extraction_methods.add(entry["note"]) + + if extraction_methods: + report_lines.append("\n## Extraction Notes") + for method in extraction_methods: + report_lines.append(f"- {method}") + + # Entry details + for i, entry in enumerate(keytab_entries, 1): + report_lines.append(f"\n## Entry {i}") + + # Handle error case + if "error" in entry: + report_lines.append(f"\n**ERROR**: {entry['error']}") + continue + + # Add note if present + if "note" in entry: + report_lines.append(f"\n**Note**: {entry['note']}") + + # Basic entry details + report_lines.append(f"\n**Principal**: `{entry['principal']}@{entry['realm']}`") + report_lines.append(f"\n**Key Version Number (KVNO)**: {entry['kvno']}") + report_lines.append(f"\n**Timestamp**: {entry['timestamp']}") + report_lines.append(f"\n**Key Type**: {entry['key_type_name']} ({entry['key_type']})") + report_lines.append(f"\n**Key Length**: {entry['key_length']} bits") + + # Format key with better readability + key_hex = entry["key"] + formatted_key = " ".join([key_hex[i : i + 8] for i in range(0, len(key_hex), 8)]) + report_lines.append(f"\n**Key (Hex)**: \n{formatted_key}") + + # Create summary report transform + with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8") as tmp_report: + tmp_report.write("\n".join(report_lines)) + tmp_report.flush() + report_id = self.storage.upload_file(tmp_report.name) + + transforms.append( + Transform( + type="finding_summary", + object_id=f"{report_id}", + metadata={ + "file_name": f"{file_enriched.file_name}_analysis.md", + "display_type_in_dashboard": "markdown", + "default_display": True, + }, + ) + ) + + # Create finding ONLY if there are valid entries with non-null keys + valid_keys = [entry for entry in valid_entries if entry.get("key") and entry["key"] != ""] + if valid_keys: + finding_data = [] + + # Create a summary of encryption keys found + key_summary = "## Kerberos Encryption Keys Detected\n\n" + + # Add extraction method note if present + if extraction_methods: + key_summary += "**Note**: Some keys were extracted using fallback methods due to parsing issues. " + key_summary += "See the analysis report for details.\n\n" + + key_summary += "The following Kerberos encryption keys were found in the keytab file:\n\n" + + for i, entry in enumerate(valid_keys, 1): + key_summary += f"**Entry {i}**\n" + key_summary += f"- **Principal:** `{entry['principal']}@{entry['realm']}`\n" + key_summary += f"- **Key Type:** {entry['key_type_name']}\n" + key_summary += f"- **Key Length:** {entry['key_length']} bits\n" + key_summary += f"- **KVNO:** {entry['kvno']}\n" + if "note" in entry: + key_summary += f"- **Note:** {entry['note']}\n" + key_summary += "\n" + + # Add the key summary as a finding + display_data = FileObject(type="finding_summary", metadata={"summary": key_summary}) + finding_data.append(display_data) + + finding = Finding( + category=FindingCategory.CREDENTIAL, + finding_name="kerberos_encryption_keys", + origin_type=FindingOrigin.ENRICHMENT_MODULE, + origin_name=self.name, + object_id=file_enriched.object_id, + severity=7, # Higher severity than certificates since these are actual keys + raw_data={"keytab_entries": valid_keys}, + data=finding_data, + ) + + findings.append(finding) + + # Add the results to the enrichment result + enrichment_result.transforms = transforms + enrichment_result.findings = findings + enrichment_result.results = {"keytab_entries": keytab_entries} + + return enrichment_result + + except Exception as e: + logger.exception(e, message=f"Error processing keytab file: {file_enriched.file_name}") + + # Create an error report with more detailed information + error_report = [ + "# Keytab Analysis Error", + f"\nFailed to analyze {file_enriched.file_name}", + "\n## Error Details", + f"\n**Error Message**: {str(e)}", + "\n**Possible Causes**:", + "- The file may not be a valid Kerberos keytab file", + "- The file format may be corrupted or malformed", + "- The file may use an unsupported keytab format variation", + "\n**Troubleshooting**:", + "- Verify the file is a genuine keytab file", + "- Check if the file was created correctly", + "- Try opening the file with ktutil or a similar Kerberos utility", + ] + + with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8") as tmp_error: + tmp_error.write("\n".join(error_report)) + tmp_error.flush() + error_id = self.storage.upload_file(tmp_error.name) + + transforms.append( + Transform( + type="finding_summary", + object_id=f"{error_id}", + metadata={ + "file_name": f"{file_enriched.file_name}_analysis_error.md", + "display_type_in_dashboard": "markdown", + "default_display": True, + }, + ) + ) + + # No finding is created for errors + enrichment_result.transforms = transforms + return enrichment_result + + def process(self, object_id: str, file_path: str | None = None) -> EnrichmentResult | None: + """Process keytab file. + + Args: + object_id: The object ID of the file + file_path: Optional path to already downloaded file + + Returns: + EnrichmentResult or None if processing fails + """ try: file_enriched = get_file_enriched(object_id) - enrichment_result = EnrichmentResult(module_name=self.name, dependencies=self.dependencies) - transforms = [] - findings = [] - with self.storage.download(file_enriched.object_id) as temp_file: - try: - # Read the keytab file - with open(temp_file.name, "rb") as f: - file_data = f.read() - - # Parse the keytab file - keytab_entries = self._parse_keytab(file_data) - - # Generate summary report - report_lines = [] - report_lines.append("# Keytab Analysis Summary") - report_lines.append(f"\nFile name: {file_enriched.file_name}") - - # Count valid entries vs error entries - valid_entries = [entry for entry in keytab_entries if "error" not in entry] - error_entries = [entry for entry in keytab_entries if "error" in entry] - - report_lines.append(f"Total entries found: {len(keytab_entries)}") - report_lines.append(f"Valid entries: {len(valid_entries)}") - if error_entries: - report_lines.append(f"Entries with errors: {len(error_entries)}") - - # Add extraction method note if present - extraction_methods = set() - for entry in valid_entries: - if "note" in entry and "extracted" in entry["note"].lower(): - extraction_methods.add(entry["note"]) - - if extraction_methods: - report_lines.append("\n## Extraction Notes") - for method in extraction_methods: - report_lines.append(f"- {method}") - - # Entry details - for i, entry in enumerate(keytab_entries, 1): - report_lines.append(f"\n## Entry {i}") - - # Handle error case - if "error" in entry: - report_lines.append(f"\n**ERROR**: {entry['error']}") - continue - - # Add note if present - if "note" in entry: - report_lines.append(f"\n**Note**: {entry['note']}") - - # Basic entry details - report_lines.append(f"\n**Principal**: {entry['principal']}@{entry['realm']}") - report_lines.append(f"\n**Key Version Number (KVNO)**: {entry['kvno']}") - report_lines.append(f"\n**Timestamp**: {entry['timestamp']}") - report_lines.append(f"\n**Key Type**: {entry['key_type_name']} ({entry['key_type']})") - report_lines.append(f"\n**Key Length**: {entry['key_length']} bits") - - # Format key with better readability - key_hex = entry["key"] - formatted_key = " ".join([key_hex[i : i + 8] for i in range(0, len(key_hex), 8)]) - report_lines.append(f"\n**Key (Hex)**: \n{formatted_key}") - - # Create summary report transform - with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8") as tmp_report: - tmp_report.write("\n".join(report_lines)) - tmp_report.flush() - report_id = self.storage.upload_file(tmp_report.name) - - transforms.append( - Transform( - type="finding_summary", - object_id=f"{report_id}", - metadata={ - "file_name": f"{file_enriched.file_name}_analysis.md", - "display_type_in_dashboard": "markdown", - "default_display": True, - }, - ) - ) - - # Create finding ONLY if there are valid entries with non-null keys - valid_keys = [entry for entry in valid_entries if entry.get("key") and entry["key"] != ""] - if valid_keys: - finding_data = [] - - # Create a summary of encryption keys found - key_summary = "## Kerberos Encryption Keys Detected\n\n" - - # Add extraction method note if present - if extraction_methods: - key_summary += ( - "**Note**: Some keys were extracted using fallback methods due to parsing issues. " - ) - key_summary += "See the analysis report for details.\n\n" - - key_summary += "The following Kerberos encryption keys were found in the keytab file:\n\n" - - for i, entry in enumerate(valid_keys, 1): - key_summary += f"**Entry {i}**\n" - key_summary += f"- **Principal:** {entry['principal']}@{entry['realm']}\n" - key_summary += f"- **Key Type:** {entry['key_type_name']}\n" - key_summary += f"- **Key Length:** {entry['key_length']} bits\n" - key_summary += f"- **KVNO:** {entry['kvno']}\n" - if "note" in entry: - key_summary += f"- **Note:** {entry['note']}\n" - key_summary += "\n" - - # Add the key summary as a finding - display_data = FileObject(type="finding_summary", metadata={"summary": key_summary}) - finding_data.append(display_data) - - finding = Finding( - category=FindingCategory.CREDENTIAL, - finding_name="kerberos_encryption_keys", - origin_type=FindingOrigin.ENRICHMENT_MODULE, - origin_name=self.name, - object_id=file_enriched.object_id, - severity=7, # Higher severity than certificates since these are actual keys - raw_data={"keytab_entries": valid_keys}, - data=finding_data, - ) - - findings.append(finding) - - # Add the results to the enrichment result - enrichment_result.transforms = transforms - enrichment_result.findings = findings - enrichment_result.results = {"keytab_entries": keytab_entries} - - return enrichment_result - - except Exception as e: - logger.exception(e, message=f"Error processing keytab file: {file_enriched.file_name}") - - # Create an error report with more detailed information - error_report = [ - "# Keytab Analysis Error", - f"\nFailed to analyze {file_enriched.file_name}", - "\n## Error Details", - f"\n**Error Message**: {str(e)}", - "\n**Possible Causes**:", - "- The file may not be a valid Kerberos keytab file", - "- The file format may be corrupted or malformed", - "- The file may use an unsupported keytab format variation", - "\n**Troubleshooting**:", - "- Verify the file is a genuine keytab file", - "- Check if the file was created correctly", - "- Try opening the file with ktutil or a similar Kerberos utility", - ] - - with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8") as tmp_error: - tmp_error.write("\n".join(error_report)) - tmp_error.flush() - error_id = self.storage.upload_file(tmp_error.name) - - transforms.append( - Transform( - type="finding_summary", - object_id=f"{error_id}", - metadata={ - "file_name": f"{file_enriched.file_name}_analysis_error.md", - "display_type_in_dashboard": "markdown", - "default_display": True, - }, - ) - ) - - # No finding is created for errors - - enrichment_result.transforms = transforms - return enrichment_result + # Use provided file_path if available, otherwise download + if file_path: + return self._analyze_keytab_file(file_path, file_enriched) + else: + with self.storage.download(file_enriched.object_id) as temp_file: + return self._analyze_keytab_file(temp_file.name, file_enriched) except Exception as e: logger.exception(e, message="Error in keytab analyzer") + return None def create_enrichment_module() -> EnrichmentModule: diff --git a/libs/file_enrichment_modules/file_enrichment_modules/llm_credential_analysis/analyzer.py b/libs/file_enrichment_modules/file_enrichment_modules/llm_credential_analysis/analyzer.py deleted file mode 100644 index bf478e4..0000000 --- a/libs/file_enrichment_modules/file_enrichment_modules/llm_credential_analysis/analyzer.py +++ /dev/null @@ -1,372 +0,0 @@ -# enrichment_modules/llm_credential_analysis/analyzer.py -import asyncio -import json -import logging -import os -import tempfile -from typing import Optional - -import psycopg -import rigging as rg -import structlog -from common.models import EnrichmentResult, FileObject, Finding, FindingCategory, FindingOrigin, Transform -from common.state_helpers import get_file_enriched -from common.storage import StorageMinio -from dapr.clients import DaprClient - -from file_enrichment_modules.module_loader import EnrichmentModule - -logger = structlog.get_logger(module=__name__) - - -class Credentials(rg.Model): - content: str - - -def credentials_to_markdown(credentials: str) -> str: - """Format extracted credentials as markdown report.""" - if not credentials or credentials.strip().lower() == "none": - return "## LLM Credential Analysis\n\nNo credentials found in this document." - - lines = credentials.strip().split("\n") - markdown = "## LLM Credential Analysis\n\n" - markdown += "### Detected Credentials\n\n" - - for line in lines: - if line.strip(): - markdown += f"- `{line.strip()}`\n" - - return markdown - - -class CredentialExtractor(EnrichmentModule): - def __init__(self): - super().__init__("llm_credential_analysis") - self.storage = StorageMinio() - - logging.getLogger("litellm").setLevel(logging.INFO) # not working how it should... - - # Check if rigging generator config is available - self.rigging_generator = os.getenv("RIGGING_GENERATOR_CREDENTIALS") - if not self.rigging_generator: - logger.info("RIGGING_GENERATOR_CREDENTIALS environment variable not set - credential analysis disabled") - - with DaprClient() as client: - secret = client.get_secret(store_name="nemesis-secret-store", key="POSTGRES_CONNECTION_STRING") - self.postgres_connection_string = secret.secret["POSTGRES_CONNECTION_STRING"] - - def _has_extracted_text_transform(self, object_id: str) -> tuple[bool, Optional[str]]: - """ - Check if file has an extracted_text transform. - Returns a tuple of (has_transform, transform_object_id) - """ - try: - with psycopg.connect(self.postgres_connection_string) as conn: - with conn.cursor() as cur: - cur.execute( - """ - SELECT transform_object_id - FROM transforms - WHERE object_id = %s AND type = 'extracted_text' - LIMIT 1 - """, - (object_id,), - ) - result = cur.fetchone() - if result: - return True, str(result[0]) - return False, None - except Exception as e: - logger.error(f"Error checking for extracted_text transform: {e}") - return False, None - - def should_process(self, object_id: str) -> bool: - """Determine if this module should run.""" - if not self.rigging_generator: - return False - - file_enriched = get_file_enriched(object_id) - print(f"file_enriched: {file_enriched}") - - # Process if file is plaintext or has extracted_text transform - has_transform, _ = self._has_extracted_text_transform(object_id) - print(f"has_transform: {has_transform}") - return file_enriched.is_plaintext or has_transform - - def _get_text_content(self, object_id: str) -> tuple[Optional[str], Optional[str]]: - """ - Get plaintext content from file or extracted text transform. - Returns a tuple of (content, source_object_id) - """ - try: - # Check if file is plaintext first - file_enriched = get_file_enriched(object_id) - - if file_enriched.is_plaintext: - try: - file_bytes = self.storage.download_bytes(object_id) - return file_bytes.decode("utf-8", errors="replace"), object_id - except Exception as e: - logger.warning(f"Failed to decode plaintext file content: {e}") - - # If not plaintext or decode failed, look for extracted_text transform - has_transform, transform_object_id = self._has_extracted_text_transform(object_id) - if has_transform and transform_object_id: - try: - transform_bytes = self.storage.download_bytes(transform_object_id) - return transform_bytes.decode("utf-8", errors="replace"), transform_object_id - except Exception as e: - logger.error(f"Failed to get extracted text transform content: {e}") - - return None, None - - except Exception as e: - logger.error(f"Error getting text content: {e}") - return None, None - - async def _extract_credentials(self, text_content: str) -> str: - """Async function to extract credentials with an LLM using rigging.""" - max_retries = 3 - attempt = 0 - - while attempt < max_retries: - try: - generator = rg.get_generator(self.rigging_generator) - - response = await generator.chat( - [ - { - "role": "system", - "content": "You are a cybersecurity expert extremely proficient at identifying credentials and passwords.", - }, - { - "role": "user", - "content": f"If the following document contains any credentials or passwords, output each credential/password between on a separate line with no other details or explanation, and have the all the lines output between {Credentials.xml_start_tag()}{Credentials.xml_end_tag()} tags. If the document contains no credentials or passwords, output 'none' without the quotes, not wrapped in any tags.\n\nDocument:\n\n{text_content}\n\n", - }, - ] - ).run() - - print(f"last: {response.last}") - - credentials = response.last.try_parse(Credentials) - if credentials: - return credentials.content - else: - return "" - - except Exception as e: - # Try to check if it's error 529 - need to handle different exception types - error_str = str(e).lower() - - # Look for indications of a 529 error in the error message or response - if "529" in error_str or "too many requests" in error_str or "rate limit" in error_str: - attempt += 1 - if attempt < max_retries: - wait_time = 15 - logger.warning( - f"Received what appears to be a rate limit error, waiting {wait_time} seconds before retry (attempt {attempt}/{max_retries})" - ) - await asyncio.sleep(wait_time) - else: - logger.error("Max retries reached after apparent rate limit errors") - raise - else: - # For any other error, log and re-raise immediately - logger.exception(e, message="Error extracting credentials") - - def _get_original_file_id(self, transform_object_id: str) -> Optional[str]: - """ - Find the original file ID for an extracted text transform by checking - which file has this transform_object_id in its transforms. - """ - try: - with psycopg.connect(self.postgres_connection_string) as conn: - with conn.cursor() as cur: - cur.execute( - """ - SELECT object_id - FROM transforms - WHERE transform_object_id = %s AND type = 'extracted_text' - LIMIT 1 - """, - (transform_object_id,), - ) - result = cur.fetchone() - if result: - return str(result[0]) - return None - except Exception as e: - logger.exception(e, message="Error finding original file for transform") - return None - - def process(self, object_id: str) -> EnrichmentResult | None: - """Process text content and extract credentials using LLM.""" - try: - # First, check if this is an extracted text file or a regular file - # If it's an extracted_text transform, we need to find its original file - original_file_id = None - - # Check if current object is already a transform (extracted_text) - with psycopg.connect(self.postgres_connection_string) as conn: - with conn.cursor() as cur: - cur.execute( - """ - SELECT object_id FROM transforms - WHERE transform_object_id = %s AND type = 'extracted_text' - """, - (object_id,), - ) - result = cur.fetchone() - if result: - # This means we're processing an extracted_text file directly - # We should use the original file ID for our transform - original_file_id = str(result[0]) - logger.info(f"Processing extracted_text file. Original file is: {original_file_id}") - - # Get the text content to extract credentials from - text_content, source_object_id = self._get_text_content(object_id) - if not text_content or not source_object_id: - logger.error("No text content found to analyze") - return None - - # See if there are any credentials to extract - credentials = asyncio.run(self._extract_credentials(text_content)) - - # Create a markdown report - markdown_report = credentials_to_markdown(credentials) - - # Store the analysis as a file - with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8") as tmp_credentials: - tmp_credentials.write(markdown_report) - tmp_credentials.flush() - credentials_id = self.storage.upload_file(tmp_credentials.name) - - # IMPORTANT CHANGE: Use the original_file_id (if found) for attaching the transform - # This ensures the analysis is attached to the original file, not the extracted text - target_file_id = original_file_id if original_file_id else object_id - - # Create transform object - credential_transform = Transform( - type="llm_extracted_credentials", - object_id=f"{credentials_id}", - metadata={ - "file_name": "extracted_credentials.md", - "display_type_in_dashboard": "markdown", - "display_title": "LLM-Extracted Credentials", - "default_display": True, - }, - ) - - # Create a finding if credentials were found - findings = [] - if credentials and credentials.strip().lower() != "none": - # Create display data for the finding - display_data = FileObject( - type="finding_summary", - metadata={"summary": markdown_report}, - ) - - # Create the finding - finding = Finding( - category=FindingCategory.CREDENTIAL, - finding_name="llm_extracted_credentials", - origin_type=FindingOrigin.ENRICHMENT_MODULE, - origin_name=self.name, - object_id=target_file_id, - severity=8, - raw_data={"credentials": credentials}, - data=[display_data], - ) - - findings.append(finding) - - # Create the result with the transform and findings - enrichment_result = EnrichmentResult( - module_name=self.name, - dependencies=self.dependencies, - transforms=[credential_transform], - findings=findings if findings else None, - ) - - # Override the object_id in the result to use the original file - # This is a key fix - we're changing where the transform gets attached - if original_file_id: - logger.info(f"Attaching credential transform to original file: {original_file_id}") - - metadata = credential_transform.metadata or {} - - self._add_transform_to_file( - original_file_id, "llm_extracted_credentials", f"{credentials_id}", metadata - ) - - # If we have findings, add them manually too - if findings: - for finding in findings: - self._add_finding_to_file(finding) - - # Return None since we manually added the transform - # This prevents the transform from being added to the extracted text file - return None - - return enrichment_result - - except Exception as e: - logger.exception(e, message="Error extracting credentials from document") - return None - - def _add_transform_to_file(self, object_id: str, transform_type: str, transform_object_id: str, metadata: dict): - """Manually add a transform to a file in the database.""" - try: - with psycopg.connect(self.postgres_connection_string) as conn: - with conn.cursor() as cur: - cur.execute( - """ - INSERT INTO transforms (object_id, type, transform_object_id, metadata) - VALUES (%s, %s, %s, %s) - """, - ( - object_id, - transform_type, - transform_object_id, - json.dumps(metadata) if metadata else None, - ), - ) - conn.commit() - logger.info(f"Added transform {transform_type} to file {object_id}") - except Exception as e: - logger.exception(e, message=f"Error adding transform to file {object_id}") - - def _add_finding_to_file(self, finding: Finding): - """Manually add a finding to the database.""" - try: - with psycopg.connect(self.postgres_connection_string) as conn: - with conn.cursor() as cur: - # Insert the finding - # Note: This is a simplified version - adjust to match your actual database schema - cur.execute( - """ - INSERT INTO findings ( - category, finding_name, origin_type, origin_name, - object_id, severity, raw_data, data - ) - VALUES (%s, %s, %s, %s, %s, %s, %s, %s) - """, - ( - finding.category.value, - finding.finding_name, - finding.origin_type.value, - finding.origin_name, - finding.object_id, - finding.severity, - json.dumps(finding.raw_data) if finding.raw_data else None, - json.dumps([d.model_dump_json() for d in finding.data]) if finding.data else None, - ), - ) - conn.commit() - logger.info(f"Added finding {finding.finding_name} to file {finding.object_id}") - except Exception as e: - logger.exception(e, message=f"Error adding finding to file {finding.object_id}") - - -def create_enrichment_module() -> EnrichmentModule: - return CredentialExtractor() diff --git a/libs/file_enrichment_modules/file_enrichment_modules/lnk/analyzer.py b/libs/file_enrichment_modules/file_enrichment_modules/lnk/analyzer.py index 44dfee1..2191158 100644 --- a/libs/file_enrichment_modules/file_enrichment_modules/lnk/analyzer.py +++ b/libs/file_enrichment_modules/file_enrichment_modules/lnk/analyzer.py @@ -5,15 +5,14 @@ import textwrap from datetime import datetime import LnkParse3 -import structlog import yaml +from common.logger import get_logger from common.models import EnrichmentResult, Transform from common.state_helpers import get_file_enriched from common.storage import StorageMinio - from file_enrichment_modules.module_loader import EnrichmentModule -logger = structlog.get_logger(module=__name__) +logger = get_logger(__name__) def get_lnk_file_display(lnk_file, print_all=False): @@ -26,18 +25,18 @@ def get_lnk_file_display(lnk_file, print_all=False): return identifier.upper().replace("_", " ") return identifier.capitalize().replace("_", " ") - def make_keys_nice(input, uppercase=False): - if isinstance(input, list): - return [make_keys_nice(item) for item in input] - if isinstance(input, dict): - if "class" in input: - key = input.pop("class") - return {key: make_keys_nice(input)} + def make_keys_nice(data, uppercase=False): + if isinstance(data, list): + return [make_keys_nice(item) for item in data] + if isinstance(data, dict): + if "class" in data: + key = data.pop("class") + return {key: make_keys_nice(data)} result = {} - for key, value in input.items(): + for key, value in data.items(): result[nice_id(key, uppercase)] = make_keys_nice(value) return result - return input + return data # remove r_hotkey from header and reformat flags res["header"].pop("r_hotkey") @@ -81,49 +80,77 @@ class LnkParser(EnrichmentModule): # the workflows this module should automatically run in self.workflows = ["default"] - def should_process(self, object_id: str) -> bool: + def should_process(self, object_id: str, file_path: str | None = None) -> bool: """Determine if this module should run.""" - # get the current `file_enriched` from the Dapr statestore file_enriched = get_file_enriched(object_id) - should_run = "ms windows shortcut" in file_enriched.magic_type.lower() - return should_run + return "ms windows shortcut" in file_enriched.magic_type.lower() + + def _analyze_lnk(self, file_path: str, file_enriched) -> EnrichmentResult | None: + """Analyze LNK file and generate enrichment result. + + Args: + file_path: Path to the LNK file to analyze + file_enriched: File enrichment data + + Returns: + EnrichmentResult or None if analysis fails + """ + enrichment_result = EnrichmentResult(module_name=self.name, dependencies=self.dependencies) - def process(self, object_id: str) -> EnrichmentResult | None: - """Process file.""" try: - # get the current `file_enriched` FileEnriched object from the database backend - file_enriched = get_file_enriched(object_id) + with open(file_path, "rb") as f: + lnk = LnkParse3.lnk_file(f) - enrichment_result = EnrichmentResult(module_name=self.name, dependencies=self.dependencies) + enrichment_result.results = convert_datetime(lnk.get_json()) - with self.storage.download(file_enriched.object_id) as temp_file: - with open(temp_file.name, "rb") as f: - lnk = LnkParse3.lnk_file(f) + with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8") as tmp_display_file: + display = get_lnk_file_display(lnk) + tmp_display_file.write(display) + tmp_display_file.flush() - enrichment_result.results = convert_datetime(lnk.get_json()) + object_id = self.storage.upload_file(tmp_display_file.name) - with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8") as tmp_display_file: - display = get_lnk_file_display(lnk) - tmp_display_file.write(display) - tmp_display_file.flush() - - object_id = self.storage.upload_file(tmp_display_file.name) - - displayable_parsed = Transform( - type="displayable_parsed", - object_id=f"{object_id}", - metadata={ - "file_name": f"{file_enriched.file_name}.txt", - "display_type_in_dashboard": "monaco", - "default_display": True, - }, - ) - enrichment_result.transforms = [displayable_parsed] + displayable_parsed = Transform( + type="displayable_parsed", + object_id=f"{object_id}", + metadata={ + "file_name": f"{file_enriched.file_name}.txt", + "display_type_in_dashboard": "monaco", + "default_display": True, + }, + ) + enrichment_result.transforms = [displayable_parsed] return enrichment_result + except Exception as e: + logger.exception(e, message=f"Error analyzing LNK file for {file_enriched.file_name}") + return None + + def process(self, object_id: str, file_path: str | None = None) -> EnrichmentResult | None: + """Process file. + + Args: + object_id: The object ID of the file + file_path: Optional path to already downloaded file + + Returns: + EnrichmentResult or None if processing fails + """ + try: + # get the current `file_enriched` FileEnriched object from the database backend + file_enriched = get_file_enriched(object_id) + + # Use provided file_path if available, otherwise download + if file_path: + return self._analyze_lnk(file_path, file_enriched) + else: + with self.storage.download(file_enriched.object_id) as temp_file: + return self._analyze_lnk(temp_file.name, file_enriched) + except Exception as e: logger.exception(e, message="Error processing file", file_object_id=object_id) + return None def create_enrichment_module() -> EnrichmentModule: diff --git a/libs/file_enrichment_modules/file_enrichment_modules/lsass_dump/analyzer.py b/libs/file_enrichment_modules/file_enrichment_modules/lsass_dump/analyzer.py index 9711ba7..7dcd0a1 100644 --- a/libs/file_enrichment_modules/file_enrichment_modules/lsass_dump/analyzer.py +++ b/libs/file_enrichment_modules/file_enrichment_modules/lsass_dump/analyzer.py @@ -1,24 +1,41 @@ # enrichment_modules/lsass_dump/analyzer.py +import asyncio import tempfile import textwrap -from pathlib import Path from datetime import datetime -import structlog -from common.models import EnrichmentResult, Transform, Finding, FindingCategory, FindingOrigin, FileObject -from common.state_helpers import get_file_enriched +from typing import TYPE_CHECKING +from uuid import UUID + +from common.logger import get_logger +from common.models import EnrichmentResult, FileObject, Finding, FindingCategory, FindingOrigin, Transform +from common.state_helpers import get_file_enriched, get_file_enriched_async from common.storage import StorageMinio from file_enrichment_modules.module_loader import EnrichmentModule - +from nemesis_dpapi import DpapiManager, MasterKey, MasterKeyType from pypykatz.pypykatz import pypykatz -logger = structlog.get_logger(module=__name__) +if TYPE_CHECKING: + from nemesis_dpapi import DpapiManager + +logger = get_logger(__name__) class Credential: """Simple credential class to match the original structure""" - def __init__(self, hostname=None, ssp=None, domain=None, username=None, - password=None, lmhash=None, nthash=None, sha1=None, - masterkey=None, ticket=None): + + def __init__( + self, + hostname=None, + ssp=None, + domain=None, + username=None, + password=None, + lmhash=None, + nthash=None, + sha1=None, + masterkey=None, + ticket=None, + ): self.hostname = hostname self.ssp = ssp self.domain = domain @@ -30,6 +47,7 @@ class Credential: self.masterkey = masterkey self.ticket = ticket + # adapted from/inspired by https://github.com/login-securite/lsassy/blob/9682127364f6f64ce190e8b7f03cdfa1dd457066/lsassy/parser.py (MIT License) class LsassDumpParser(EnrichmentModule): def __init__(self): @@ -37,23 +55,22 @@ class LsassDumpParser(EnrichmentModule): self.storage = StorageMinio() # the workflows this module should automatically run in self.workflows = ["default"] + self.dpapi_manager: DpapiManager = None # type: ignore + self.loop: asyncio.AbstractEventLoop = None # type: ignore + self.size_limit = 1024 * 1024 * 100 # 100 MB size limit for LSASS dumps - def should_process(self, object_id: str) -> bool: + def should_process(self, object_id: str, file_path: str | None = None) -> bool: """Determine if this module should run based on file type.""" file_enriched = get_file_enriched(object_id) - should_run = "mini dump crash report" in file_enriched.magic_type.lower() - logger.debug( - f"LsassDumpParser should_run: {should_run}, file_name: {file_enriched.file_name}" - ) - return should_run + return "mini dump crash report" in file_enriched.magic_type.lower() def _convert_bytes_to_string(self, value): """Convert bytes objects and datetime objects to strings for JSON serialization""" if isinstance(value, bytes): - return value.decode('utf-8', errors='replace') + return value.decode("utf-8", errors="replace") elif isinstance(value, datetime): return str(value) - elif hasattr(value, 'strftime'): # Handle other datetime-like objects + elif hasattr(value, "strftime"): # Handle other datetime-like objects return str(value) elif isinstance(value, dict): return {k: self._convert_bytes_to_string(v) for k, v in value.items()} @@ -62,7 +79,9 @@ class LsassDumpParser(EnrichmentModule): else: return value - def _parse_lsass_dump(self, dump_file_path: str, target_hostname: str = "unknown") -> tuple[list, list, list, list]: + async def _parse_lsass_dump( + self, dump_file_path: str, target_hostname: str = "unknown" + ) -> tuple[list, list, list, list]: """ Parse LSASS dump file using pypykatz :param dump_file_path: Path to the dump file @@ -78,7 +97,7 @@ class LsassDumpParser(EnrichmentModule): pypy_parse = pypykatz.parse_minidump_file(dump_file_path) except Exception as e: logger.error(f"An error occurred while parsing lsass dump: {e}", exc_info=True) - return None, None, None, None + return [], [], [], [] ssps = [ "msv_creds", @@ -96,22 +115,22 @@ class LsassDumpParser(EnrichmentModule): # Extract session metadata session_data = { - 'authentication_id': getattr(session, 'authentication_id', luid), - 'session_id': getattr(session, 'session_id', None), - 'username': getattr(session, 'username', None), - 'domainname': getattr(session, 'domainname', None), - 'logon_server': getattr(session, 'logon_server', None), - 'logon_time': getattr(session, 'logon_time', None), - 'sid': getattr(session, 'sid', None), - 'luid': luid, - 'credentials_by_ssp': {} + "authentication_id": getattr(session, "authentication_id", luid), + "session_id": getattr(session, "session_id", None), + "username": getattr(session, "username", None), + "domainname": getattr(session, "domainname", None), + "logon_server": getattr(session, "logon_server", None), + "logon_time": getattr(session, "logon_time", None), + "sid": getattr(session, "sid", None), + "luid": luid, + "credentials_by_ssp": {}, } # Convert logon_time to string if it exists - if session_data['logon_time']: - session_data['logon_time'] = str(session_data['logon_time']) - if session_data['sid']: - session_data['sid'] = str(session_data['sid']) + if session_data["logon_time"]: + session_data["logon_time"] = str(session_data["logon_time"]) + if session_data["sid"]: + session_data["sid"] = str(session_data["sid"]) # Process each SSP type for session data AND create original credentials for ssp in ssps: @@ -122,16 +141,16 @@ class LsassDumpParser(EnrichmentModule): cred_data = {} # Common fields - if hasattr(cred, 'username'): - cred_data['username'] = cred.username - if hasattr(cred, 'domainname'): - cred_data['domainname'] = cred.domainname - if hasattr(cred, 'password'): - cred_data['password'] = cred.password - if hasattr(cred, 'credtype'): - cred_data['credtype'] = cred.credtype - if hasattr(cred, 'luid'): - cred_data['luid'] = cred.luid + if hasattr(cred, "username"): + cred_data["username"] = cred.username + if hasattr(cred, "domainname"): + cred_data["domainname"] = cred.domainname + if hasattr(cred, "password"): + cred_data["password"] = cred.password + if hasattr(cred, "credtype"): + cred_data["credtype"] = cred.credtype + if hasattr(cred, "luid"): + cred_data["luid"] = cred.luid # Extract credential info for original credential objects (for ALL SSP types) domain = getattr(cred, "domainname", None) @@ -142,11 +161,11 @@ class LsassDumpParser(EnrichmentModule): SHA1 = getattr(cred, "SHAHash", None) if LMHash is not None: - LMHash = LMHash.hex() if hasattr(LMHash, 'hex') else str(LMHash) + LMHash = LMHash.hex() if hasattr(LMHash, "hex") else str(LMHash) if NThash is not None: - NThash = NThash.hex() if hasattr(NThash, 'hex') else str(NThash) + NThash = NThash.hex() if hasattr(NThash, "hex") else str(NThash) if SHA1 is not None: - SHA1 = SHA1.hex() if hasattr(SHA1, 'hex') else str(SHA1) + SHA1 = SHA1.hex() if hasattr(SHA1, "hex") else str(SHA1) # Create credential object for all SSP types that have valid credentials if username and ( @@ -169,49 +188,92 @@ class LsassDumpParser(EnrichmentModule): # MSV specific fields for session data if ssp == "msv_creds": - if hasattr(cred, 'LMHash') and cred.LMHash: - cred_data['LMHash'] = cred.LMHash.hex() if hasattr(cred.LMHash, 'hex') else str(cred.LMHash) - if hasattr(cred, 'NThash') and cred.NThash: - cred_data['NThash'] = cred.NThash.hex() if hasattr(cred.NThash, 'hex') else str(cred.NThash) - if hasattr(cred, 'SHAHash') and cred.SHAHash: - cred_data['SHAHash'] = cred.SHAHash.hex() if hasattr(cred.SHAHash, 'hex') else str(cred.SHAHash) - if hasattr(cred, 'DPAPI') and cred.DPAPI: - cred_data['DPAPI'] = cred.DPAPI.hex() if hasattr(cred.DPAPI, 'hex') else str(cred.DPAPI) + if hasattr(cred, "LMHash") and cred.LMHash: + cred_data["LMHash"] = cred.LMHash.hex() if hasattr(cred.LMHash, "hex") else str(cred.LMHash) + if hasattr(cred, "NThash") and cred.NThash: + cred_data["NThash"] = cred.NThash.hex() if hasattr(cred.NThash, "hex") else str(cred.NThash) + if hasattr(cred, "SHAHash") and cred.SHAHash: + cred_data["SHAHash"] = ( + cred.SHAHash.hex() if hasattr(cred.SHAHash, "hex") else str(cred.SHAHash) + ) + if hasattr(cred, "DPAPI") and cred.DPAPI: + cred_data["DPAPI"] = cred.DPAPI.hex() if hasattr(cred.DPAPI, "hex") else str(cred.DPAPI) # Kerberos specific fields elif ssp == "kerberos_creds": ticket_list = [] - if hasattr(cred, 'tickets'): + if hasattr(cred, "tickets"): for ticket in cred.tickets: tickets.append(ticket) # Add ticket info to the session data ticket_info = { - 'service_name': getattr(ticket, 'ServiceName', [None])[0] if hasattr(ticket, 'ServiceName') and ticket.ServiceName else None, - 'client_name': getattr(ticket, 'EClientName', [None])[0] if hasattr(ticket, 'EClientName') and ticket.EClientName else None, - 'domain_name': getattr(ticket, 'DomainName', None), - 'end_time': str(getattr(ticket, 'EndTime', None)) if hasattr(ticket, 'EndTime') else None + "service_name": getattr(ticket, "ServiceName", [None])[0] + if hasattr(ticket, "ServiceName") and ticket.ServiceName + else None, + "client_name": getattr(ticket, "EClientName", [None])[0] + if hasattr(ticket, "EClientName") and ticket.EClientName + else None, + "domain_name": getattr(ticket, "DomainName", None), + "end_time": str(getattr(ticket, "EndTime", None)) + if hasattr(ticket, "EndTime") + else None, } ticket_list.append(ticket_info) - cred_data['tickets'] = ticket_list + cred_data["tickets"] = ticket_list else: - cred_data['tickets'] = [] - if hasattr(cred, 'aes128') and cred.aes128: - cred_data['aes128'] = cred.aes128.hex() if hasattr(cred.aes128, 'hex') else str(cred.aes128) - if hasattr(cred, 'aes256') and cred.aes256: - cred_data['aes256'] = cred.aes256.hex() if hasattr(cred.aes256, 'hex') else str(cred.aes256) + cred_data["tickets"] = [] + if hasattr(cred, "aes128") and cred.aes128: + cred_data["aes128"] = cred.aes128.hex() if hasattr(cred.aes128, "hex") else str(cred.aes128) + if hasattr(cred, "aes256") and cred.aes256: + cred_data["aes256"] = cred.aes256.hex() if hasattr(cred.aes256, "hex") else str(cred.aes256) # DPAPI specific fields elif ssp == "dpapi_creds": - if hasattr(cred, 'key_guid'): - cred_data['key_guid'] = str(cred.key_guid) - if hasattr(cred, 'masterkey') and cred.masterkey: - cred_data['masterkey'] = cred.masterkey.hex() if hasattr(cred.masterkey, 'hex') else str(cred.masterkey) - if hasattr(cred, 'sha1_masterkey') and cred.sha1_masterkey: - sha1_hex = cred.sha1_masterkey.hex() if hasattr(cred.sha1_masterkey, 'hex') else str(cred.sha1_masterkey) - cred_data['sha1_masterkey'] = sha1_hex + if hasattr(cred, "key_guid"): + cred_data["key_guid"] = str(cred.key_guid) + masterkey_bytes = None + sha1_masterkey_bytes = None + if hasattr(cred, "masterkey") and cred.masterkey: + masterkey_bytes = bytes.fromhex(cred.masterkey) + if hasattr(cred, "sha1_masterkey") and cred.sha1_masterkey: + sha1_masterkey_bytes = bytes.fromhex(cred.sha1_masterkey) + + mk = MasterKey( + guid=UUID(str(cred.key_guid)), + masterkey_type=MasterKeyType.UNKNOWN, + plaintext_key=masterkey_bytes, + plaintext_key_sha1=sha1_masterkey_bytes, + ) + # add this masterkey to the DPAPI cache + if self.dpapi_manager: + try: + await self.dpapi_manager.upsert_masterkey(mk) + logger.info( + "Inserted masterkey from lsass dump into DPAPI manager", + masterkey_guid=cred.key_guid, + ) + except Exception as e: + # Database operation failed - skip to avoid event loop conflicts + logger.warning( + f"Inserting master key failed, skipping masterkey database update. Error: {e}" + ) + else: + logger.warning("self.dpapi_manager not initialized!") + + if hasattr(cred, "masterkey") and cred.masterkey: + cred_data["masterkey"] = ( + cred.masterkey.hex() if hasattr(cred.masterkey, "hex") else str(cred.masterkey) + ) + if hasattr(cred, "sha1_masterkey") and cred.sha1_masterkey: + sha1_hex = ( + cred.sha1_masterkey.hex() + if hasattr(cred.sha1_masterkey, "hex") + else str(cred.sha1_masterkey) + ) + cred_data["sha1_masterkey"] = sha1_hex # Add to masterkeys list - m = "{%s}:%s" % (cred.key_guid, sha1_hex) + m = f"{{{cred.key_guid}}}:{sha1_hex}" if m not in masterkeys: masterkeys.append(m) credentials.append( @@ -226,12 +288,14 @@ class LsassDumpParser(EnrichmentModule): # WDIGEST specific fields elif ssp == "wdigest_creds": - if hasattr(cred, 'password_raw'): + if hasattr(cred, "password_raw"): # Convert bytes to string for JSON serialization if isinstance(cred.password_raw, bytes): - cred_data['password_raw'] = cred.password_raw.decode('utf-8', errors='replace') + cred_data["password_raw"] = cred.password_raw.decode("utf-8", errors="replace") else: - cred_data['password_raw'] = str(cred.password_raw) if cred.password_raw is not None else "" + cred_data["password_raw"] = ( + str(cred.password_raw) if cred.password_raw is not None else "" + ) if cred_data: # Only add if we have some data # Convert any bytes objects to strings for JSON serialization @@ -239,7 +303,7 @@ class LsassDumpParser(EnrichmentModule): ssp_creds.append(cred_data) if ssp_creds: # Only add SSP if it has credentials - session_data['credentials_by_ssp'][ssp] = ssp_creds + session_data["credentials_by_ssp"][ssp] = ssp_creds # Clean session data of any remaining bytes objects session_data = self._convert_bytes_to_string(session_data) @@ -255,10 +319,7 @@ class LsassDumpParser(EnrichmentModule): for ticket in tickets: if ticket.ServiceName is not None and ticket.ServiceName[0] == "krbtgt": if ticket.EClientName is not None and ticket.DomainName is not None: - if ( - ticket.TargetDomainName is not None - and ticket.TargetDomainName != ticket.DomainName - ): + if ticket.TargetDomainName is not None and ticket.TargetDomainName != ticket.DomainName: target_domain = ticket.TargetDomainName else: target_domain = ticket.DomainName @@ -281,17 +342,24 @@ class LsassDumpParser(EnrichmentModule): ) ) + # # for debugging + # mks = await self.dpapi_manager.get_all_masterkeys() + # import pprint + # pprint.pprint(mks) + return logon_sessions, credentials, tickets, masterkeys - def _create_finding_summary(self, logon_sessions: list, credentials: list, tickets: list, masterkeys: list) -> str: + async def _create_finding_summary( + self, logon_sessions: list, credentials: list, tickets: list, masterkeys: list + ) -> str: """Creates a markdown summary for the LSASS dump findings.""" summary = "# LSASS Dump Analysis Results\n\n" # Summary statistics - summary += f"**Total Logon Sessions**: {len(logon_sessions)}\n" - summary += f"**Total Credentials Found**: {len(credentials)}\n" - summary += f"**Total Tickets Found**: {len(tickets)}\n" - summary += f"**Total DPAPI Masterkeys**: {len(masterkeys)}\n\n" + summary += f"**Total Logon Sessions**: {len(logon_sessions)}\n\n" + summary += f"**Total Credentials Found**: {len(credentials)}\n\n" + summary += f"**Total Tickets Found**: {len(tickets)}\n\n" + summary += f"**Total DPAPI Masterkeys**: {len(masterkeys)}\n\n\n" # Process each logon session for i, session in enumerate(logon_sessions, 1): @@ -308,7 +376,7 @@ class LsassDumpParser(EnrichmentModule): summary += f"* **LUID**: `{session.get('luid', 'N/A')}`\n\n" # Credentials by SSP - creds_by_ssp = session.get('credentials_by_ssp', {}) + creds_by_ssp = session.get("credentials_by_ssp", {}) if creds_by_ssp: for ssp, creds in creds_by_ssp.items(): if creds: @@ -319,9 +387,18 @@ class LsassDumpParser(EnrichmentModule): for key, value in cred.items(): if value is not None and value != "": - if key in ['NThash', 'LMHash', 'SHAHash', 'DPAPI', 'aes128', 'aes256', 'masterkey', 'sha1_masterkey']: + if key in [ + "NThash", + "LMHash", + "SHAHash", + "DPAPI", + "aes128", + "aes256", + "masterkey", + "sha1_masterkey", + ]: summary += f"* **{key}**: `{value}`\n" - elif key == 'tickets' and isinstance(value, list) and value: + elif key == "tickets" and isinstance(value, list) and value: summary += f"* **Tickets**: {len(value)} found\n" for i, ticket in enumerate(value, 1): summary += f" * **Ticket {i}**: Service=`{ticket.get('service_name', 'N/A')}`, Client=`{ticket.get('client_name', 'N/A')}`, Domain=`{ticket.get('domain_name', 'N/A')}`, EndTime=`{ticket.get('end_time', 'N/A')}`\n" @@ -335,145 +412,161 @@ class LsassDumpParser(EnrichmentModule): return summary - def process(self, object_id: str) -> EnrichmentResult | None: - """Process LSASS dump file and extract credentials.""" - try: - file_enriched = get_file_enriched(object_id) - enrichment_result = EnrichmentResult( - module_name=self.name, - dependencies=self.dependencies + async def _analyze_lsass_dump_file(self, file_path: str, file_enriched) -> EnrichmentResult | None: + """Analyze LSASS dump file and generate enrichment result. + + Args: + file_path: Path to the LSASS dump file to analyze + file_enriched: File enrichment data + + Returns: + EnrichmentResult or None if analysis fails + """ + enrichment_result = EnrichmentResult(module_name=self.name, dependencies=self.dependencies) + + # Parse the LSASS dump + logon_sessions, credentials, tickets, masterkeys = await self._parse_lsass_dump( + file_path, file_enriched.file_name + ) + + if not len(logon_sessions): + logger.error("Failed to parse LSASS dump file") + return None + + if logon_sessions or credentials or tickets or masterkeys: + # Create finding summary + summary_markdown = await self._create_finding_summary(logon_sessions, credentials, tickets, masterkeys) + + # Prepare credentials data for serialization (convert objects to dicts) + credentials_data = [] + for cred in credentials: + cred_dict = { + "hostname": cred.hostname, + "ssp": cred.ssp, + "domain": cred.domain, + "username": cred.username, + "password": cred.password, + "lmhash": cred.lmhash, + "nthash": cred.nthash, + "sha1": cred.sha1, + "masterkey": cred.masterkey, + "ticket": cred.ticket, + } + credentials_data.append(cred_dict) + + # Create display data + display_data = FileObject(type="finding_summary", metadata={"summary": summary_markdown}) + + # Create finding + finding = Finding( + category=FindingCategory.CREDENTIAL, + finding_name="lsass_credentials_detected", + origin_type=FindingOrigin.ENRICHMENT_MODULE, + origin_name=self.name, + object_id=file_enriched.object_id, + severity=9, # High severity for credential extraction + raw_data={ + "logon_sessions": logon_sessions, + "credentials": credentials_data, + "ticket_count": len(tickets), + "masterkey_count": len(masterkeys), + }, + data=[display_data], ) + # Add finding to enrichment result + enrichment_result.findings = [finding] + enrichment_result.results = { + "logon_sessions": logon_sessions, + "credentials": credentials_data, + "ticket_count": len(tickets), + "masterkey_count": len(masterkeys), + } + + # Create a displayable version of the results + with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8") as tmp_display_file: + yaml_output = [] + yaml_output.append("LSASS Dump Analysis Results") + yaml_output.append("===========================\n") + + yaml_output.append(f"Total Logon Sessions: {len(logon_sessions)}") + yaml_output.append(f"Total Credentials: {len(credentials)}") + yaml_output.append(f"Total Tickets: {len(tickets)}") + yaml_output.append(f"Total Masterkeys: {len(masterkeys)}\n") + + for i, session in enumerate(logon_sessions, 1): + yaml_output.append(f"Logon Session {i}:") + yaml_output.append(f" Authentication ID: {session.get('authentication_id', 'N/A')}") + yaml_output.append(f" Username: {session.get('username', 'N/A')}") + yaml_output.append(f" Domain: {session.get('domainname', 'N/A')}") + yaml_output.append(f" Logon Server: {session.get('logon_server', 'N/A')}") + yaml_output.append(f" Logon Time: {session.get('logon_time', 'N/A')}") + yaml_output.append(f" SID: {session.get('sid', 'N/A')}") + yaml_output.append(f" LUID: {session.get('luid', 'N/A')}") + + creds_by_ssp = session.get("credentials_by_ssp", {}) + if creds_by_ssp: + for ssp, creds in creds_by_ssp.items(): + yaml_output.append(f" {ssp.upper()}:") + for j, cred in enumerate(creds, 1): + yaml_output.append(f" Credential {j}:") + for key, value in cred.items(): + if value is not None and value != "": + if key == "tickets" and isinstance(value, list) and value: + yaml_output.append(f" {key}: {len(value)} tickets found") + for k, ticket in enumerate(value, 1): + yaml_output.append( + f" Ticket {k}: Service={ticket.get('service_name', 'N/A')}, Client={ticket.get('client_name', 'N/A')}, Domain={ticket.get('domain_name', 'N/A')}, EndTime={ticket.get('end_time', 'N/A')}" + ) + else: + yaml_output.append(f" {key}: {value}") + yaml_output.append("") # Add empty line between sessions + + display = textwrap.indent("\n".join(yaml_output), " ") + tmp_display_file.write(display) + tmp_display_file.flush() + + object_id = self.storage.upload_file(tmp_display_file.name) + + displayable_parsed = Transform( + type="displayable_parsed", + object_id=f"{object_id}", + metadata={ + "file_name": f"{file_enriched.file_name}_lsass_analysis.txt", + "display_type_in_dashboard": "monaco", + "default_display": True, + }, + ) + enrichment_result.transforms = [displayable_parsed] + + return enrichment_result + + async def process(self, object_id: str, file_path: str | None = None) -> EnrichmentResult | None: + """Process LSASS dump file and extract credentials. + + Args: + object_id: The object ID of the file + file_path: Optional path to already downloaded file + + Returns: + EnrichmentResult or None if processing fails + """ + return await self._process_async(object_id, file_path) + + async def _process_async(self, object_id: str, file_path: str | None = None) -> EnrichmentResult | None: + """Async helper for process method.""" + + logger.debug("Starting async processing of LSASS dump", object_id=object_id) + file_enriched = await get_file_enriched_async(object_id) + + # Use provided file_path if available, otherwise download + if file_path: + return await self._analyze_lsass_dump_file(file_path, file_enriched) + else: # Download the file to a temporary location with self.storage.download(file_enriched.object_id) as temp_file: - # Parse the LSASS dump - logon_sessions, credentials, tickets, masterkeys = self._parse_lsass_dump( - temp_file.name, - file_enriched.file_name - ) - - if logon_sessions is None: - logger.error("Failed to parse LSASS dump file") - return None - - if logon_sessions or credentials or tickets or masterkeys: - # Create finding summary - summary_markdown = self._create_finding_summary(logon_sessions, credentials, tickets, masterkeys) - - # Prepare credentials data for serialization (convert objects to dicts) - credentials_data = [] - for cred in credentials: - cred_dict = { - 'hostname': cred.hostname, - 'ssp': cred.ssp, - 'domain': cred.domain, - 'username': cred.username, - 'password': cred.password, - 'lmhash': cred.lmhash, - 'nthash': cred.nthash, - 'sha1': cred.sha1, - 'masterkey': cred.masterkey, - 'ticket': cred.ticket - } - credentials_data.append(cred_dict) - - # Create display data - display_data = FileObject( - type="finding_summary", - metadata={ - "summary": summary_markdown - } - ) - - # Create finding - finding = Finding( - category=FindingCategory.CREDENTIAL, - finding_name="lsass_credentials_detected", - origin_type=FindingOrigin.ENRICHMENT_MODULE, - origin_name=self.name, - object_id=file_enriched.object_id, - severity=9, # High severity for credential extraction - raw_data={ - "logon_sessions": logon_sessions, - "credentials": credentials_data, - "ticket_count": len(tickets), - "masterkey_count": len(masterkeys) - }, - data=[display_data] - ) - - # Add finding to enrichment result - enrichment_result.findings = [finding] - enrichment_result.results = { - "logon_sessions": logon_sessions, - "credentials": credentials_data, - "ticket_count": len(tickets), - "masterkey_count": len(masterkeys) - } - - # Create a displayable version of the results - with tempfile.NamedTemporaryFile(mode='w', encoding='utf-8') as tmp_display_file: - yaml_output = [] - yaml_output.append("LSASS Dump Analysis Results") - yaml_output.append("===========================\n") - - yaml_output.append(f"Total Logon Sessions: {len(logon_sessions)}") - yaml_output.append(f"Total Credentials: {len(credentials)}") - yaml_output.append(f"Total Tickets: {len(tickets)}") - yaml_output.append(f"Total Masterkeys: {len(masterkeys)}\n") - - for i, session in enumerate(logon_sessions, 1): - yaml_output.append(f"Logon Session {i}:") - yaml_output.append(f" Authentication ID: {session.get('authentication_id', 'N/A')}") - yaml_output.append(f" Username: {session.get('username', 'N/A')}") - yaml_output.append(f" Domain: {session.get('domainname', 'N/A')}") - yaml_output.append(f" Logon Server: {session.get('logon_server', 'N/A')}") - yaml_output.append(f" Logon Time: {session.get('logon_time', 'N/A')}") - yaml_output.append(f" SID: {session.get('sid', 'N/A')}") - yaml_output.append(f" LUID: {session.get('luid', 'N/A')}") - - creds_by_ssp = session.get('credentials_by_ssp', {}) - if creds_by_ssp: - for ssp, creds in creds_by_ssp.items(): - yaml_output.append(f" {ssp.upper()}:") - for j, cred in enumerate(creds, 1): - yaml_output.append(f" Credential {j}:") - for key, value in cred.items(): - if value is not None and value != "": - if key == 'tickets' and isinstance(value, list) and value: - yaml_output.append(f" {key}: {len(value)} tickets found") - for k, ticket in enumerate(value, 1): - yaml_output.append(f" Ticket {k}: Service={ticket.get('service_name', 'N/A')}, Client={ticket.get('client_name', 'N/A')}, Domain={ticket.get('domain_name', 'N/A')}, EndTime={ticket.get('end_time', 'N/A')}") - else: - yaml_output.append(f" {key}: {value}") - yaml_output.append("") # Add empty line between sessions - - display = textwrap.indent( - "\n".join(yaml_output), - " " - ) - tmp_display_file.write(display) - tmp_display_file.flush() - - object_id = self.storage.upload_file(tmp_display_file.name) - - displayable_parsed = Transform( - type="displayable_parsed", - object_id=f"{object_id}", - metadata={ - "file_name": f"{file_enriched.file_name}_lsass_analysis.txt", - "display_type_in_dashboard": "monaco", - "default_display": True - }, - ) - enrichment_result.transforms = [displayable_parsed] - - return enrichment_result - - except Exception as e: - logger.exception(e, message="Error processing LSASS dump file") - return None + return await self._analyze_lsass_dump_file(temp_file.name, file_enriched) def create_enrichment_module() -> EnrichmentModule: - return LsassDumpParser() \ No newline at end of file + return LsassDumpParser() diff --git a/libs/file_enrichment_modules/file_enrichment_modules/module_loader.py b/libs/file_enrichment_modules/file_enrichment_modules/module_loader.py index 9d1b923..b5e4b33 100644 --- a/libs/file_enrichment_modules/file_enrichment_modules/module_loader.py +++ b/libs/file_enrichment_modules/file_enrichment_modules/module_loader.py @@ -2,12 +2,11 @@ import asyncio import importlib.util import sys from pathlib import Path -from typing import Optional -import structlog +from common.logger import get_logger from common.models import EnrichmentResult -logger = structlog.get_logger(module=__name__) +logger = get_logger(__name__) class EnrichmentModule: @@ -15,17 +14,17 @@ class EnrichmentModule: self.name = name self.dependencies = dependencies or [] - async def should_process(self, object_id: str) -> bool: + def should_process(self, object_id: str, file_path: str | None = None) -> bool: """Determine if this module should process the given file.""" raise NotImplementedError - async def process(self, object_id: str) -> EnrichmentResult | None: + def process(self, object_id: str, file_path: str | None = None) -> EnrichmentResult | None: """Process the file and return enrichment results.""" raise NotImplementedError class ModuleLoader: - def __init__(self, modules_dir: Optional[str] = None): + def __init__(self, modules_dir: str | None = None): if modules_dir is None: self.modules_dir = Path(__file__).parent else: diff --git a/libs/file_enrichment_modules/file_enrichment_modules/office_doc/analyzer.py b/libs/file_enrichment_modules/file_enrichment_modules/office_doc/analyzer.py index a27eeec..515a140 100644 --- a/libs/file_enrichment_modules/file_enrichment_modules/office_doc/analyzer.py +++ b/libs/file_enrichment_modules/file_enrichment_modules/office_doc/analyzer.py @@ -7,17 +7,15 @@ from typing import Any import msoffcrypto import olefile -import structlog -from common.helpers import escape_markdown +from common.logger import get_logger from common.models import EnrichmentResult, FileObject, Finding, FindingCategory, FindingOrigin, Transform from common.state_helpers import get_file_enriched from common.storage import StorageMinio -from oletools.olevba import VBA_Parser - from file_enrichment_modules.module_loader import EnrichmentModule from file_enrichment_modules.office_doc.office2john import extract_file_encryption_hash +from oletools.olevba import VBA_Parser -logger = structlog.get_logger(module=__name__) +logger = get_logger(__name__) def create_encryption_finding(file_enriched, encryption_hash: str, module_name: str) -> Finding: @@ -26,7 +24,7 @@ def create_encryption_finding(file_enriched, encryption_hash: str, module_name: # Encrypted Document The document is encrypted. Attempt to crack it using the following hash: ``` -{escape_markdown(encryption_hash)} +{encryption_hash} ``` """ display_data = FileObject(type="finding_summary", metadata={"summary": summary_markdown}) @@ -332,7 +330,7 @@ class OfficeAnalyzer(EnrichmentModule): # the workflows this module should automatically run in self.workflows = ["default"] - def should_process(self, object_id: str) -> bool: + def should_process(self, object_id: str, file_path: str | None = None) -> bool: """Determine if this module should run based on file extension and type.""" file_enriched = get_file_enriched(object_id) @@ -350,24 +348,29 @@ class OfficeAnalyzer(EnrichmentModule): should_run = has_valid_extension or is_office_type return should_run - def process(self, object_id: str) -> EnrichmentResult | None: - """Process Office file using the storage system.""" - file_enriched = get_file_enriched(object_id) - path = file_enriched.path.lower() if file_enriched.path else "" + def _analyze_office_document(self, file_path: str, file_enriched) -> EnrichmentResult | None: + """Analyze Office document and generate enrichment result. + Args: + file_path: Path to the Office document to analyze + file_enriched: File enrichment data + + Returns: + EnrichmentResult or None if analysis fails + """ enrichment_result = EnrichmentResult(module_name=self.name) - with self.storage.download(file_enriched.object_id) as file: + try: # Determine file type and use appropriate parser if "openxmlformats" in file_enriched.mime_type.lower(): - analysis = parse_office_new_file(file.name) + analysis = parse_office_new_file(file_path) else: - analysis = parse_office_ole_file(file.name) + analysis = parse_office_ole_file(file_path) enrichment_result.results = analysis findings = [] - transforms = [] + # transforms = [] # Create finding if document is encrypted if analysis.get("encryption_hash"): @@ -407,7 +410,35 @@ class OfficeAnalyzer(EnrichmentModule): enrichment_result.findings = findings - return enrichment_result + return enrichment_result + + except Exception as e: + logger.exception(e, message=f"Error analyzing Office document for {file_enriched.file_name}") + return None + + def process(self, object_id: str, file_path: str | None = None) -> EnrichmentResult | None: + """Process Office file using the storage system. + + Args: + object_id: The object ID of the file + file_path: Optional path to already downloaded file + + Returns: + EnrichmentResult or None if processing fails + """ + try: + file_enriched = get_file_enriched(object_id) + + # Use provided file_path if available, otherwise download + if file_path: + return self._analyze_office_document(file_path, file_enriched) + else: + with self.storage.download(file_enriched.object_id) as file: + return self._analyze_office_document(file.name, file_enriched) + + except Exception as e: + logger.exception(e, message="Error processing Office file") + return None def create_enrichment_module() -> EnrichmentModule: diff --git a/libs/file_enrichment_modules/file_enrichment_modules/office_doc/office2john.py b/libs/file_enrichment_modules/file_enrichment_modules/office_doc/office2john.py index 0e58e6c..ccfcdcf 100644 --- a/libs/file_enrichment_modules/file_enrichment_modules/office_doc/office2john.py +++ b/libs/file_enrichment_modules/file_enrichment_modules/office_doc/office2john.py @@ -368,7 +368,7 @@ def set_debug_mode(debug_mode): # === CONSTANTS =============================================================== # magic bytes that should be at the beginning of every OLE file: -MAGIC = b"\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1" +MAGIC = b"\xd0\xcf\x11\xe0\xa1\xb1\x1a\xe1" # [PL]: added constants for Sector IDs (from AAF specifications) MAXREGSECT = 0xFFFFFFFA # (-6) maximum SECT @@ -497,7 +497,11 @@ def isOleFile(filename): header = filename[: len(MAGIC)] else: # string-like object: filename of file on disk - header = open(filename, "rb").read(len(MAGIC)) + f = open(filename, "rb") + try: + header = f.read(len(MAGIC)) + finally: + f.close() if header == MAGIC: return True else: @@ -552,7 +556,9 @@ def _clsid(clsid): # (PL: why not simply return the string with zeroes?) if not clsid.strip(b"\0"): return "" - return ("%08X-%04X-%04X-%02X%02X-" + "%02X" * 6) % ((i32(clsid, 0), i16(clsid, 4), i16(clsid, 6)) + tuple(map(i8, clsid[8:16]))) + return ("%08X-%04X-%04X-%02X%02X-" + "%02X" * 6) % ( + (i32(clsid, 0), i16(clsid, 4), i16(clsid, 6)) + tuple(map(i8, clsid[8:16])) + ) def filetime2datetime(filetime): @@ -796,7 +802,10 @@ class _OleStream(io.BytesIO): :returns: a BytesIO instance containing the OLE stream """ debug("_OleStream.__init__:") - debug(" sect=%d (%X), size=%d, offset=%d, sectorsize=%d, len(fat)=%d, fp=%s" % (sect, sect, size, offset, sectorsize, len(fat), repr(fp))) + debug( + " sect=%d (%X), size=%d, offset=%d, sectorsize=%d, len(fat)=%d, fp=%s" + % (sect, sect, size, offset, sectorsize, len(fat), repr(fp)) + ) # [PL] To detect malformed documents with FAT loops, we compute the # expected number of sectors in the stream: unknown_size = False @@ -813,7 +822,7 @@ class _OleStream(io.BytesIO): # This number should (at least) be less than the total number of # sectors in the given FAT: if nb_sectors > len(fat): - raise IOError("malformed OLE document, stream too large") + raise OSError("malformed OLE document, stream too large") # optimization(?): data is first a list of strings, and join() is called # at the end to concatenate all in one string. # (this may not be really useful with recent Python versions) @@ -821,7 +830,7 @@ class _OleStream(io.BytesIO): # if size is zero, then first sector index should be ENDOFCHAIN: if size == 0 and sect != ENDOFCHAIN: debug("size == 0 and sect != ENDOFCHAIN:") - raise IOError("incorrect OLE sector index for empty stream") + raise OSError("incorrect OLE sector index for empty stream") # [PL] A fixed-length for loop is used instead of an undefined while # loop to avoid DoS attacks: for i in range(nb_sectors): @@ -832,7 +841,7 @@ class _OleStream(io.BytesIO): else: # else this means that the stream is smaller than declared: debug("sect=ENDOFCHAIN before expected size") - raise IOError("incomplete OLE stream") + raise OSError("incomplete OLE stream") # sector index should be within FAT: if sect < 0 or sect >= len(fat): debug("sect=%d (%X) / len(fat)=%d" % (sect, sect, len(fat))) @@ -842,33 +851,36 @@ class _OleStream(io.BytesIO): # f.write(tmp_data) # f.close() # debug('data read so far: %d bytes' % len(tmp_data)) - raise IOError("incorrect OLE FAT, sector index out of range") + raise OSError("incorrect OLE FAT, sector index out of range") # TODO: merge this code with OleFileIO.getsect() ? # TODO: check if this works with 4K sectors: try: fp.seek(offset + sectorsize * sect) except: debug("sect=%d, seek=%d, filesize=%d" % (sect, offset + sectorsize * sect, filesize)) - raise IOError("OLE sector index out of range") + raise OSError("OLE sector index out of range") sector_data = fp.read(sectorsize) # [PL] check if there was enough data: # Note: if sector is the last of the file, sometimes it is not a # complete sector (of 512 or 4K), so we may read less than # sectorsize. if len(sector_data) != sectorsize and sect != (len(fat) - 1): - debug("sect=%d / len(fat)=%d, seek=%d / filesize=%d, len read=%d" % (sect, len(fat), offset + sectorsize * sect, filesize, len(sector_data))) + debug( + "sect=%d / len(fat)=%d, seek=%d / filesize=%d, len read=%d" + % (sect, len(fat), offset + sectorsize * sect, filesize, len(sector_data)) + ) debug("seek+len(read)=%d" % (offset + sectorsize * sect + len(sector_data))) - raise IOError("incomplete OLE sector") + raise OSError("incomplete OLE sector") data.append(sector_data) # jump to next sector in the FAT: try: sect = fat[sect] & 0xFFFFFFFF # JYTHON-WORKAROUND except IndexError: # [PL] if pointer is out of the FAT an exception is raised - raise IOError("incorrect OLE FAT, sector index out of range") + raise OSError("incorrect OLE FAT, sector index out of range") # [PL] Last sector should be a "end of chain" marker: if sect != ENDOFCHAIN: - raise IOError("incorrect last sector index in OLE stream") + raise OSError("incorrect last sector index in OLE stream") data = b"".join(data) # Data is truncated to the actual stream size: if len(data) >= size: @@ -882,7 +894,7 @@ class _OleStream(io.BytesIO): else: # read data is less than expected: debug("len(data)=%d, size=%d" % (len(data), size)) - raise IOError("OLE stream size is less than declared") + raise OSError("OLE stream size is less than declared") # when all data is read in memory, BytesIO constructor is called io.BytesIO.__init__(self, data) # Then the _OleStream object can be used as a read-only file object. @@ -892,7 +904,6 @@ class _OleStream(io.BytesIO): class _OleDirectoryEntry: - """ OLE2 Directory Entry """ @@ -945,9 +956,22 @@ class _OleDirectoryEntry: # directory: self.used = False # decode DirEntry - (name, namelength, self.entry_type, self.color, self.sid_left, self.sid_right, self.sid_child, clsid, self.dwUserFlags, self.createTime, self.modifyTime, self.isectStart, sizeLow, sizeHigh) = struct.unpack( - _OleDirectoryEntry.STRUCT_DIRENTRY, entry - ) + ( + name, + namelength, + self.entry_type, + self.color, + self.sid_left, + self.sid_right, + self.sid_child, + clsid, + self.dwUserFlags, + self.createTime, + self.modifyTime, + self.isectStart, + sizeLow, + sizeHigh, + ) = struct.unpack(_OleDirectoryEntry.STRUCT_DIRENTRY, entry) if self.entry_type not in [STGTY_ROOT, STGTY_STORAGE, STGTY_STREAM, STGTY_EMPTY]: olefile._raise_defect(DEFECT_INCORRECT, "unhandled OLE storage type") # only first directory entry can (and should) be root: @@ -994,7 +1018,9 @@ class _OleDirectoryEntry: olefile._raise_defect(DEFECT_POTENTIAL, "OLE storage with size>0") # check if stream is not already referenced elsewhere: if self.entry_type in (STGTY_ROOT, STGTY_STREAM) and self.size > 0: - if self.size < olefile.minisectorcutoff and self.entry_type == STGTY_STREAM: # only streams can be in MiniFAT + if ( + self.size < olefile.minisectorcutoff and self.entry_type == STGTY_STREAM + ): # only streams can be in MiniFAT # ministream object minifat = True else: @@ -1041,7 +1067,10 @@ class _OleDirectoryEntry: self.olefile._raise_defect(DEFECT_FATAL, "OLE DirEntry index out of range") # get child direntry: child = self.olefile._load_direntry(child_sid) # direntries[child_sid] - debug("append_kids: child_sid=%d - %s - sid_left=%d, sid_right=%d, sid_child=%d" % (child.sid, repr(child.name), child.sid_left, child.sid_right, child.sid_child)) + debug( + "append_kids: child_sid=%d - %s - sid_left=%d, sid_right=%d, sid_child=%d" + % (child.sid, repr(child.name), child.sid_left, child.sid_right, child.sid_child) + ) # the directory entries are organized as a red-black tree. # (cf. Wikipedia for details) # First walk through left side of the tree: @@ -1143,18 +1172,28 @@ class OleFileIO: if entry[1:2] == "Image": fin = ole.openstream(entry) fout = open(entry[0:1], "wb") - while True: - s = fin.read(8192) - if not s: - break - fout.write(s) + try: + while True: + s = fin.read(8192) + if not s: + break + fout.write(s) + finally: + fout.close() You can use the viewer application provided with the Python Imaging Library to view the resulting files (which happens to be standard TIFF files). """ - def __init__(self, filename=None, raise_defects=DEFECT_FATAL, write_mode=False, debug=False, path_encoding=DEFAULT_PATH_ENCODING): + def __init__( + self, + filename=None, + raise_defects=DEFECT_FATAL, + write_mode=False, + debug=False, + path_encoding=DEFAULT_PATH_ENCODING, + ): """ Constructor for the OleFileIO class. @@ -1625,7 +1664,7 @@ class OleFileIO: nb_difat = (self.csectFat - 109 + nb_difat_sectors - 1) // nb_difat_sectors debug("nb_difat = %d" % nb_difat) if self.csectDif != nb_difat: - raise IOError("incorrect DIFAT") + raise OSError("incorrect DIFAT") isect_difat = self.sectDifStart for i in iterrange(nb_difat): debug("DIFAT block %d, sector %X" % (i, isect_difat)) @@ -1640,7 +1679,7 @@ class OleFileIO: # checks: if isect_difat not in [ENDOFCHAIN, FREESECT]: # last DIFAT pointer value must be ENDOFCHAIN or FREESECT - raise IOError("incorrect end of DIFAT") + raise OSError("incorrect end of DIFAT") # if len(self.fat) != self.csectFat: # # FAT should contain csectFat blocks # print("FAT length: %d instead of %d" % (len(self.fat), self.csectFat)) @@ -1671,7 +1710,10 @@ class OleFileIO: # 32 bits indexes: nb_minisectors = (self.root.size + self.MiniSectorSize - 1) // self.MiniSectorSize used_size = nb_minisectors * 4 - debug("loadminifat(): minifatsect=%d, nb FAT sectors=%d, used_size=%d, stream_size=%d, nb MiniSectors=%d" % (self.minifatsect, self.csectMiniFat, used_size, stream_size, nb_minisectors)) + debug( + "loadminifat(): minifatsect=%d, nb FAT sectors=%d, used_size=%d, stream_size=%d, nb MiniSectors=%d" + % (self.minifatsect, self.csectMiniFat, used_size, stream_size, nb_minisectors) + ) if used_size > stream_size: # This is not really a problem, but may indicate a wrong implementation: self._raise_defect(DEFECT_INCORRECT, "OLE MiniStream is larger than MiniFAT") @@ -1827,10 +1869,26 @@ class OleFileIO: size_ministream = self.root.size debug("Opening MiniStream: sect=%d, size=%d" % (self.root.isectStart, size_ministream)) self.ministream = self._open(self.root.isectStart, size_ministream, force_FAT=True) - return _OleStream(fp=self.ministream, sect=start, size=size, offset=0, sectorsize=self.minisectorsize, fat=self.minifat, filesize=self.ministream.size) + return _OleStream( + fp=self.ministream, + sect=start, + size=size, + offset=0, + sectorsize=self.minisectorsize, + fat=self.minifat, + filesize=self.ministream.size, + ) else: # standard stream - return _OleStream(fp=self.fp, sect=start, size=size, offset=self.sectorsize, sectorsize=self.sectorsize, fat=self.fat, filesize=self._filesize) + return _OleStream( + fp=self.fp, + sect=start, + size=size, + offset=self.sectorsize, + sectorsize=self.sectorsize, + fat=self.fat, + filesize=self._filesize, + ) def _list(self, files, prefix, node, streams=True, storages=False): """ @@ -1858,7 +1916,9 @@ class OleFileIO: # add it to the list files.append(prefix[1:] + [entry.name]) else: - self._raise_defect(DEFECT_INCORRECT, "The directory tree contains an entry which is not a stream nor a storage.") + self._raise_defect( + DEFECT_INCORRECT, "The directory tree contains an entry which is not a stream nor a storage." + ) def listdir(self, streams=True, storages=False): """ @@ -1900,7 +1960,7 @@ class OleFileIO: if kid.name.lower() == name.lower(): break else: - raise IOError("file not found") + raise OSError("file not found") node = kid return node.sid @@ -1922,7 +1982,7 @@ class OleFileIO: sid = self._find(filename) entry = self.direntries[sid] if entry.entry_type != STGTY_STREAM: - raise IOError("this file is not a stream") + raise OSError("this file is not a stream") return self._open(entry.isectStart, entry.size) def write_stream(self, stream_name, data): @@ -1945,7 +2005,7 @@ class OleFileIO: sid = self._find(stream_name) entry = self.direntries[sid] if entry.entry_type != STGTY_STREAM: - raise IOError("this is not a stream") + raise OSError("this is not a stream") size = entry.size if size != len(data): raise ValueError("write_stream: data must be the same size as the existing stream") @@ -1970,7 +2030,10 @@ class OleFileIO: else: data_sector = data[i * self.sectorsize :] # TODO: comment this if it works - debug("write_stream: size=%d sectorsize=%d data_sector=%d size%%sectorsize=%d" % (size, self.sectorsize, len(data_sector), size % self.sectorsize)) + debug( + "write_stream: size=%d sectorsize=%d data_sector=%d size%%sectorsize=%d" + % (size, self.sectorsize, len(data_sector), size % self.sectorsize) + ) assert len(data_sector) % self.sectorsize == size % self.sectorsize self.write_sect(sect, data_sector) # self.fp.write(data_sector) @@ -1979,10 +2042,10 @@ class OleFileIO: sect = self.fat[sect] except IndexError: # [PL] if pointer is out of the FAT an exception is raised - raise IOError("incorrect OLE FAT, sector index out of range") + raise OSError("incorrect OLE FAT, sector index out of range") # [PL] Last sector should be a "end of chain" marker: if sect != ENDOFCHAIN: - raise IOError("incorrect last sector index in OLE stream") + raise OSError("incorrect last sector index in OLE stream") def get_type(self, filename): """ @@ -2168,7 +2231,10 @@ class OleFileIO: # FILETIME is a 64-bit int: "number of 100ns periods # since Jan 1,1601". if convert_time and id not in no_conversion: - debug("Converting property #%d to python datetime, value=%d=%fs" % (id, value, float(value) / 10000000)) + debug( + "Converting property #%d to python datetime, value=%d=%fs" + % (id, value, float(value) / 10000000) + ) # convert FILETIME to Python datetime.datetime # inspired from http://code.activestate.com/recipes/511425-filetime-to-datetime/ _FILETIME_null_date = datetime.datetime(1601, 1, 1, 0, 0, 0) @@ -2236,7 +2302,6 @@ class OleFileIO: # storage file. if __name__ == "__main__disabled": - # Standard Libraries import sys @@ -2288,7 +2353,35 @@ For more information, see http://www.decalage.info/olefile v = v[:50] if isinstance(v, bytes): # quick and dirty binary check: - for c in (1, 2, 3, 4, 5, 6, 7, 11, 12, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31): + for c in ( + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 11, + 12, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + ): if c in bytearray(v): v = "(binary data)" break @@ -2392,9 +2485,15 @@ def find_rc4_passinfo_xls(filename, stream, hashcat_format=True): if type == 0x2F: # FILEPASS if length == 4: # Excel 95 XOR obfuscation - sys.stderr.write("%s : Excel 95 XOR obfuscation detected, key : %s, hash : %s\n" % (filename, binascii.hexlify(data[0:2]), binascii.hexlify(data[2:4]))) + sys.stderr.write( + "%s : Excel 95 XOR obfuscation detected, key : %s, hash : %s\n" + % (filename, binascii.hexlify(data[0:2]), binascii.hexlify(data[2:4])) + ) elif data[0:2] == b"\x00\x00": # XOR obfuscation - sys.stderr.write("%s : XOR obfuscation detected, key : %s, hash : %s\n" % (filename, binascii.hexlify(data[2:4]), binascii.hexlify(data[4:6]))) + sys.stderr.write( + "%s : XOR obfuscation detected, key : %s, hash : %s\n" + % (filename, binascii.hexlify(data[2:4]), binascii.hexlify(data[4:6])) + ) elif data[0:6] == b"\x01\x00\x01\x00\x01\x00": # RC4 encryption header structure data = data[6:] @@ -2402,7 +2501,9 @@ def find_rc4_passinfo_xls(filename, stream, hashcat_format=True): verifier = data[16:32] verifierHash = data[32:48] return (salt, verifier, verifierHash) - elif data[0:4] == b"\x01\x00\x02\x00" or data[0:4] == b"\x01\x00\x03\x00" or data[0:4] == b"\x01\x00\x04\x00": + elif ( + data[0:4] == b"\x01\x00\x02\x00" or data[0:4] == b"\x01\x00\x03\x00" or data[0:4] == b"\x01\x00\x04\x00" + ): # If RC4 CryptoAPI encryption is used, certain storages and streams are stored in Encryption Stream stm = StringIO(data) stm.read(2) # unused @@ -2455,12 +2556,26 @@ def find_rc4_passinfo_xls(filename, stream, hashcat_format=True): if hashcat_format: sys.stdout.write( - "$oldoffice$%s*%s*%s*%s%s\n" % (typ, binascii.hexlify(salt).decode("ascii"), binascii.hexlify(encryptedVerifier).decode("ascii"), binascii.hexlify(encryptedVerifierHash).decode("ascii"), second_block_extra) + "$oldoffice$%s*%s*%s*%s%s\n" + % ( + typ, + binascii.hexlify(salt).decode("ascii"), + binascii.hexlify(encryptedVerifier).decode("ascii"), + binascii.hexlify(encryptedVerifierHash).decode("ascii"), + second_block_extra, + ) ) else: sys.stdout.write( "%s:$oldoffice$%s*%s*%s*%s%s\n" - % (os.path.basename(filename), typ, binascii.hexlify(salt).decode("ascii"), binascii.hexlify(encryptedVerifier).decode("ascii"), binascii.hexlify(encryptedVerifierHash).decode("ascii"), second_block_extra) + % ( + os.path.basename(filename), + typ, + binascii.hexlify(salt).decode("ascii"), + binascii.hexlify(encryptedVerifier).decode("ascii"), + binascii.hexlify(encryptedVerifierHash).decode("ascii"), + second_block_extra, + ) ) return None @@ -2486,7 +2601,9 @@ def find_table(filename, stream): if F == 1 and M == 1: stream.read(2) # unused i_key = stream.read(4) - sys.stderr.write("%s : XOR obfuscation detected, Password Verifier : %s\n" % (filename, binascii.hexlify(i_key))) + sys.stderr.write( + "%s : XOR obfuscation detected, Password Verifier : %s\n" % (filename, binascii.hexlify(i_key)) + ) return "none" if F == 0: sys.stderr.write("%s : Document is not encrypted!\n" % (filename)) @@ -2576,12 +2693,28 @@ def find_rc4_passinfo_doc(filename, stream, hashcat_format=True): if hashcat_format: sys.stdout.write( - "$oldoffice$%s*%s*%s*%s%s%s\n" % (typ, binascii.hexlify(salt).decode("ascii"), binascii.hexlify(encryptedVerifier).decode("ascii"), binascii.hexlify(encryptedVerifierHash).decode("ascii"), second_block_extra, summary_extra) + "$oldoffice$%s*%s*%s*%s%s%s\n" + % ( + typ, + binascii.hexlify(salt).decode("ascii"), + binascii.hexlify(encryptedVerifier).decode("ascii"), + binascii.hexlify(encryptedVerifierHash).decode("ascii"), + second_block_extra, + summary_extra, + ) ) else: sys.stdout.write( "%s:$oldoffice$%s*%s*%s*%s%s%s\n" - % (os.path.basename(filename), typ, binascii.hexlify(salt).decode("ascii"), binascii.hexlify(encryptedVerifier).decode("ascii"), binascii.hexlify(encryptedVerifierHash).decode("ascii"), second_block_extra, summary_extra) + % ( + os.path.basename(filename), + typ, + binascii.hexlify(salt).decode("ascii"), + binascii.hexlify(encryptedVerifier).decode("ascii"), + binascii.hexlify(encryptedVerifierHash).decode("ascii"), + second_block_extra, + summary_extra, + ) ) else: @@ -2689,11 +2822,27 @@ def find_rc4_passinfo_ppt(filename, stream, offset, hashcat_format=True): stream.seek(offset_cur) # to be safe, seek back to old pos (not really needed) if hashcat_format: - sys.stdout.write("$oldoffice$%s*%s*%s*%s%s\n" % (typ, binascii.hexlify(salt).decode("ascii"), binascii.hexlify(encryptedVerifier).decode("ascii"), binascii.hexlify(encryptedVerifierHash).decode("ascii"), second_block_extra)) + sys.stdout.write( + "$oldoffice$%s*%s*%s*%s%s\n" + % ( + typ, + binascii.hexlify(salt).decode("ascii"), + binascii.hexlify(encryptedVerifier).decode("ascii"), + binascii.hexlify(encryptedVerifierHash).decode("ascii"), + second_block_extra, + ) + ) else: sys.stdout.write( "%s:$oldoffice$%s*%s*%s*%s%s\n" - % (os.path.basename(filename), typ, binascii.hexlify(salt).decode("ascii"), binascii.hexlify(encryptedVerifier).decode("ascii"), binascii.hexlify(encryptedVerifierHash).decode("ascii"), second_block_extra) + % ( + os.path.basename(filename), + typ, + binascii.hexlify(salt).decode("ascii"), + binascii.hexlify(encryptedVerifier).decode("ascii"), + binascii.hexlify(encryptedVerifierHash).decode("ascii"), + second_block_extra, + ) ) return True else: @@ -2704,8 +2853,11 @@ def find_rc4_passinfo_ppt(filename, stream, offset, hashcat_format=True): def find_rc4_passinfo_ppt_bf(filename, stream, offset, hashcat_format=True): """We don't use stream and offset anymore! The current method is a bit slow for large files.""" sys.stderr.write("This can take a while, please wait.\n") - stream = open(filename, "rb") - original = stream.read() + f = open(filename, "rb") + try: + original = f.read() + finally: + f.close() found = False for i in range(0, len(original)): data = original[i : i + 384] @@ -2774,11 +2926,27 @@ def find_rc4_passinfo_ppt_bf(filename, stream, offset, hashcat_format=True): found = True if hashcat_format: - sys.stdout.write("$oldoffice$%s*%s*%s*%s%s\n" % (typ, binascii.hexlify(salt).decode("ascii"), binascii.hexlify(encryptedVerifier).decode("ascii"), binascii.hexlify(encryptedVerifierHash).decode("ascii"), second_block_extra)) + sys.stdout.write( + "$oldoffice$%s*%s*%s*%s%s\n" + % ( + typ, + binascii.hexlify(salt).decode("ascii"), + binascii.hexlify(encryptedVerifier).decode("ascii"), + binascii.hexlify(encryptedVerifierHash).decode("ascii"), + second_block_extra, + ) + ) else: sys.stdout.write( "%s:$oldoffice$%s*%s*%s*%s%s\n" - % (os.path.basename(filename), typ, binascii.hexlify(salt).decode("ascii"), binascii.hexlify(encryptedVerifier).decode("ascii"), binascii.hexlify(encryptedVerifierHash).decode("ascii"), second_block_extra) + % ( + os.path.basename(filename), + typ, + binascii.hexlify(salt).decode("ascii"), + binascii.hexlify(encryptedVerifier).decode("ascii"), + binascii.hexlify(encryptedVerifierHash).decode("ascii"), + second_block_extra, + ) ) if not found: @@ -2789,7 +2957,11 @@ def process_access_2007_older_crypto(filename, hashcat_format=True): """Dirty hash extractor for MS Office 2007 .accdb files which use CryptoAPI based encryption.""" - original = open(filename, "rb").read() + f = open(filename, "rb") + try: + original = f.read() + finally: + f.close() for i in range(0, len(original)): data = original[i:40960] # is this limit on data reasonable? @@ -2863,11 +3035,27 @@ def process_access_2007_older_crypto(filename, hashcat_format=True): second_block_extra = "*%s" % binascii.hexlify(second_block_bytes).decode("ascii") if hashcat_format: - sys.stdout.write("$oldoffice$%s*%s*%s*%s%s\n" % (typ, binascii.hexlify(salt).decode("ascii"), binascii.hexlify(encryptedVerifier).decode("ascii"), binascii.hexlify(encryptedVerifierHash).decode("ascii"), second_block_extra)) + sys.stdout.write( + "$oldoffice$%s*%s*%s*%s%s\n" + % ( + typ, + binascii.hexlify(salt).decode("ascii"), + binascii.hexlify(encryptedVerifier).decode("ascii"), + binascii.hexlify(encryptedVerifierHash).decode("ascii"), + second_block_extra, + ) + ) else: sys.stdout.write( "%s:$oldoffice$%s*%s*%s*%s%s\n" - % (os.path.basename(filename), typ, binascii.hexlify(salt).decode("ascii"), binascii.hexlify(encryptedVerifier).decode("ascii"), binascii.hexlify(encryptedVerifierHash).decode("ascii"), second_block_extra) + % ( + os.path.basename(filename), + typ, + binascii.hexlify(salt).decode("ascii"), + binascii.hexlify(encryptedVerifier).decode("ascii"), + binascii.hexlify(encryptedVerifierHash).decode("ascii"), + second_block_extra, + ) ) break @@ -2930,7 +3118,15 @@ def process_new_office(filename, hashcat_format=True): if hashcat_format: sys.stdout.write( "$office$*%d*%d*%d*%d*%s*%s*%s\n" - % (2007, verifierHashSize, keySize, saltSize, binascii.hexlify(salt).decode("ascii"), binascii.hexlify(encryptedVerifier).decode("ascii"), binascii.hexlify(encryptedVerifierHash)[0:64].decode("ascii")) + % ( + 2007, + verifierHashSize, + keySize, + saltSize, + binascii.hexlify(salt).decode("ascii"), + binascii.hexlify(encryptedVerifier).decode("ascii"), + binascii.hexlify(encryptedVerifierHash)[0:64].decode("ascii"), + ) ) else: sys.stdout.write( @@ -2969,11 +3165,15 @@ def xml_metadata_parser(data, filename, hashcat_format=True): elif hashAlgorithm == "SHA512": version = 2013 else: - sys.stderr.write("%s uses un-supported hashing algorithm %s, please file a bug! \n" % (filename, hashAlgorithm)) + sys.stderr.write( + "%s uses un-supported hashing algorithm %s, please file a bug! \n" % (filename, hashAlgorithm) + ) return -3 cipherAlgorithm = node.attrib.get("cipherAlgorithm") if not cipherAlgorithm.find("AES") > -1: - sys.stderr.write("%s uses un-supported cipher algorithm %s, please file a bug! \n" % (filename, cipherAlgorithm)) + sys.stderr.write( + "%s uses un-supported cipher algorithm %s, please file a bug! \n" % (filename, cipherAlgorithm) + ) return -4 saltValue = node.attrib.get("saltValue") @@ -2987,15 +3187,42 @@ def xml_metadata_parser(data, filename, hashcat_format=True): if PY3: saltAscii = binascii.hexlify(base64.decodebytes(saltValue.encode())).decode("ascii") - encryptedVerifierHashAscii = binascii.hexlify(base64.decodebytes(encryptedVerifierHashInput.encode())).decode("ascii") + encryptedVerifierHashAscii = binascii.hexlify( + base64.decodebytes(encryptedVerifierHashInput.encode()) + ).decode("ascii") else: saltAscii = binascii.hexlify(base64.decodestring(saltValue.encode())).decode("ascii") - encryptedVerifierHashAscii = binascii.hexlify(base64.decodestring(encryptedVerifierHashInput.encode())).decode("ascii") + encryptedVerifierHashAscii = binascii.hexlify( + base64.decodestring(encryptedVerifierHashInput.encode()) + ).decode("ascii") if hashcat_format: - sys.stdout.write("$office$*%d*%d*%d*%d*%s*%s*%s\n" % (version, int(spinCount), int(keyBits), int(saltSize), saltAscii, encryptedVerifierHashAscii, encryptedVerifierHashValue[0:64].decode("ascii"))) + sys.stdout.write( + "$office$*%d*%d*%d*%d*%s*%s*%s\n" + % ( + version, + int(spinCount), + int(keyBits), + int(saltSize), + saltAscii, + encryptedVerifierHashAscii, + encryptedVerifierHashValue[0:64].decode("ascii"), + ) + ) else: - sys.stdout.write("%s:$office$*%d*%d*%d*%d*%s*%s*%s\n" % (os.path.basename(filename), version, int(spinCount), int(keyBits), int(saltSize), saltAscii, encryptedVerifierHashAscii, encryptedVerifierHashValue[0:64].decode("ascii"))) + sys.stdout.write( + "%s:$office$*%d*%d*%d*%d*%s*%s*%s\n" + % ( + os.path.basename(filename), + version, + int(spinCount), + int(keyBits), + int(saltSize), + saltAscii, + encryptedVerifierHashAscii, + encryptedVerifierHashValue[0:64].decode("ascii"), + ) + ) return 0 @@ -3069,111 +3296,131 @@ def process_file(filename, hashcat_format=True): # Open OLE file: ole = OleFileIO(filename) - stream = None - - # find "summary" streams - global have_summary, summary - have_summary = False - summary = [] - - for streamname in ole.listdir(): - streamname = streamname[-1] - if streamname[0] == "\005": - have_summary = True - props = ole.getproperties(streamname) - for k, v in props.items(): - if v is None: - continue - if not PY3: - # We are only interested in strings - if not isinstance(v, unicode): # pyright: ignore[reportUndefinedVariable] - continue - else: - if not isinstance(v, str): # We are only interested in strings - continue - v = remove_html_tags(v) - v = v.replace(":", "") - v = remove_extra_spaces(v) - # words = v.split() - # words = filter(lambda x: len(x) < 20, words) - # v = " ".join(words) - summary.append(v) - summary = " ".join(summary) - summary = remove_extra_spaces(summary) - - if ["EncryptionInfo"] in ole.listdir(): - # process Office 2003 / 2010 / 2013 files - return process_new_office(filename) - if ["Workbook"] in ole.listdir(): - stream = "Workbook" - elif ["Book"] in ole.listdir(): - stream = "Book" - elif ["WordDocument"] in ole.listdir(): - typ = 1 - sdoc = ole.openstream("WordDocument") - stream = find_table(filename, sdoc) - if stream == "none": - return 5 - - elif ["PowerPoint Document"] in ole.listdir(): - stream = "Current User" - else: - sys.stderr.write("%s : No supported streams found\n" % filename) - return 2 - try: - workbookStream = ole.openstream(stream) - except: - # Standard Libraries - import traceback + stream = None - traceback.print_exc() - sys.stderr.write("%s : stream %s not found!\n" % (filename, stream)) - return 2 + # find "summary" streams + global have_summary, summary + have_summary = False + summary = [] - if workbookStream is None: - sys.stderr.write("%s : Error opening stream, %s\n" % filename) - (filename, stream) - return 3 + for streamname in ole.listdir(): + streamname = streamname[-1] + if streamname[0] == "\005": + have_summary = True + props = ole.getproperties(streamname) + for k, v in props.items(): + if v is None: + continue + if not PY3: + # We are only interested in strings + if not isinstance(v, unicode): # pyright: ignore[reportUndefinedVariable] + continue + else: + if not isinstance(v, str): # We are only interested in strings + continue + v = remove_html_tags(v) + v = v.replace(":", "") + v = remove_extra_spaces(v) + # words = v.split() + # words = filter(lambda x: len(x) < 20, words) + # v = " ".join(words) + summary.append(v) + summary = " ".join(summary) + summary = remove_extra_spaces(summary) - if stream == "Workbook" or stream == "Book": - typ = 0 - passinfo = find_rc4_passinfo_xls(filename, workbookStream) - if passinfo is None: - return 4 - elif stream == "0Table" or stream == "1Table": - passinfo = find_rc4_passinfo_doc(filename, workbookStream) - if passinfo is None: - return 4 - else: - sppt = ole.openstream("Current User") - offset = find_ppt_type(filename, sppt) - sppt = ole.openstream("PowerPoint Document") - ret = find_rc4_passinfo_ppt(filename, sppt, offset) - if not ret: - find_rc4_passinfo_ppt_bf(filename, sppt, offset) + if ["EncryptionInfo"] in ole.listdir(): + # process Office 2003 / 2010 / 2013 files + return process_new_office(filename) + if ["Workbook"] in ole.listdir(): + stream = "Workbook" + elif ["Book"] in ole.listdir(): + stream = "Book" + elif ["WordDocument"] in ole.listdir(): + typ = 1 + sdoc = ole.openstream("WordDocument") + stream = find_table(filename, sdoc) + if stream == "none": + return 5 - return 6 + elif ["PowerPoint Document"] in ole.listdir(): + stream = "Current User" + else: + sys.stderr.write("%s : No supported streams found\n" % filename) + return 2 - (salt, verifier, verifierHash) = passinfo + try: + workbookStream = ole.openstream(stream) + except: + # Standard Libraries + import traceback - summary_extra = "" - # if have_summary: - # summary_extra = ":::%s::%s" % (summary, filename) + traceback.print_exc() + sys.stderr.write("%s : stream %s not found!\n" % (filename, stream)) + return 2 - if hashcat_format: - sys.stdout.write("$oldoffice$%s*%s*%s*%s%s\n" % (typ, binascii.hexlify(salt).decode("ascii"), binascii.hexlify(verifier).decode("ascii"), binascii.hexlify(verifierHash).decode("ascii"), summary_extra)) - else: - sys.stdout.write("%s:$oldoffice$%s*%s*%s*%s%s\n" % (os.path.basename(filename), typ, binascii.hexlify(salt).decode("ascii"), binascii.hexlify(verifier).decode("ascii"), binascii.hexlify(verifierHash).decode("ascii"), summary_extra)) + if workbookStream is None: + sys.stderr.write("%s : Error opening stream, %s\n" % filename) + (filename, stream) + return 3 - workbookStream.close() - ole.close() + if stream == "Workbook" or stream == "Book": + typ = 0 + passinfo = find_rc4_passinfo_xls(filename, workbookStream) + if passinfo is None: + return 4 + elif stream == "0Table" or stream == "1Table": + passinfo = find_rc4_passinfo_doc(filename, workbookStream) + if passinfo is None: + return 4 + else: + sppt = ole.openstream("Current User") + offset = find_ppt_type(filename, sppt) + sppt = ole.openstream("PowerPoint Document") + ret = find_rc4_passinfo_ppt(filename, sppt, offset) + if not ret: + find_rc4_passinfo_ppt_bf(filename, sppt, offset) - return 0 + return 6 + + (salt, verifier, verifierHash) = passinfo + + summary_extra = "" + # if have_summary: + # summary_extra = ":::%s::%s" % (summary, filename) + + if hashcat_format: + sys.stdout.write( + "$oldoffice$%s*%s*%s*%s%s\n" + % ( + typ, + binascii.hexlify(salt).decode("ascii"), + binascii.hexlify(verifier).decode("ascii"), + binascii.hexlify(verifierHash).decode("ascii"), + summary_extra, + ) + ) + else: + sys.stdout.write( + "%s:$oldoffice$%s*%s*%s*%s%s\n" + % ( + os.path.basename(filename), + typ, + binascii.hexlify(salt).decode("ascii"), + binascii.hexlify(verifier).decode("ascii"), + binascii.hexlify(verifierHash).decode("ascii"), + summary_extra, + ) + ) + + workbookStream.close() + + return 0 + finally: + ole.close() def extract_file_encryption_hash(filename, hashcat_format=True): - with io.StringIO() as buf, redirect_stdout(buf): process_file(filename, hashcat_format) return buf.getvalue().strip() @@ -3190,4 +3437,4 @@ def extract_file_encryption_hash(filename, hashcat_format=True): # if not PY3: # ret = process_file_string(sys.argv[i].decode("utf8")) # else: -# ret = process_file_string(sys.argv[i]) \ No newline at end of file +# ret = process_file_string(sys.argv[i]) diff --git a/libs/file_enrichment_modules/file_enrichment_modules/parquet/analyzer.py b/libs/file_enrichment_modules/file_enrichment_modules/parquet/analyzer.py index 6ac468b..2b6b7fe 100644 --- a/libs/file_enrichment_modules/file_enrichment_modules/parquet/analyzer.py +++ b/libs/file_enrichment_modules/file_enrichment_modules/parquet/analyzer.py @@ -3,14 +3,13 @@ import csv import tempfile import pyarrow.parquet as pq -import structlog +from common.logger import get_logger from common.models import EnrichmentResult, Transform from common.state_helpers import get_file_enriched from common.storage import StorageMinio - from file_enrichment_modules.module_loader import EnrichmentModule -logger = structlog.get_logger(module=__name__) +logger = get_logger(__name__) class ParquetFileParser(EnrichmentModule): @@ -21,15 +20,12 @@ class ParquetFileParser(EnrichmentModule): # the workflows this module should automatically run in self.workflows = ["default"] - def should_process(self, object_id: str) -> bool: + def should_process(self, object_id: str, file_path: str | None = None) -> bool: """Determine if this module should run.""" file_enriched = get_file_enriched(object_id) # Check if file is a Parquet file - should_run = "apache parquet" in file_enriched.magic_type.lower() - - # logger.debug(f"ParquetFileParser should_run: {should_run}") - return should_run + return "apache parquet" in file_enriched.magic_type.lower() def _get_parquet_schema_info(self, schema): """Extract readable schema information from PyArrow schema.""" @@ -38,135 +34,166 @@ class ParquetFileParser(EnrichmentModule): schema_info.append({"name": field.name, "type": str(field.type), "nullable": field.nullable}) return schema_info - def process(self, object_id: str) -> EnrichmentResult | None: - """Process Parquet file.""" + def _analyze_parquet(self, file_path: str, file_enriched) -> EnrichmentResult | None: + """Analyze Parquet file and generate enrichment result. + + Args: + file_path: Path to the Parquet file to analyze + file_enriched: File enrichment data + + Returns: + EnrichmentResult or None if analysis fails + """ + enrichment_result = EnrichmentResult(module_name=self.name, dependencies=self.dependencies) + transforms = [] + + try: + # Read the Parquet file + parquet_file = pq.ParquetFile(file_path) + + # Get metadata + file_metadata = parquet_file.metadata + num_rows = file_metadata.num_rows + num_row_groups = file_metadata.num_row_groups + schema = parquet_file.schema.to_arrow_schema() + schema_info = self._get_parquet_schema_info(schema) + + # Generate summary report + report_lines = [] + + # File summary + report_lines.append("# Parquet File Summary") + report_lines.append(f"\nFile name: {file_enriched.file_name}") + report_lines.append(f"Total rows: {num_rows}") + report_lines.append(f"Row groups: {num_row_groups}") + + # Schema information + report_lines.append("\n## Schema") + report_lines.append("\n| Column | Type | Nullable |") + report_lines.append("| ------ | ---- | -------- |") + for field in schema_info: + report_lines.append(f"| {field['name']} | {field['type']} | {field['nullable']} |") + + # Sample data - first 10 rows + first_rows = parquet_file.read_row_group(0).to_pandas().head(10) + report_lines.append("\n## Sample Data (First 10 rows)") + + # Manually create markdown table to avoid tabulate dependency + if not first_rows.empty: + # Add column headers + report_lines.append("\n| " + " | ".join(str(col) for col in first_rows.columns) + " |") + # Add separator line + report_lines.append("| " + " | ".join(["---"] * len(first_rows.columns)) + " |") + # Add data rows + for _, row in first_rows.iterrows(): + # Handle different data types and null values + row_values = [] + for val in row: + if val is None: + row_values.append("") + elif isinstance(val, (int, float, bool)): + row_values.append(str(val)) + else: + # Escape pipe characters in string values + row_values.append(str(val).replace("|", "\\|")) + report_lines.append("| " + " | ".join(row_values) + " |") + else: + report_lines.append("\n*No data available*") + + # Row group information + report_lines.append("\n## Row Group Details") + report_lines.append("\n| Group | Rows | Size (bytes) |") + report_lines.append("| ----- | ---- | ------------ |") + for i in range(num_row_groups): + rg = file_metadata.row_group(i) + report_lines.append(f"| {i} | {rg.num_rows} | {rg.total_byte_size} |") + + # Create summary report transform + with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8") as tmp_report: + tmp_report.write("\n".join(report_lines)) + tmp_report.flush() + report_id = self.storage.upload_file(tmp_report.name) + + transforms.append( + Transform( + type="finding_summary", + object_id=f"{report_id}", + metadata={ + "file_name": f"{file_enriched.file_name}.md", + "display_type_in_dashboard": "markdown", + "default_display": True, + }, + ) + ) + + # Convert to CSV efficiently using chunking + with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8", newline="") as tmp_csv: + writer = csv.writer(tmp_csv) + + # Write header (column names) + writer.writerow([field.name for field in schema]) + + # Process each row group to handle large files + chunksize = 100000 # Number of rows to process at a time + for i in range(num_row_groups): + # Read a row group + table = parquet_file.read_row_group(i) + + # Process in batches to minimize memory usage + for batch in table.to_batches(max_chunksize=chunksize): + # Convert to pandas for easier row iteration + batch_df = batch.to_pandas() + + # Write rows + writer.writerows(batch_df.values.tolist()) + + # Free memory + del table + + tmp_csv.flush() + csv_id = self.storage.upload_file(tmp_csv.name) + + transforms.append( + Transform( + type="parquet_to_csv", + object_id=f"{csv_id}", + metadata={ + "file_name": f"{file_enriched.file_name}.csv", + "offer_as_download": True, + }, + ) + ) + + enrichment_result.transforms = transforms + return enrichment_result + + except Exception as e: + logger.exception(e, message=f"Error analyzing Parquet file for {file_enriched.file_name}") + return None + + def process(self, object_id: str, file_path: str | None = None) -> EnrichmentResult | None: + """Process Parquet file. + + Args: + object_id: The object ID of the file + file_path: Optional path to already downloaded file + + Returns: + EnrichmentResult or None if processing fails + """ try: file_enriched = get_file_enriched(object_id) - enrichment_result = EnrichmentResult(module_name=self.name, dependencies=self.dependencies) - transforms = [] - with self.storage.download(file_enriched.object_id) as temp_file: - # Read the Parquet file - parquet_file = pq.ParquetFile(temp_file.name) - - # Get metadata - file_metadata = parquet_file.metadata - num_rows = file_metadata.num_rows - num_row_groups = file_metadata.num_row_groups - schema = parquet_file.schema.to_arrow_schema() - schema_info = self._get_parquet_schema_info(schema) - - # Generate summary report - report_lines = [] - - # File summary - report_lines.append("# Parquet File Summary") - report_lines.append(f"\nFile name: {file_enriched.file_name}") - report_lines.append(f"Total rows: {num_rows}") - report_lines.append(f"Row groups: {num_row_groups}") - - # Schema information - report_lines.append("\n## Schema") - report_lines.append("\n| Column | Type | Nullable |") - report_lines.append("| ------ | ---- | -------- |") - for field in schema_info: - report_lines.append(f"| {field['name']} | {field['type']} | {field['nullable']} |") - - # Sample data - first 10 rows - first_rows = parquet_file.read_row_group(0).to_pandas().head(10) - report_lines.append("\n## Sample Data (First 10 rows)") - - # Manually create markdown table to avoid tabulate dependency - if not first_rows.empty: - # Add column headers - report_lines.append("\n| " + " | ".join(str(col) for col in first_rows.columns) + " |") - # Add separator line - report_lines.append("| " + " | ".join(["---"] * len(first_rows.columns)) + " |") - # Add data rows - for _, row in first_rows.iterrows(): - # Handle different data types and null values - row_values = [] - for val in row: - if val is None: - row_values.append("") - elif isinstance(val, (int, float, bool)): - row_values.append(str(val)) - else: - # Escape pipe characters in string values - row_values.append(str(val).replace("|", "\\|")) - report_lines.append("| " + " | ".join(row_values) + " |") - else: - report_lines.append("\n*No data available*") - - # Row group information - report_lines.append("\n## Row Group Details") - report_lines.append("\n| Group | Rows | Size (bytes) |") - report_lines.append("| ----- | ---- | ------------ |") - for i in range(num_row_groups): - rg = file_metadata.row_group(i) - report_lines.append(f"| {i} | {rg.num_rows} | {rg.total_byte_size} |") - - # Create summary report transform - with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8") as tmp_report: - tmp_report.write("\n".join(report_lines)) - tmp_report.flush() - report_id = self.storage.upload_file(tmp_report.name) - - transforms.append( - Transform( - type="finding_summary", - object_id=f"{report_id}", - metadata={ - "file_name": f"{file_enriched.file_name}.md", - "display_type_in_dashboard": "markdown", - "default_display": True, - }, - ) - ) - - # Convert to CSV efficiently using chunking - with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8", newline="") as tmp_csv: - writer = csv.writer(tmp_csv) - - # Write header (column names) - writer.writerow([field.name for field in schema]) - - # Process each row group to handle large files - chunksize = 100000 # Number of rows to process at a time - for i in range(num_row_groups): - # Read a row group - table = parquet_file.read_row_group(i) - - # Process in batches to minimize memory usage - for batch in table.to_batches(max_chunksize=chunksize): - # Convert to pandas for easier row iteration - batch_df = batch.to_pandas() - - # Write rows - writer.writerows(batch_df.values.tolist()) - - # Free memory - del table - - tmp_csv.flush() - csv_id = self.storage.upload_file(tmp_csv.name) - - transforms.append( - Transform( - type="parquet_to_csv", - object_id=f"{csv_id}", - metadata={ - "file_name": f"{file_enriched.file_name}.csv", - "offer_as_download": True, - }, - ) - ) - - enrichment_result.transforms = transforms - return enrichment_result + # Use provided file_path if available, otherwise download + if file_path: + return self._analyze_parquet(file_path, file_enriched) + else: + with self.storage.download(file_enriched.object_id) as temp_file: + return self._analyze_parquet(temp_file.name, file_enriched) except Exception as e: logger.exception(e, message="Error processing Parquet file") + return None def create_enrichment_module() -> EnrichmentModule: diff --git a/libs/file_enrichment_modules/file_enrichment_modules/pdf/analyzer.py b/libs/file_enrichment_modules/file_enrichment_modules/pdf/analyzer.py index d32d827..323271e 100644 --- a/libs/file_enrichment_modules/file_enrichment_modules/pdf/analyzer.py +++ b/libs/file_enrichment_modules/file_enrichment_modules/pdf/analyzer.py @@ -2,17 +2,15 @@ from datetime import datetime from typing import Any -import structlog -from common.helpers import escape_markdown +from common.logger import get_logger from common.models import EnrichmentResult, FileObject, Finding, FindingCategory, FindingOrigin from common.state_helpers import get_file_enriched from common.storage import StorageMinio -from pypdf import PdfReader - from file_enrichment_modules.module_loader import EnrichmentModule from file_enrichment_modules.pdf.pdf2john import PdfParser +from pypdf import PdfReader -logger = structlog.get_logger(module=__name__) +logger = get_logger(__name__) def parse_pdf_file(file_path: str) -> dict[str, Any]: @@ -97,24 +95,23 @@ class PDFAnalyzer(EnrichmentModule): # the workflows this module should automatically run in self.workflows = ["default"] - def should_process(self, object_id: str) -> bool: + def should_process(self, object_id: str, file_path: str | None = None) -> bool: # Get the current file_enriched from the database backend file_enriched = get_file_enriched(object_id) + return "pdf document" in file_enriched.magic_type.lower() - if file_enriched.magic_type: - should_run = "pdf document" in file_enriched.magic_type.lower() - else: - should_run = False + def _analyze_pdf(self, file_path: str, file_enriched) -> EnrichmentResult | None: + """Analyze PDF file and generate enrichment result. - logger.debug(f"PDFAnalyzer should_run: {should_run}") - return should_run + Args: + file_path: Path to the PDF file to analyze + file_enriched: File enrichment data - def process(self, object_id: str) -> EnrichmentResult | None: - # get the current `file_enriched` from the database backend - file_enriched = get_file_enriched(object_id) - - with self.storage.download(file_enriched.object_id) as file: - analysis = parse_pdf_file(file.name) + Returns: + EnrichmentResult or None if analysis fails + """ + try: + analysis = parse_pdf_file(file_path) enrichment_result = EnrichmentResult(module_name=self.name) enrichment_result.results = analysis @@ -125,7 +122,7 @@ class PDFAnalyzer(EnrichmentModule): # Encrypted PDF The document is encrypted. Attempt to crack it using the following hash: ``` -{escape_markdown(encryption_hash)} +{encryption_hash} ``` """ display_data = FileObject(type="finding_summary", metadata={"summary": summary_markdown}) @@ -148,6 +145,35 @@ The document is encrypted. Attempt to crack it using the following hash: return enrichment_result + except Exception as e: + logger.exception(e, message=f"Error analyzing PDF file for {file_enriched.file_name}") + return None + + def process(self, object_id: str, file_path: str | None = None) -> EnrichmentResult | None: + """Process PDF file. + + Args: + object_id: The object ID of the file + file_path: Optional path to already downloaded file + + Returns: + EnrichmentResult or None if processing fails + """ + try: + # get the current `file_enriched` from the database backend + file_enriched = get_file_enriched(object_id) + + # Use provided file_path if available, otherwise download + if file_path: + return self._analyze_pdf(file_path, file_enriched) + else: + with self.storage.download(file_enriched.object_id) as file: + return self._analyze_pdf(file.name, file_enriched) + + except Exception as e: + logger.exception(e, message="Error processing PDF file") + return None + def create_enrichment_module() -> EnrichmentModule: return PDFAnalyzer() diff --git a/libs/file_enrichment_modules/file_enrichment_modules/pdf/pdf2john.py b/libs/file_enrichment_modules/file_enrichment_modules/pdf/pdf2john.py index 28ccfea..992786e 100644 --- a/libs/file_enrichment_modules/file_enrichment_modules/pdf/pdf2john.py +++ b/libs/file_enrichment_modules/file_enrichment_modules/pdf/pdf2john.py @@ -40,7 +40,7 @@ class PdfParser: try: self.pdf_spec = psr.findall(self.encrypted)[0] except IndexError: - sys.stderr.write("%s is not a PDF file!\n" % file_name) + sys.stderr.write(f"{file_name} is not a PDF file!\n") self.process = False def parse(self): @@ -51,7 +51,7 @@ class PdfParser: trailer = self.get_trailer() except RuntimeError: e = sys.exc_info()[1] - sys.stderr.write("%s : %s\n" % (self.file_name, str(e))) + sys.stderr.write(f"{self.file_name} : {e!s}\n") return # print >> sys.stderr, trailer object_id = self.get_object_id(b"Encrypt", trailer) @@ -65,8 +65,8 @@ class PdfParser: rr = re.compile(rb"\/R \d") try: v = dr.findall(vr.findall(encryption_dictionary)[0])[0] - except IndexError: - raise RuntimeError("Could not find /V") + except IndexError as err: + raise RuntimeError("Could not find /V") from err r = dr.findall(rr.findall(encryption_dictionary)[0])[0] lr = re.compile(rb"\/Length \d+") longest = 0 @@ -80,9 +80,9 @@ class PdfParser: pr = re.compile(rb"\/P -?\d+") try: p = pr.findall(encryption_dictionary)[0] - except IndexError: + except IndexError as err: # print >> sys.stderr, "** dict:", encryption_dictionary - raise RuntimeError("Could not find /P") + raise RuntimeError("Could not find /P") from err pr = re.compile(rb"-?\d+") p = pr.findall(p)[0] meta = "1" if self.is_meta_data_encrypted(encryption_dictionary) else "0" @@ -94,11 +94,10 @@ class PdfParser: idr = re.compile(rb"\/ID\s*\[\s*\(\w+\)\s*\(\w+\)\s*\]") try: i_d = idr.findall(trailer)[0] # id key word - except IndexError: + except IndexError as err: # print >> sys.stderr, "** idr:", idr # print >> sys.stderr, "** trailer:", trailer - raise RuntimeError("Could not find /ID tag") - return + raise RuntimeError("Could not find /ID tag") from err idr = re.compile(rb"<\w+>") try: i_d = idr.findall(trailer)[0] diff --git a/libs/file_enrichment_modules/file_enrichment_modules/pe/analyzer.py b/libs/file_enrichment_modules/file_enrichment_modules/pe/analyzer.py index 7ac5bd1..59fdcbc 100644 --- a/libs/file_enrichment_modules/file_enrichment_modules/pe/analyzer.py +++ b/libs/file_enrichment_modules/file_enrichment_modules/pe/analyzer.py @@ -3,15 +3,14 @@ from pathlib import Path from typing import Any import lief -import structlog import yara_x +from common.logger import get_logger from common.models import EnrichmentResult from common.state_helpers import get_file_enriched from common.storage import StorageMinio - from file_enrichment_modules.module_loader import EnrichmentModule -logger = structlog.get_logger(module=__name__) +logger = get_logger(__name__) # # simpler but may have additional information we often don't care about @@ -49,7 +48,7 @@ def parse_pe_file(file_path: str) -> dict[str, Any]: binary = lief.parse(file_path) if binary is None: - lief.lief_errors.parsing_error + raise lief.lief_errors.parsing_error("Failed to parse PE file") # Initialize the result dictionary result = { @@ -69,7 +68,7 @@ def parse_pe_file(file_path: str) -> dict[str, Any]: is_dotnet = binary.has_configuration and bool( binary.data_directories.get(lief.PE.DATA_DIRECTORY.CLR_RUNTIME_HEADER, None) ) - except: + except Exception: is_dotnet = False # General Information @@ -226,8 +225,11 @@ def parse_pe_file(file_path: str) -> dict[str, Any]: try: tls = binary.tls result["tls"] = { - "callbacks": [callback for callback in tls.callbacks], - "addressof_raw_data": {"start": tls.addressof_raw_data.start, "end": tls.addressof_raw_data.end}, + "callbacks": list(tls.callbacks), + "addressof_raw_data": { + "start": tls.addressof_raw_data.start if tls.addressof_raw_data else None, + "end": tls.addressof_raw_data.end if tls.addressof_raw_data else None, + }, "addressof_index": tls.addressof_index, "addressof_callbacks": tls.addressof_callbacks, "sizeof_zero_fill": tls.sizeof_zero_fill, @@ -349,32 +351,67 @@ rule is_pe } """) - def should_process(self, object_id: str) -> bool: + def should_process(self, object_id: str, file_path: str | None = None) -> bool: """Uses a Yara run to determine if this module should run.""" # Get the current file_enriched from the database backend file_enriched = get_file_enriched(object_id) # download a max of 1000 bytes num_bytes = file_enriched.size if file_enriched.size < 1000 else 1000 - file_bytes = self.storage.download_bytes(file_enriched.object_id, length=num_bytes) - logger.debug(f"pe_analyzer downloaded {len(file_bytes)} bytes") + + if file_path: + # Use provided file path - read only the needed bytes + with open(file_path, "rb") as f: + file_bytes = f.read(num_bytes) + else: + # Fallback to downloading the file itself + file_bytes = self.storage.download_bytes(file_enriched.object_id, length=num_bytes) should_run = len(self.yara_rule.scan(file_bytes).matching_rules) > 0 - logger.debug(f"pe_analyzer should_run: {should_run}") return should_run - def process(self, object_id: str) -> EnrichmentResult | None: - """Process file using.""" + def _analyze_pe(self, file_path: str, file_enriched) -> EnrichmentResult | None: + """Analyze PE file and generate enrichment result. + + Args: + file_path: Path to the PE file to analyze + file_enriched: File enrichment data + + Returns: + EnrichmentResult or None if analysis fails + """ + try: + enrichment_result = EnrichmentResult(module_name=self.name) + enrichment_result.results = parse_pe_file(file_path) + return enrichment_result + except Exception as e: + logger.exception(e, message=f"Error analyzing PE file for {file_enriched.file_name}") + return None + + def process(self, object_id: str, file_path: str | None = None) -> EnrichmentResult | None: + """Process file using. + + Args: + object_id: The object ID of the file + file_path: Optional path to already downloaded file + + Returns: + EnrichmentResult or None if processing fails + """ try: # Get the current file_enriched from the database backend file_enriched = get_file_enriched(object_id) - with self.storage.download(file_enriched.object_id) as file: - enrichment_result = EnrichmentResult(module_name=self.name) - enrichment_result.results = parse_pe_file(file.name) - return enrichment_result + # Use provided file_path if available, otherwise download + if file_path: + return self._analyze_pe(file_path, file_enriched) + else: + with self.storage.download(file_enriched.object_id) as file: + return self._analyze_pe(file.name, file_enriched) + except Exception as e: logger.exception(e, message="Error in PE file analysis", file_object_id=object_id) + return None def create_enrichment_module() -> EnrichmentModule: diff --git a/libs/file_enrichment_modules/file_enrichment_modules/pii/analyzer.py b/libs/file_enrichment_modules/file_enrichment_modules/pii/analyzer.py index 386a180..a410d41 100644 --- a/libs/file_enrichment_modules/file_enrichment_modules/pii/analyzer.py +++ b/libs/file_enrichment_modules/file_enrichment_modules/pii/analyzer.py @@ -2,17 +2,15 @@ import tempfile import threading from pathlib import Path -from typing import Optional -import structlog import yara_x +from common.logger import get_logger from common.models import EnrichmentResult, FileObject, Finding, FindingCategory, FindingOrigin, Transform from common.state_helpers import get_file_enriched from common.storage import StorageMinio - from file_enrichment_modules.module_loader import EnrichmentModule -logger = structlog.get_logger(module=__name__) +logger = get_logger(__name__) class PIIAnalyzer(EnrichmentModule): @@ -70,7 +68,7 @@ rule detect_pii """) self._compiled_rules = self._compiler.build() - def _get_scanner(self) -> Optional[yara_x.Scanner]: + def _get_scanner(self) -> yara_x.Scanner | None: """Get or create thread-local scanner instance.""" if not hasattr(self._thread_local, "scanner"): if self._compiled_rules: @@ -85,16 +83,26 @@ rule detect_pii if hasattr(self._thread_local, "scanner"): delattr(self._thread_local, "scanner") - def should_process(self, object_id: str) -> bool: + def should_process(self, object_id: str, file_path: str | None = None) -> bool: """Determine if file should be processed based on size and content.""" file_enriched = get_file_enriched(object_id) if file_enriched.is_plaintext: if file_enriched.size > self.size_limit: - logger.warning(f"File {file_enriched.path} ({file_enriched.size} bytes) exceeds size limit") - return False + logger.warning( + f"[pii_analyzer] file {file_enriched.path} ({file_enriched.object_id} / {file_enriched.size} bytes) exceeds the size limit of {self.size_limit} bytes, only analyzing the first {self.size_limit} bytes" + ) + + if file_path: + # Use provided file path - read only the needed bytes + with open(file_path, "rb") as f: + num_bytes = min(file_enriched.size, self.size_limit) + file_bytes = f.read(num_bytes) + else: + # Fallback to downloading the file itself + num_bytes = file_enriched.size if file_enriched.size < self.size_limit else self.size_limit + file_bytes = self.storage.download_bytes(file_enriched.object_id, length=num_bytes) - file_bytes = self.storage.download_bytes(file_enriched.object_id) scanner = self._get_scanner() if not scanner: logger.warning("No Yara rules compiled") @@ -103,7 +111,6 @@ rule detect_pii matches = scanner.scan(file_bytes) should_run = len(list(matches.matching_rules)) > 0 - logger.debug(f"PIIAnalyzer should_run: {should_run}") return should_run else: return False @@ -168,100 +175,130 @@ rule detect_pii return summary - def process(self, object_id: str) -> Optional[EnrichmentResult]: - """Process file to detect PII using Yara rules.""" + def _analyze_pii(self, file_path: str, file_enriched) -> EnrichmentResult | None: + """Analyze file for PII and generate enrichment result. + + Args: + file_path: Path to the file to analyze for PII + file_enriched: File enrichment data + + Returns: + EnrichmentResult or None if analysis fails + """ + enrichment_result = EnrichmentResult(module_name=self.name, dependencies=self.dependencies) + + scanner = self._get_scanner() + if not scanner: + logger.warning("No Yara rules compiled") + return None + + try: + content = Path(file_path).read_text(encoding="utf-8") + file_bytes = content.encode("utf-8") + + scan_results = scanner.scan(file_bytes) + findings_by_type = {} + + for rule in scan_results.matching_rules: + for pattern in rule.patterns: + if pattern.matches: + # Get the pattern name directly from the identifier + # Remove the $ prefix that Yara adds to pattern names + pattern_name = pattern.identifier.lstrip("$") + pii_type = self._categorize_match(pattern_name) + + if pii_type not in findings_by_type: + findings_by_type[pii_type] = [] + + for match in pattern.matches: + if match.length < 1000: + value = content[match.offset : match.offset + match.length] + context = self._get_match_context(content, match.offset, match.length) + + findings_by_type[pii_type].append( + { + "value": value, + "context": context, + "offset": match.offset, + "length": match.length, + "pattern_id": pattern_name, # Store the clean pattern name + } + ) + + if findings_by_type: + # Create finding summary + summary_markdown = self._create_finding_summary(findings_by_type) + + # Create display data + display_data = FileObject(type="finding_summary", metadata={"summary": summary_markdown}) + + # Create finding + finding = Finding( + category=FindingCategory.PII, + finding_name="pii_detected", + origin_type=FindingOrigin.ENRICHMENT_MODULE, + origin_name=self.name, + object_id=file_enriched.object_id, + severity=8, + raw_data={"findings": findings_by_type}, + data=[display_data], + ) + + enrichment_result.findings = [finding] + enrichment_result.results = {"pii_detected": findings_by_type} + + # Create a displayable version of the results + with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8") as tmp_display_file: + display = "PII Analysis Results\n==================\n\n" + for pii_type, matches in findings_by_type.items(): + display += f"{pii_type}:\n" + display += f" Total instances: {len(matches)}\n" + display += " Found Values:\n" + for match in matches: + display += f" - Offset {match['offset']}: {match['value']}\n" + display += "\n" + + tmp_display_file.write(display) + tmp_display_file.flush() + + object_id = self.storage.upload_file(tmp_display_file.name) + + displayable_parsed = Transform( + type="displayable_parsed", + object_id=f"{object_id}", + metadata={ + "file_name": f"{file_enriched.file_name}_pii_analysis.txt", + "display_type_in_dashboard": "monaco", + "default_display": True, + }, + ) + enrichment_result.transforms = [displayable_parsed] + + return enrichment_result + + except Exception as e: + logger.exception(e, message=f"Error analyzing PII for {file_enriched.file_name}") + return None + + def process(self, object_id: str, file_path: str | None = None) -> EnrichmentResult | None: + """Process file to detect PII using Yara rules. + + Args: + object_id: The object ID of the file + file_path: Optional path to already downloaded file + + Returns: + EnrichmentResult or None if processing fails + """ try: file_enriched = get_file_enriched(object_id) - enrichment_result = EnrichmentResult(module_name=self.name, dependencies=self.dependencies) - scanner = self._get_scanner() - if not scanner: - logger.warning("No Yara rules compiled") - return None - - with self.storage.download(file_enriched.object_id) as temp_file: - content = Path(temp_file.name).read_text(encoding="utf-8") - file_bytes = content.encode("utf-8") - - scan_results = scanner.scan(file_bytes) - findings_by_type = {} - - for rule in scan_results.matching_rules: - for pattern in rule.patterns: - if pattern.matches: - # Get the pattern name directly from the identifier - # Remove the $ prefix that Yara adds to pattern names - pattern_name = pattern.identifier.lstrip("$") - pii_type = self._categorize_match(pattern_name) - - if pii_type not in findings_by_type: - findings_by_type[pii_type] = [] - - for match in pattern.matches: - if match.length < 1000: - value = content[match.offset : match.offset + match.length] - context = self._get_match_context(content, match.offset, match.length) - - findings_by_type[pii_type].append( - { - "value": value, - "context": context, - "offset": match.offset, - "length": match.length, - "pattern_id": pattern_name, # Store the clean pattern name - } - ) - - if findings_by_type: - # Create finding summary - summary_markdown = self._create_finding_summary(findings_by_type) - - # Create display data - display_data = FileObject(type="finding_summary", metadata={"summary": summary_markdown}) - - # Create finding - finding = Finding( - category=FindingCategory.PII, - finding_name="pii_detected", - origin_type=FindingOrigin.ENRICHMENT_MODULE, - origin_name=self.name, - object_id=file_enriched.object_id, - severity=8, - raw_data={"findings": findings_by_type}, - data=[display_data], - ) - - enrichment_result.findings = [finding] - enrichment_result.results = {"pii_detected": findings_by_type} - - # Create a displayable version of the results - with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8") as tmp_display_file: - display = "PII Analysis Results\n==================\n\n" - for pii_type, matches in findings_by_type.items(): - display += f"{pii_type}:\n" - display += f" Total instances: {len(matches)}\n" - display += " Found Values:\n" - for match in matches: - display += f" - Offset {match['offset']}: {match['value']}\n" - display += "\n" - - tmp_display_file.write(display) - tmp_display_file.flush() - - object_id = self.storage.upload_file(tmp_display_file.name) - - displayable_parsed = Transform( - type="displayable_parsed", - object_id=f"{object_id}", - metadata={ - "file_name": f"{file_enriched.file_name}_pii_analysis.txt", - "display_type_in_dashboard": "monaco", - "default_display": True, - }, - ) - enrichment_result.transforms = [displayable_parsed] - - return enrichment_result + # Use provided file_path if available, otherwise download + if file_path: + return self._analyze_pii(file_path, file_enriched) + else: + with self.storage.download(file_enriched.object_id) as temp_file: + return self._analyze_pii(temp_file.name, file_enriched) except Exception as e: logger.exception(e, message="Error processing file for PII detection") diff --git a/libs/file_enrichment_modules/file_enrichment_modules/putty_reg/analyzer.py b/libs/file_enrichment_modules/file_enrichment_modules/putty_reg/analyzer.py index 270c465..67ee153 100644 --- a/libs/file_enrichment_modules/file_enrichment_modules/putty_reg/analyzer.py +++ b/libs/file_enrichment_modules/file_enrichment_modules/putty_reg/analyzer.py @@ -4,15 +4,14 @@ import tempfile import textwrap from pathlib import Path -import structlog import yara_x +from common.logger import get_logger from common.models import EnrichmentResult, FileObject, Finding, FindingCategory, FindingOrigin, Transform from common.state_helpers import get_file_enriched from common.storage import StorageMinio - from file_enrichment_modules.module_loader import EnrichmentModule -logger = structlog.get_logger(module=__name__) +logger = get_logger(__name__) # Port of https://github.com/NetSPI/PowerHuntShares/blob/46238ba37dc85f65f2c1d7960f551ea3d80c236a/Scripts/ConfigParsers/parser-putty.reg.ps1 # Original Author: Scott Sutherland, NetSPI (@_nullbind / nullbind) @@ -26,6 +25,8 @@ class PuttyParser(EnrichmentModule): # the workflows this module should automatically run in self.workflows = ["default"] + self.size_limit = 50_000_000 # 50MB size limit + # Yara rule to check for Putty registry content self.yara_rule = yara_x.compile(""" rule has_putty_reg @@ -37,7 +38,7 @@ rule has_putty_reg } """) - def should_process(self, object_id: str) -> bool: + def should_process(self, object_id: str, file_path: str | None = None) -> bool: """Determine if this module should run based on file type.""" file_enriched = get_file_enriched(object_id) @@ -45,11 +46,17 @@ rule has_putty_reg if not (file_enriched.is_plaintext and file_enriched.file_name.lower().endswith(".reg")): return False - # Check for Putty registry content using Yara - file_bytes = self.storage.download_bytes(file_enriched.object_id) - should_run = len(self.yara_rule.scan(file_bytes).matching_rules) > 0 + if file_path: + # Use provided file path - read only the needed bytes + with open(file_path, "rb") as f: + num_bytes = min(file_enriched.size, self.size_limit) + file_bytes = f.read(num_bytes) + else: + # Fallback to downloading the file itself + num_bytes = file_enriched.size if file_enriched.size < self.size_limit else self.size_limit + file_bytes = self.storage.download_bytes(file_enriched.object_id, length=num_bytes) - logger.debug(f"PuttyParser should_run: {should_run}") + should_run = len(self.yara_rule.scan(file_bytes).matching_rules) > 0 return should_run def _parse_putty_reg(self, content: str) -> list[dict]: @@ -115,73 +122,102 @@ rule has_putty_reg return summary - def process(self, object_id: str) -> EnrichmentResult | None: - """Process Putty registry file.""" + def _analyze_putty_registry(self, file_path: str, file_enriched) -> EnrichmentResult | None: + """Analyze Putty registry file and generate enrichment result. + + Args: + file_path: Path to the Putty registry file to analyze + file_enriched: File enrichment data + + Returns: + EnrichmentResult or None if analysis fails + """ + enrichment_result = EnrichmentResult(module_name=self.name, dependencies=self.dependencies) + + try: + content = Path(file_path).read_text(encoding="utf-8") + + # Parse the registry content + sessions = self._parse_putty_reg(content) + + if sessions: + # Create finding summary + summary_markdown = self._create_finding_summary(sessions) + + # Create display data + display_data = FileObject(type="finding_summary", metadata={"summary": summary_markdown}) + + # Create finding + finding = Finding( + category=FindingCategory.CREDENTIAL, + finding_name="putty_sessions_detected", + origin_type=FindingOrigin.ENRICHMENT_MODULE, + origin_name=self.name, + object_id=file_enriched.object_id, + severity=5, + raw_data={"sessions": sessions}, + data=[display_data], + ) + + # Add finding to enrichment result + enrichment_result.findings = [finding] + enrichment_result.results = {"sessions": sessions} + + # Create a displayable version of the results + with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8") as tmp_display_file: + yaml_output = [] + yaml_output.append("Putty Registry Analysis") + yaml_output.append("=====================\n") + + for session in sessions: + yaml_output.append(f"Session: {session['session_name']}") + for key, value in session.items(): + if key != "session_name": + yaml_output.append(f" {key}: {value}") + yaml_output.append("") + + display = textwrap.indent("\n".join(yaml_output), " ") + tmp_display_file.write(display) + tmp_display_file.flush() + + object_id = self.storage.upload_file(tmp_display_file.name) + + displayable_parsed = Transform( + type="displayable_parsed", + object_id=f"{object_id}", + metadata={ + "file_name": f"{file_enriched.file_name}_analysis.txt", + "display_type_in_dashboard": "monaco", + "default_display": True, + }, + ) + enrichment_result.transforms = [displayable_parsed] + + return enrichment_result + + except Exception as e: + logger.exception(e, message=f"Error analyzing Putty registry for {file_enriched.file_name}") + return None + + def process(self, object_id: str, file_path: str | None = None) -> EnrichmentResult | None: + """Process Putty registry file. + + Args: + object_id: The object ID of the file + file_path: Optional path to already downloaded file + + Returns: + EnrichmentResult or None if processing fails + """ try: file_enriched = get_file_enriched(object_id) - enrichment_result = EnrichmentResult(module_name=self.name, dependencies=self.dependencies) - # Download and read the file - with self.storage.download(file_enriched.object_id) as temp_file: - content = Path(temp_file.name).read_text(encoding="utf-8") - - # Parse the registry content - sessions = self._parse_putty_reg(content) - - if sessions: - # Create finding summary - summary_markdown = self._create_finding_summary(sessions) - - # Create display data - display_data = FileObject(type="finding_summary", metadata={"summary": summary_markdown}) - - # Create finding - finding = Finding( - category=FindingCategory.CREDENTIAL, - finding_name="putty_sessions_detected", - origin_type=FindingOrigin.ENRICHMENT_MODULE, - origin_name=self.name, - object_id=file_enriched.object_id, - severity=5, - raw_data={"sessions": sessions}, - data=[display_data], - ) - - # Add finding to enrichment result - enrichment_result.findings = [finding] - enrichment_result.results = {"sessions": sessions} - - # Create a displayable version of the results - with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8") as tmp_display_file: - yaml_output = [] - yaml_output.append("Putty Registry Analysis") - yaml_output.append("=====================\n") - - for session in sessions: - yaml_output.append(f"Session: {session['session_name']}") - for key, value in session.items(): - if key != "session_name": - yaml_output.append(f" {key}: {value}") - yaml_output.append("") - - display = textwrap.indent("\n".join(yaml_output), " ") - tmp_display_file.write(display) - tmp_display_file.flush() - - object_id = self.storage.upload_file(tmp_display_file.name) - - displayable_parsed = Transform( - type="displayable_parsed", - object_id=f"{object_id}", - metadata={ - "file_name": f"{file_enriched.file_name}_analysis.txt", - "display_type_in_dashboard": "monaco", - "default_display": True, - }, - ) - enrichment_result.transforms = [displayable_parsed] - - return enrichment_result + # Use provided file_path if available, otherwise download + if file_path: + return self._analyze_putty_registry(file_path, file_enriched) + else: + with self.storage.download(file_enriched.object_id) as temp_file: + return self._analyze_putty_registry(temp_file.name, file_enriched) except Exception as e: logger.exception(e, message="Error processing Putty registry file") diff --git a/libs/file_enrichment_modules/file_enrichment_modules/registry_hive/analyzer.py b/libs/file_enrichment_modules/file_enrichment_modules/registry_hive/analyzer.py new file mode 100644 index 0000000..574f587 --- /dev/null +++ b/libs/file_enrichment_modules/file_enrichment_modules/registry_hive/analyzer.py @@ -0,0 +1,990 @@ +# enrichment_modules/registry_hive/analyzer.py +import asyncio +import os +import posixpath +import shutil +import tempfile +import textwrap +from typing import TYPE_CHECKING + +import psycopg +from common.db import get_postgres_connection_str +from common.helpers import get_drive_from_path +from common.logger import get_logger +from common.models import EnrichmentResult, FileObject, Finding, FindingCategory, FindingOrigin, Transform +from common.state_helpers import get_file_enriched, get_file_enriched_async +from common.storage import StorageMinio +from file_enrichment_modules.module_loader import EnrichmentModule +from file_linking.helpers import add_file_linking +from nemesis_dpapi import DpapiSystemCredential +from psycopg.rows import dict_row +from pypykatz.registry.offline_parser import OffineRegistry as OfflineRegistry +from regipy.registry import RegistryHive + +if TYPE_CHECKING: + from nemesis_dpapi import DpapiManager + + +logger = get_logger(__name__) + + +class RegistryHiveAnalyzer(EnrichmentModule): + def __init__(self): + super().__init__("registry_hive") + self.storage = StorageMinio() + self.workflows = ["default"] + self.dpapi_manager: DpapiManager = None # type: ignore + self.loop: asyncio.AbstractEventLoop = None # type: ignore + self._conninfo = get_postgres_connection_str() + + def should_process(self, object_id: str, file_path: str | None = None) -> bool: + """Determine if this module should run based on file type.""" + file_enriched = get_file_enriched(object_id) + magic_type = file_enriched.magic_type.lower() + mime_type = file_enriched.mime_type.lower() + + # This is because the "strings.txt" of a registry hive + # has a matching magic type of the registry hive itself + if mime_type != "application/octet-stream": + return False + + if file_enriched.is_plaintext: + return False + + # Check if it's a Windows registry hive + return any( + hive_type in magic_type + for hive_type in [ + "ms windows registry file", + "windows registry file", + "registry hive", + "windows nt registry hive", + ] + ) + + def _identify_hive_type(self, file_path: str) -> str | None: + """Identify the type of registry hive based regipy""" + + try: + return RegistryHive(file_path).hive_type.upper() + except Exception as e: + logger.exception(e, "Error parsing using regipy") + + return None + + def _extract_bootkey(self, registry: OfflineRegistry) -> str | None: + """Extract bootkey from SYSTEM hive using pypykatz.""" + try: + # pypykatz OfflineRegistry already extracts the bootkey when parsing SYSTEM + if hasattr(registry, "system") and registry.system: + # The bootkey is available in the system object + if hasattr(registry.system, "bootkey"): + return ( + registry.system.bootkey.hex() + if hasattr(registry.system.bootkey, "hex") + else str(registry.system.bootkey) + ) + return None + except Exception as e: + logger.error(f"Failed to extract bootkey: {e}") + return None + + def _find_existing_hive(self, file_enriched, target_hive_path: str) -> str | None: + """Find an existing hive by path.""" + try: + with psycopg.connect(self._conninfo, row_factory=dict_row) as conn: + with conn.cursor() as cur: + # Look for existing hive by path + cur.execute( + """ + SELECT object_id + FROM files_enriched + WHERE source = %s + AND LOWER(path) = LOWER(%s) + ORDER BY timestamp DESC + LIMIT 1 + """, + (file_enriched.source, target_hive_path), + ) + + result = cur.fetchone() + if result: + return str(result["object_id"]) # Convert UUID to string + + # Fallback query: look for registry files by magic_type and enrichment results + # Extract the hive type from the target path (e.g., SECURITY from .../Windows/System32/Config/SECURITY) + target_hive_type = posixpath.basename(target_hive_path).upper() + + cur.execute( + """ + SELECT fe.object_id + FROM files_enriched fe + JOIN enrichments e ON fe.object_id = e.object_id + WHERE fe.source = %s + AND fe.magic_type = 'MS Windows registry file, NT/2000 or above' + AND e.module_name = 'registry_hive' + AND e.result_data->'results'->'hive_type' = %s + ORDER BY fe.timestamp DESC + LIMIT 1 + """, + (file_enriched.source, f'"{target_hive_type}"'), + ) + + result = cur.fetchone() + if result: + return str(result["object_id"]) # Convert UUID to string + + except Exception as e: + logger.error(f"Failed to find existing hive {target_hive_path}: {e}") + + return None + + def _get_existing_hive_path(self, file_enriched, standard_path: str) -> str: + """Get the actual path of an existing hive, or return the standard path if not found.""" + # First try to find an existing hive + object_id = self._find_existing_hive(file_enriched, standard_path) + + if object_id: + # Found an existing hive, get its actual path from the database + try: + with psycopg.connect(self._conninfo, row_factory=dict_row) as conn: + with conn.cursor() as cur: + cur.execute( + """ + SELECT path + FROM files_enriched + WHERE object_id = %s + LIMIT 1 + """, + (object_id,), + ) + + result = cur.fetchone() + if result and result["path"]: + logger.debug(f"Found existing hive at {result['path']} instead of {standard_path}") + return result["path"] + except Exception as e: + logger.error(f"Failed to get path for existing hive {object_id}: {e}") + + # Fall back to standard path if not found or on error + return standard_path + + async def _create_proactive_file_linkings(self, file_enriched, hive_type: str): + """Create proactive file linkings based on hive type.""" + if not file_enriched.source or not file_enriched.path: + return + + drive = get_drive_from_path(file_enriched.path) or "" + # if not drive: + # logger.warning(f"Could not extract drive from path: {file_enriched.path}") + # return + + try: + if hive_type == "SYSTEM": + # Link to SAM and SECURITY hives + # First check if they exist at non-standard locations + sam_standard_path = f"{drive}/Windows/System32/Config/SAM" + security_standard_path = f"{drive}/Windows/System32/Config/SECURITY" + + sam_path = self._get_existing_hive_path(file_enriched, sam_standard_path) + security_path = self._get_existing_hive_path(file_enriched, security_standard_path) + + await add_file_linking( + source=file_enriched.source, + source_file_path=file_enriched.path, + linked_file_path=sam_path, + link_type="registry_system", + collection_reason="SYSTEM hive can decrypt SAM accounts", + ) + + await add_file_linking( + source=file_enriched.source, + source_file_path=file_enriched.path, + linked_file_path=security_path, + link_type="registry_system", + collection_reason="SYSTEM hive required to decrypt SECURITY data", + ) + + elif hive_type in ["SAM", "SECURITY"]: + # Link to SYSTEM hive + # First check if it exists at a non-standard location + system_standard_path = f"{drive}/Windows/System32/Config/SYSTEM" + system_path = self._get_existing_hive_path(file_enriched, system_standard_path) + + await add_file_linking( + source=file_enriched.source, + source_file_path=file_enriched.path, + linked_file_path=system_path, + link_type="registry_system", + collection_reason=f"SYSTEM hive required to decrypt {hive_type} data", + ) + + except Exception as e: + logger.error(f"Failed to create proactive file linkings: {e}") + + def _process_sam_hive(self, sam_file: str, system_file: str | None) -> dict: + """Process SAM hive to extract local accounts using pypykatz.""" + results = {"accounts": [], "bootkey_available": system_file is not None} + + try: + # Use pypykatz to parse the SAM hive with optional SYSTEM hive for decryption + if system_file: + registry = OfflineRegistry.from_files(system_path=system_file, sam_path=sam_file) + bootkey = self._extract_bootkey(registry) + results["bootkey"] = bootkey + else: + # Cannot parse SAM without SYSTEM - pypykatz requires SYSTEM hive for proper parsing + logger.warning("Cannot process SAM hive without SYSTEM hive - pypykatz requires both") + return results + + # Extract user information from parsed SAM + if hasattr(registry, "sam") and registry.sam: + sam_obj = registry.sam + if hasattr(sam_obj, "users"): + for user in sam_obj.users: + user_info = { + "rid": getattr(user, "rid", None), + "username": getattr(user, "username", None), + "full_name": getattr(user, "fullname", None), + "comment": getattr(user, "comment", None), + "nt_hash": getattr(user, "nt_hash", None), + "lm_hash": getattr(user, "lm_hash", None), + "bootkey_available": system_file is not None, + } + + # Convert hashes to hex strings if they exist + if user_info["nt_hash"]: + user_info["nt_hash"] = ( + user_info["nt_hash"].hex() + if hasattr(user_info["nt_hash"], "hex") + else str(user_info["nt_hash"]) + ) + if user_info["lm_hash"]: + user_info["lm_hash"] = ( + user_info["lm_hash"].hex() + if hasattr(user_info["lm_hash"], "hex") + else str(user_info["lm_hash"]) + ) + + results["accounts"].append(user_info) + + except Exception as e: + logger.error(f"Failed to process SAM hive with pypykatz: {e}") + # Return empty results on error + results["accounts"] = [] + results["error"] = "Could not parse SAM hive" + + return results + + async def _process_security_hive(self, security_file: str, system_file: str | None) -> dict: + """Process SECURITY hive to extract LSA secrets using pypykatz.""" + results = { + "lsa_secrets": [], + "cached_credentials": [], + "bootkey_available": system_file is not None, + } + + if not system_file: + # Cannot parse SECURITY without SYSTEM - pypykatz requires SYSTEM hive + logger.debug("Cannot process SECURITY hive without SYSTEM hive - pypykatz requires both") + return results + + # Create persistent copies of both files that pypykatz can access + security_copy_path = None + system_copy_path = None + + try: + # Create temporary copies that persist during processing + security_fd, security_copy_path = tempfile.mkstemp(suffix=".security") + system_fd, system_copy_path = tempfile.mkstemp(suffix=".system") + + # Close the file descriptors but keep the paths + os.close(security_fd) + os.close(system_fd) + + # Copy the files + shutil.copy2(security_file, security_copy_path) + shutil.copy2(system_file, system_copy_path) + + # Now parse with pypykatz using the persistent copies + registry = OfflineRegistry.from_files(system_path=system_copy_path, security_path=security_copy_path) + + # Extract bootkey from SYSTEM + bootkey = self._extract_bootkey(registry) + if bootkey: + results["bootkey"] = bootkey + logger.debug("Extracted bootkey from SYSTEM hive") + else: + logger.warning("Failed to extract bootkey from SYSTEM hive") + + # Call get_secrets to extract and decrypt secrets + try: + registry.get_secrets() + logger.debug("get_secrets() completed successfully") + except Exception as e: + logger.warning(f"Failed to extract secrets: {e}") + # Continue anyway to see if we can get any data + + # Extract LSA secrets from parsed SECURITY hive + if hasattr(registry, "security") and registry.security: + security_obj = registry.security + + # Try to get secrets as dictionary + try: + security_dict = security_obj.to_dict() + logger.debug(f"Security dict keys: {list(security_dict.keys()) if security_dict else 'None'}") + + # Extract LSA secrets - they're in 'cached_secrets', not 'lsa_secrets'! + if security_dict and "cached_secrets" in security_dict: + cached_secrets = security_dict["cached_secrets"] + + if isinstance(cached_secrets, list): + # cached_secrets is a list of secret objects + for i, secret_data in enumerate(cached_secrets): + # If it's a dict, inspect its keys to find the actual secret data + if isinstance(secret_data, dict): + # Look for common secret data keys + secret_value = None + secret_name = f"cached_secret_{i}" + + # First try common secret keys + for key in [ + "secret", + "data", + "value", + "cleartext", + "plaintext", + "decrypted", + ]: + if key in secret_data: + secret_value = secret_data[key] + break + + # For DPAPI secrets, extract machine_key and user_key + if not secret_value and "machine_key" in secret_data and "user_key" in secret_data: + machine_key = secret_data["machine_key"] + user_key = secret_data["user_key"] + if isinstance(machine_key, bytes) and isinstance(user_key, bytes): + secret_value = { + "machine_key": machine_key.hex(), + "user_key": user_key.hex(), + } + secret_name = secret_data.get("key_name", f"cached_secret_{i}") + + logger.debug( + f"Found DPAPI keys - machine_key: {len(machine_key)} bytes, user_key: {len(user_key)} bytes" + ) + + # Register the DPAPI_SYSTEM credential with the DPAPI manager + await self._register_dpapi_system_credential(machine_key, user_key) + + # For NL$KM secrets, extract raw_secret + elif not secret_value and "raw_secret" in secret_data: + raw_secret = secret_data["raw_secret"] + if isinstance(raw_secret, bytes): + secret_value = raw_secret.hex() + secret_name = secret_data.get("key_name", f"cached_secret_{i}") + + # If still no specific key found, try to get the first non-metadata value + elif not secret_value: + for key, value in secret_data.items(): + if ( + key + not in [ + "type", + "name", + "id", + "index", + "key_name", + "history", + ] + and value + ): + secret_value = value + break + + secret_info = { + "name": secret_name, + "decrypted": True, + "value": str(secret_value) if secret_value else str(secret_data), + "bootkey_available": True, + } + else: + secret_info = { + "name": f"cached_secret_{i}", + "decrypted": True, + "value": str(secret_data) if secret_data else None, + "bootkey_available": True, + } + results["lsa_secrets"].append(secret_info) + elif isinstance(cached_secrets, dict): + # cached_secrets is a dictionary + for secret_name, secret_data in cached_secrets.items(): + secret_info = { + "name": secret_name, + "decrypted": True, + "value": str(secret_data) if secret_data else None, + "bootkey_available": True, + } + results["lsa_secrets"].append(secret_info) + + # Also check for other secret types + secret_keys = ["lsa_key", "NK$LM", "dcc"] + for key in secret_keys: + if security_dict and key in security_dict: + secret_data = security_dict[key] + + # Format bytes as hex strings for better readability + if isinstance(secret_data, bytes): + formatted_value = secret_data.hex() + else: + formatted_value = str(secret_data) if secret_data else None + + secret_info = { + "name": key, + "decrypted": True, + "value": formatted_value, + "bootkey_available": True, + } + results["lsa_secrets"].append(secret_info) + + if not results["lsa_secrets"]: + logger.warning( + "No secrets extracted from security_dict - available keys: " + + str(list(security_dict.keys())) + ) + + # Also try direct attribute access for LSA secrets + if hasattr(security_obj, "lsa_secrets"): + lsa_secrets_attr = getattr(security_obj, "lsa_secrets", {}) + logger.debug(f"Found lsa_secrets attribute with {len(lsa_secrets_attr)} items") + for secret_name, secret_data in lsa_secrets_attr.items(): + # Avoid duplicates if we already processed from dict + if not any(s["name"] == secret_name for s in results["lsa_secrets"]): + secret_info = { + "name": secret_name, + "decrypted": True, + "value": str(secret_data) if secret_data else None, + "bootkey_available": True, + } + results["lsa_secrets"].append(secret_info) + + # Check for cached domain credentials + if security_dict and "cached_creds" in security_dict and security_dict["cached_creds"]: + results["cached_credentials_key_present"] = True + for cached_cred in security_dict["cached_creds"]: + cred_info = { + "domain": cached_cred.get("domain"), + "username": cached_cred.get("username"), + "decrypted": True, + } + results["cached_credentials"].append(cred_info) + + except Exception as e: + logger.error(f"Failed to extract security dictionary: {e}") + # Try string representation as fallback + try: + security_str = str(security_obj) + logger.warning(f"Security string representation length: {len(security_str)}") + if security_str and len(security_str) > 10: + # If we have substantial content, create a basic entry + secret_info = { + "name": "parsed_from_string_representation", + "decrypted": True, + "value": "LSA secrets present - check raw data", + "bootkey_available": True, + } + results["lsa_secrets"].append(secret_info) + except Exception as e2: + logger.error(f"Failed to get security string representation: {e2}") + + # Also check for cached credentials via attribute access + if hasattr(security_obj, "cached_creds"): + cached_creds_attr = getattr(security_obj, "cached_creds", []) + if cached_creds_attr: + results["cached_credentials_key_present"] = True + try: + for cached_cred in cached_creds_attr: + cred_info = { + "domain": getattr(cached_cred, "domain", None), + "username": getattr(cached_cred, "username", None), + "decrypted": True, + } + results["cached_credentials"].append(cred_info) + except Exception as e: + logger.error(f"Failed to extract cached credentials: {e}") + else: + logger.warning("No security object found in registry after parsing") + + except Exception as e: + logger.error(f"Failed to process SECURITY hive with pypykatz: {e}") + # Return empty results on error + results["lsa_secrets"] = [] + results["error"] = "Could not parse SECURITY hive" + + finally: + # Clean up temporary files + for temp_path in [security_copy_path, system_copy_path]: + if temp_path and os.path.exists(temp_path): + try: + os.unlink(temp_path) + except Exception as e: + logger.warning(f"Failed to clean up temporary file {temp_path}: {e}") + + return results + + def _process_system_hive(self, system_file: str) -> dict: + """Process SYSTEM hive to extract bootkey and system information using pypykatz.""" + results = { + "bootkey": None, + "computer_name": None, + "current_control_set": None, + "services": [], + } + + try: + # Use pypykatz to parse the SYSTEM hive + registry = OfflineRegistry.from_files(system_path=system_file) + + # Extract bootkey + bootkey = self._extract_bootkey(registry) + if bootkey: + results["bootkey"] = bootkey + + # Extract system information from parsed SYSTEM hive + if hasattr(registry, "system") and registry.system: + system_obj = registry.system + + # Get computer name if available + if hasattr(system_obj, "computer_name"): + results["computer_name"] = system_obj.computer_name + + # Get current control set info if available + if hasattr(system_obj, "current_control_set"): + results["current_control_set"] = system_obj.current_control_set + + # Extract some basic service info if available + interesting_services = [ + "NTDS", + "DNS", + "W32Time", + "LanmanServer", + "Spooler", + ] + for service_name in interesting_services: + service_info = { + "name": service_name, + "display_name": None, + "start_type": None, + "status": "present_in_system_hive", + } + results["services"].append(service_info) + + except Exception as e: + logger.exception(e, message="Failed to process SYSTEM hive with pypykatz") + # Return empty results on error + results = { + "bootkey": None, + "error": "Could not parse SYSTEM hive", + "computer_name": None, + "current_control_set": None, + "services": [], + } + + return results + + def _create_finding_summary(self, hive_type: str, analysis_results: dict, file_enriched) -> str: + """Create markdown summary for registry hive analysis.""" + summary = f"# Windows Registry Hive Analysis: {hive_type}\n\n" + summary += f"**File**: `{file_enriched.file_name}`\n\n" + summary += f"**Hive Type**: {hive_type}\n\n" + + if hive_type == "SYSTEM": + if analysis_results.get("bootkey"): + summary += f"**Bootkey**: `{analysis_results['bootkey']}`\n\n" + if analysis_results.get("computer_name"): + summary += f"**Computer Name**: `{analysis_results['computer_name']}`\n\n" + if analysis_results.get("current_control_set"): + summary += f"**Current Control Set**: {analysis_results['current_control_set']}\n\n" + + services = analysis_results.get("services", []) + if services: + summary += "## System Services\n\n" + for service in services: + summary += f"* **{service['name']}**: {service.get('display_name', 'N/A')} (Start: {service.get('start_type', 'N/A')})\n" + summary += "\n" + + # Include SAM analysis if processed + sam_analysis = analysis_results.get("sam_analysis") + if sam_analysis: + accounts = sam_analysis.get("accounts", []) + summary += "## Paired SAM Analysis\n\n" + summary += f"**Local Accounts Found**: {len(accounts)}\n\n" + if accounts: + summary += "**Local User Accounts**:\n\n" + for account in accounts[:10]: # Limit to first 10 + summary += f"* **RID {account['rid']}**: {account.get('username', 'Unknown')}\n" + if len(accounts) > 10: + summary += f"* ... and {len(accounts) - 10} more accounts\n" + summary += "\n" + + # Include SECURITY analysis if processed + security_analysis = analysis_results.get("security_analysis") + if security_analysis: + secrets = security_analysis.get("lsa_secrets", []) + summary += "## Paired SECURITY Analysis\n\n" + summary += f"**LSA Secrets Found**: {len(secrets)}\n\n" + if secrets: + summary += "**LSA Secrets**:\n\n" + for secret in secrets[:10]: # Limit to first 10 + outputStr = self._get_lsa_secret_output_string(secret, truncate_length=100, markdown=True) + summary += f"- {outputStr}\n" + if len(secrets) > 10: + summary += f"* ... and {len(secrets) - 10} more secrets\n" + summary += "\n" + + if security_analysis.get("cached_credentials_key_present"): + summary += "**Cached Domain Credentials**: Key present (NL$KM)\n\n" + + elif hive_type == "SAM": + accounts = analysis_results.get("accounts", []) + summary += f"**Local Accounts Found**: {len(accounts)}\n\n" + if analysis_results.get("bootkey_available"): + summary += "**Note**: Linked SYSTEM hive found - password decryption possible\n\n" + else: + summary += "**Note**: No linked SYSTEM hive - password hashes encrypted\n\n" + + if accounts: + summary += "## Local User Accounts\n\n" + for account in accounts[:10]: # Limit to first 10 + summary += f"* **RID {account['rid']}**: {account.get('username', 'Unknown')}\n" + if len(accounts) > 10: + summary += f"* ... and {len(accounts) - 10} more accounts\n" + summary += "\n" + + elif hive_type == "SECURITY": + secrets = analysis_results.get("lsa_secrets", []) + summary += f"**LSA Secrets Found**: {len(secrets)}\n\n" + if analysis_results.get("bootkey_available"): + summary += "**Note**: Linked SYSTEM hive found - LSA secret decryption possible\n\n" + else: + summary += "**Note**: No linked SYSTEM hive - LSA secrets encrypted\n\n" + + if secrets: + summary += "## LSA Secrets\n\n" + for secret in secrets[:10]: # Limit to first 10 + outputStr = self._get_lsa_secret_output_string(secret, truncate_length=100, markdown=True) + summary += f"- {outputStr}\n" + if len(secrets) > 10: + summary += f"* ... and {len(secrets) - 10} more secrets\n" + summary += "\n" + + if analysis_results.get("cached_credentials_key_present"): + summary += "**Cached Domain Credentials**: Key present (NL$KM)\n\n" + + return summary + + async def process(self, object_id: str, file_path: str | None = None) -> EnrichmentResult | None: + """Do the file enrichment. + + Args: + object_id: The object ID of the file + file_path: Optional path to already downloaded file + + Returns: + EnrichmentResult or None if processing fails + """ + return await self._process_async(object_id, file_path) + + async def _process_async(self, object_id: str, file_path: str | None = None) -> EnrichmentResult | None: + """Process registry hive file and extract relevant information.""" + try: + file_enriched = await get_file_enriched_async(object_id) + + # Use provided file_path if available, otherwise download + if file_path: + return await self._analyze_registry_hive_file(file_path, file_enriched) + else: + # Download the file to a temporary location + with self.storage.download(file_enriched.object_id) as temp_file: + return await self._analyze_registry_hive_file(temp_file.name, file_enriched) + + except Exception as e: + logger.exception(e, message="Error processing registry hive file") + return None + + def _get_lsa_secret_output_string(self, secret: dict, truncate_length: int = 8196, markdown: bool = False) -> str: + keyStr = f"**{secret['name']}**" if markdown else secret["name"] + + if not isinstance(secret, dict): + raise ValueError("Expect a LSA secret dictionary") + + if "name" not in secret: + raise ValueError("Expect a LSA secret dictionary with 'name' key") + + if not secret.get("decrypted", False): + return f"{keyStr}: Encrypted data" + + if not secret.get("value"): + return f"{keyStr}: No Value" + + # Show decrypted value, but truncate if too long + value = secret["value"] + + if len(value) > truncate_length: + value = value[:truncate_length] + "..." + + value = f"`{value}`" if markdown else value + + return f"{keyStr}: {value}" + + async def _analyze_registry_hive_file(self, hive_file_path: str, file_enriched) -> EnrichmentResult | None: + """Analyze registry hive file and generate enrichment result.""" + enrichment_result = EnrichmentResult(module_name=self.name, dependencies=self.dependencies) + + # Identify hive type + hive_type = self._identify_hive_type(hive_file_path) + if not hive_type: + logger.warning(f"Could not identify registry hive type for {file_enriched.file_name}") + return None + + analysis_results = {} + linked_system_object_id = None + + # Create proactive file linkings + await self._create_proactive_file_linkings(file_enriched, hive_type) + + # Process based on hive type + if hive_type == "SYSTEM": + # Process SYSTEM hive first + analysis_results = self._process_system_hive(hive_file_path) + + # Also check for and process existing SAM/SECURITY hives + drive = get_drive_from_path(file_enriched.path) or "" + # if drive: + sam_path = f"{drive}/Windows/System32/Config/SAM" + security_path = f"{drive}/Windows/System32/Config/SECURITY" + + sam_object_id = self._find_existing_hive(file_enriched, sam_path) + security_object_id = self._find_existing_hive(file_enriched, security_path) + + # Process SAM if found + if sam_object_id: + try: + with self.storage.download(sam_object_id) as sam_temp_file: + sam_results = self._process_sam_hive(sam_temp_file.name, hive_file_path) + analysis_results["sam_analysis"] = sam_results + logger.debug(f"Processed paired SAM hive for SYSTEM: {sam_path}") + except Exception as e: + logger.error(f"Failed to process paired SAM hive: {e}") + + # Process SECURITY if found + if security_object_id: + try: + with self.storage.download(security_object_id) as security_temp_file: + sam_results = await self._process_security_hive(security_temp_file.name, hive_file_path) + analysis_results["security_analysis"] = sam_results + logger.debug(f"Processed paired SECURITY hive for SYSTEM: {security_path}") + except Exception as e: + logger.error(f"Failed to process paired SECURITY hive: {e}") + + elif hive_type in ["SAM", "SECURITY"]: + # Look for SYSTEM hive + drive = get_drive_from_path(file_enriched.path) or "" + system_object_id = None + + # if drive: + system_path = f"{drive}/Windows/System32/Config/SYSTEM" + system_object_id = self._find_existing_hive(file_enriched, system_path) + + if system_object_id: + # Download the SYSTEM hive + try: + with self.storage.download(system_object_id) as system_temp_file: + if hive_type == "SAM": + analysis_results = self._process_sam_hive(hive_file_path, system_temp_file.name) + else: # SECURITY + analysis_results = await self._process_security_hive(hive_file_path, system_temp_file.name) + logger.debug(f"Processed {hive_type} hive with SYSTEM bootkey") + + except Exception as e: + logger.error(f"Error downloading SYSTEM hive: {e}") + # Process without SYSTEM hive + if hive_type == "SAM": + analysis_results = self._process_sam_hive(hive_file_path, None) + else: # SECURITY + analysis_results = await self._process_security_hive(hive_file_path, None) + logger.debug(f"Processed {hive_type} hive without SYSTEM bootkey (download error)") + + else: + # Process without SYSTEM hive + if hive_type == "SAM": + analysis_results = self._process_sam_hive(hive_file_path, None) + else: # SECURITY + analysis_results = await self._process_security_hive(hive_file_path, None) + logger.debug(f"Processed {hive_type} hive without SYSTEM bootkey") + + # Store reference to system hive if found + if system_object_id: + linked_system_object_id = system_object_id + + else: + # For other hive types, just note the type + analysis_results = {"hive_type": hive_type, "processed": False} + + # Create finding if we have results + if analysis_results: + summary_markdown = self._create_finding_summary(hive_type, analysis_results, file_enriched) + + # Determine finding category and severity + if hive_type == "SYSTEM" and analysis_results.get("bootkey"): + category = FindingCategory.CREDENTIAL + severity = 8 + finding_name = "system_hive_bootkey_extracted" + elif hive_type == "SAM" and analysis_results.get("accounts"): + category = FindingCategory.CREDENTIAL + severity = 7 if analysis_results.get("bootkey_available") else 3 + finding_name = "sam_hive_accounts_detected" + elif hive_type == "SECURITY" and analysis_results.get("lsa_secrets"): + category = FindingCategory.CREDENTIAL + severity = 7 if analysis_results.get("bootkey_available") else 3 + finding_name = "security_hive_secrets_detected" + else: + category = FindingCategory.INFORMATIONAL + severity = 1 + finding_name = f"registry_hive_{hive_type.lower()}_processed" + + # Create display data + display_data = FileObject(type="finding_summary", metadata={"summary": summary_markdown}) + + # Create finding + finding = Finding( + category=category, + finding_name=finding_name, + origin_type=FindingOrigin.ENRICHMENT_MODULE, + origin_name=self.name, + object_id=file_enriched.object_id, + severity=severity, + raw_data={ + "hive_type": hive_type, + "analysis_results": analysis_results, + "linked_system_hive": linked_system_object_id is not None, + }, + data=[display_data], + ) + + enrichment_result.findings = [finding] + enrichment_result.results = { + "hive_type": hive_type, + "analysis_results": analysis_results, + } + + # Create displayable transform + with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8") as tmp_file: + yaml_output = [] + yaml_output.append(f"Registry Hive Analysis: {hive_type}") + yaml_output.append("=" * (25 + len(hive_type))) + yaml_output.append("") + yaml_output.append(f"File: {file_enriched.file_name}") + yaml_output.append(f"Hive Type: {hive_type}") + yaml_output.append("") + + if hive_type == "SYSTEM": + if analysis_results.get("bootkey"): + yaml_output.append(f"Bootkey: {analysis_results['bootkey']}") + if analysis_results.get("computer_name"): + yaml_output.append(f"Computer Name: {analysis_results['computer_name']}") + if analysis_results.get("services"): + yaml_output.append("\nSystem Services:") + for service in analysis_results["services"]: + yaml_output.append(f" {service['name']}: {service.get('display_name', 'N/A')}") + + # Include paired SAM analysis + sam_analysis = analysis_results.get("sam_analysis") + if sam_analysis: + accounts = sam_analysis.get("accounts", []) + yaml_output.append("\nPaired SAM Analysis:") + yaml_output.append(f" Local Accounts Found: {len(accounts)}") + if accounts: + yaml_output.append(" Local User Accounts:") + for account in accounts[:10]: + yaml_output.append(f" RID {account['rid']}: {account.get('username', 'Unknown')}") + + # Include paired SECURITY analysis + security_analysis = analysis_results.get("security_analysis") + if security_analysis: + secrets = security_analysis.get("lsa_secrets", []) + yaml_output.append("\nPaired SECURITY Analysis:") + yaml_output.append(f" LSA Secrets Found: {len(secrets)}") + if secrets: + yaml_output.append(" LSA Secrets:") + for secret in secrets: + outputStr = self._get_lsa_secret_output_string(secret) + yaml_output.append(f" {outputStr}") + + elif hive_type == "SAM": + accounts = analysis_results.get("accounts", []) + yaml_output.append(f"Local Accounts Found: {len(accounts)}") + yaml_output.append(f"Bootkey Available: {analysis_results.get('bootkey_available', False)}") + if accounts: + yaml_output.append("\nLocal User Accounts:") + for account in accounts[:10]: + yaml_output.append(f" RID {account['rid']}: {account.get('username', 'Unknown')}") + + elif hive_type == "SECURITY": + secrets = analysis_results.get("lsa_secrets", []) + yaml_output.append(f"LSA Secrets Found: {len(secrets)}") + yaml_output.append(f"Bootkey Available: {analysis_results.get('bootkey_available', False)}") + if secrets: + yaml_output.append("\nLSA Secrets:") + for secret in secrets: + outputStr = self._get_lsa_secret_output_string(secret) + yaml_output.append(f" {outputStr}") + + display_content = textwrap.indent("\n".join(yaml_output), " ") + tmp_file.write(display_content) + tmp_file.flush() + + display_object_id = self.storage.upload_file(tmp_file.name) + + displayable_parsed = Transform( + type="displayable_parsed", + object_id=str(display_object_id), + metadata={ + "file_name": f"{file_enriched.file_name}_{hive_type.lower()}_analysis.txt", + "display_type_in_dashboard": "monaco", + "default_display": True, + }, + ) + enrichment_result.transforms = [displayable_parsed] + + return enrichment_result + + async def _register_dpapi_system_credential(self, machine_key: bytes, user_key: bytes): + """Register a DPAPI_SYSTEM credential with the DPAPI manager. + + Args: + machine_key: The machine key component (20 bytes) + user_key: The user key component (20 bytes) + """ + + try: + # Create DPAPI system credential from the machine and user keys + logger.debug( + "Registering DPAPI_SYSTEM credential with DPAPI manager", + machine_key=machine_key.hex(), + user_key=user_key.hex(), + ) + dpapi_system_cred = DpapiSystemCredential(machine_key=machine_key, user_key=user_key) + + # Register with the DPAPI manager - it will automatically decrypt compatible masterkeys + await self.dpapi_manager.upsert_system_credential(dpapi_system_cred) + + logger.info("Successfully registered DPAPI_SYSTEM credential with DPAPI manager") + + except Exception as e: + logger.exception(e, f"Failed to register DPAPI_SYSTEM credential: {e}") + + +def create_enrichment_module() -> EnrichmentModule: + return RegistryHiveAnalyzer() diff --git a/libs/file_enrichment_modules/file_enrichment_modules/slack/analyzer.py b/libs/file_enrichment_modules/file_enrichment_modules/slack/analyzer.py index b7861f1..97c35e4 100644 --- a/libs/file_enrichment_modules/file_enrichment_modules/slack/analyzer.py +++ b/libs/file_enrichment_modules/file_enrichment_modules/slack/analyzer.py @@ -4,15 +4,14 @@ import json import tempfile from datetime import datetime -import structlog import yara_x +from common.logger import get_logger from common.models import EnrichmentResult, Transform from common.state_helpers import get_file_enriched from common.storage import StorageMinio - from file_enrichment_modules.module_loader import EnrichmentModule -logger = structlog.get_logger(module=__name__) +logger = get_logger(__name__) class SlackRootStateParser(EnrichmentModule): @@ -23,6 +22,8 @@ class SlackRootStateParser(EnrichmentModule): # the workflows this module should automatically run in self.workflows = ["default"] + self.size_limit = 50_000_000 # 50MB size limit + # Yara rule to detect Slack root-state.json files self.yara_rule = yara_x.compile(""" rule Detect_Slack_RootState { @@ -50,7 +51,7 @@ rule Detect_Slack_RootState { } """) - def should_process(self, object_id: str) -> bool: + def should_process(self, object_id: str, file_path: str | None = None) -> bool: """Determine if this module should run.""" file_enriched = get_file_enriched(object_id) @@ -61,11 +62,17 @@ rule Detect_Slack_RootState { if "json" not in file_enriched.magic_type.lower(): return False - # Run Yara check - file_bytes = self.storage.download_bytes(file_enriched.object_id) - should_run = len(self.yara_rule.scan(file_bytes).matching_rules) > 0 + if file_path: + # Use provided file path - read only the needed bytes + with open(file_path, "rb") as f: + num_bytes = min(file_enriched.size, self.size_limit) + file_bytes = f.read(num_bytes) + else: + # Fallback to downloading the file itself + num_bytes = file_enriched.size if file_enriched.size < self.size_limit else self.size_limit + file_bytes = self.storage.download_bytes(file_enriched.object_id, length=num_bytes) - logger.debug(f"SlackRootStateParser should_run: {should_run}") + should_run = len(self.yara_rule.scan(file_bytes).matching_rules) > 0 return should_run def _parse_timestamp(self, timestamp): @@ -77,131 +84,160 @@ rule Detect_Slack_RootState { return "" return "" - def process(self, object_id: str) -> EnrichmentResult | None: - """Process Slack root-state.json file.""" + def _analyze_slack_root_state(self, file_path: str, file_enriched) -> EnrichmentResult | None: + """Analyze Slack root-state.json file and generate enrichment result. + + Args: + file_path: Path to the Slack root-state.json file to analyze + file_enriched: File enrichment data + + Returns: + EnrichmentResult or None if analysis fails + """ + enrichment_result = EnrichmentResult(module_name=self.name, dependencies=self.dependencies) + transforms = [] + try: - file_enriched = get_file_enriched(object_id) - enrichment_result = EnrichmentResult(module_name=self.name, dependencies=self.dependencies) - transforms = [] + with open(file_path, encoding="utf-8") as f: + data = json.load(f) - with self.storage.download(file_enriched.object_id) as temp_file: - # Load JSON data - with open(temp_file.name, encoding="utf-8") as f: - data = json.load(f) + # Extract workspaces data + workspaces_data = [] + workspaces = data.get("workspaces", {}) - # Extract workspaces data - workspaces_data = [] - workspaces = data.get("workspaces", {}) + for workspace_id, workspace_info in workspaces.items(): + workspaces_data.append( + { + "domain": workspace_info.get("domain", ""), + "id": workspace_info.get("id", workspace_id), + "name": workspace_info.get("name", ""), + "url": workspace_info.get("url", ""), + } + ) - for workspace_id, workspace_info in workspaces.items(): - workspaces_data.append( + # Create workspaces CSV + with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8", newline="") as tmp_workspaces_csv: + writer = csv.writer(tmp_workspaces_csv) + + # Write header + writer.writerow(["domain", "id", "name", "url"]) + + # Write workspace data + for workspace in workspaces_data: + writer.writerow([workspace["domain"], workspace["id"], workspace["name"], workspace["url"]]) + + tmp_workspaces_csv.flush() + workspaces_csv_id = self.storage.upload_file(tmp_workspaces_csv.name) + + transforms.append( + Transform( + type="slack_workspaces", + object_id=f"{workspaces_csv_id}", + metadata={ + "file_name": f"{file_enriched.file_name}_workspaces.csv", + "offer_as_download": True, + }, + ) + ) + + # Extract downloads data + downloads_data = [] + downloads = data.get("downloads", {}) + + # Create team name lookup for correlation + team_name_lookup = {ws["id"]: ws["name"] for ws in workspaces_data} + + for team_id, team_downloads in downloads.items(): + for download_id, download_info in team_downloads.items(): + downloads_data.append( { - "domain": workspace_info.get("domain", ""), - "id": workspace_info.get("id", workspace_id), - "name": workspace_info.get("name", ""), - "url": workspace_info.get("url", ""), + "id": download_info.get("id", download_id), + "teamId": download_info.get("teamId", team_id), + "team_name": team_name_lookup.get(download_info.get("teamId", team_id), ""), + "userId": download_info.get("userId", ""), + "downloadPath": download_info.get("downloadPath", ""), + "url": download_info.get("url", ""), + "downloadState": download_info.get("downloadState", ""), + "startTime": self._parse_timestamp(download_info.get("startTime")), + "endTime": self._parse_timestamp(download_info.get("endTime")), } ) - # Create workspaces CSV - with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8", newline="") as tmp_workspaces_csv: - writer = csv.writer(tmp_workspaces_csv) + # Create downloads CSV + with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8", newline="") as tmp_downloads_csv: + writer = csv.writer(tmp_downloads_csv) - # Write header - writer.writerow(["domain", "id", "name", "url"]) + # Write header + writer.writerow( + [ + "id", + "teamId", + "team_name", + "userId", + "downloadPath", + "url", + "downloadState", + "startTime", + "endTime", + ] + ) - # Write workspace data - for workspace in workspaces_data: - writer.writerow([workspace["domain"], workspace["id"], workspace["name"], workspace["url"]]) - - tmp_workspaces_csv.flush() - workspaces_csv_id = self.storage.upload_file(tmp_workspaces_csv.name) - - transforms.append( - Transform( - type="slack_workspaces", - object_id=f"{workspaces_csv_id}", - metadata={ - "file_name": f"{file_enriched.file_name}_workspaces.csv", - "offer_as_download": True, - }, - ) - ) - - # Extract downloads data - downloads_data = [] - downloads = data.get("downloads", {}) - - # Create team name lookup for correlation - team_name_lookup = {ws["id"]: ws["name"] for ws in workspaces_data} - - for team_id, team_downloads in downloads.items(): - for download_id, download_info in team_downloads.items(): - downloads_data.append( - { - "id": download_info.get("id", download_id), - "teamId": download_info.get("teamId", team_id), - "team_name": team_name_lookup.get(download_info.get("teamId", team_id), ""), - "userId": download_info.get("userId", ""), - "downloadPath": download_info.get("downloadPath", ""), - "url": download_info.get("url", ""), - "downloadState": download_info.get("downloadState", ""), - "startTime": self._parse_timestamp(download_info.get("startTime")), - "endTime": self._parse_timestamp(download_info.get("endTime")), - } - ) - - # Create downloads CSV - with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8", newline="") as tmp_downloads_csv: - writer = csv.writer(tmp_downloads_csv) - - # Write header + # Write downloads data + for download in downloads_data: writer.writerow( [ - "id", - "teamId", - "team_name", - "userId", - "downloadPath", - "url", - "downloadState", - "startTime", - "endTime", + download["id"], + download["teamId"], + download["team_name"], + download["userId"], + download["downloadPath"], + download["url"], + download["downloadState"], + download["startTime"], + download["endTime"], ] ) - # Write downloads data - for download in downloads_data: - writer.writerow( - [ - download["id"], - download["teamId"], - download["team_name"], - download["userId"], - download["downloadPath"], - download["url"], - download["downloadState"], - download["startTime"], - download["endTime"], - ] - ) + tmp_downloads_csv.flush() + downloads_csv_id = self.storage.upload_file(tmp_downloads_csv.name) - tmp_downloads_csv.flush() - downloads_csv_id = self.storage.upload_file(tmp_downloads_csv.name) - - transforms.append( - Transform( - type="slack_downloads", - object_id=f"{downloads_csv_id}", - metadata={ - "file_name": f"{file_enriched.file_name}_downloads.csv", - "offer_as_download": True, - }, - ) + transforms.append( + Transform( + type="slack_downloads", + object_id=f"{downloads_csv_id}", + metadata={ + "file_name": f"{file_enriched.file_name}_downloads.csv", + "offer_as_download": True, + }, ) + ) - enrichment_result.transforms = transforms - return enrichment_result + enrichment_result.transforms = transforms + return enrichment_result + + except Exception as e: + logger.exception(e, message=f"Error analyzing Slack root-state.json for {file_enriched.file_name}") + return None + + def process(self, object_id: str, file_path: str | None = None) -> EnrichmentResult | None: + """Process Slack root-state.json file. + + Args: + object_id: The object ID of the file + file_path: Optional path to already downloaded file + + Returns: + EnrichmentResult or None if processing fails + """ + try: + file_enriched = get_file_enriched(object_id) + + # Use provided file_path if available, otherwise download + if file_path: + return self._analyze_slack_root_state(file_path, file_enriched) + else: + with self.storage.download(file_enriched.object_id) as temp_file: + return self._analyze_slack_root_state(temp_file.name, file_enriched) except Exception as e: logger.exception(e, message="Error processing Slack root-state.json file") diff --git a/libs/file_enrichment_modules/file_enrichment_modules/sqlite/analyzer.py b/libs/file_enrichment_modules/file_enrichment_modules/sqlite/analyzer.py index a7b875b..ffbaafa 100644 --- a/libs/file_enrichment_modules/file_enrichment_modules/sqlite/analyzer.py +++ b/libs/file_enrichment_modules/file_enrichment_modules/sqlite/analyzer.py @@ -4,14 +4,13 @@ import sqlite3 import tempfile from typing import Any -import structlog +from common.logger import get_logger from common.models import EnrichmentResult, Transform from common.state_helpers import get_file_enriched from common.storage import StorageMinio - from file_enrichment_modules.module_loader import EnrichmentModule -logger = structlog.get_logger(module=__name__) +logger = get_logger(__name__) def safe_str_conversion(value: Any) -> str: @@ -99,61 +98,92 @@ class SqliteParser(EnrichmentModule): # the workflows this module should automatically run in self.workflows = ["default"] - def should_process(self, object_id: str) -> bool: + def should_process(self, object_id: str, file_path: str | None = None) -> bool: """Determine if this module should run.""" file_enriched = get_file_enriched(object_id) should_run = ( "sqlite 3.x database" in file_enriched.magic_type.lower() or file_enriched.file_name.lower().endswith(".sqlite") ) - logger.debug(f"SqliteParser should_run: {should_run}, magic_type: {file_enriched.magic_type.lower()}") - return should_run + return should_run and not file_enriched.is_plaintext + + def _analyze_sqlite_database(self, file_path: str, file_enriched) -> EnrichmentResult | None: + """Analyze SQLite database file and generate enrichment result. + + Args: + file_path: Path to the SQLite database file to analyze + file_enriched: File enrichment data + + Returns: + EnrichmentResult or None if analysis fails + """ + enrichment_result = EnrichmentResult(module_name=self.name, dependencies=self.dependencies) - def process(self, object_id: str) -> EnrichmentResult | None: - """Process SQLite database file using the state store.""" try: - file_enriched = get_file_enriched(object_id) - enrichment_result = EnrichmentResult(module_name=self.name, dependencies=self.dependencies) + # Connect to the SQLite database + conn = sqlite3.connect(file_path) + # Handle binary data properly to avoid UTF-8 decode errors + conn.text_factory = lambda x: x.decode("utf-8", errors="replace") if isinstance(x, bytes) else x + cursor = conn.cursor() - with self.storage.download(file_enriched.object_id) as temp_file: - # Connect to the SQLite database - conn = sqlite3.connect(temp_file.name) - cursor = conn.cursor() + # Get all tables + cursor.execute("SELECT name FROM sqlite_master WHERE type='table';") + tables = [t[0] for t in cursor.fetchall()] - # Get all tables - cursor.execute("SELECT name FROM sqlite_master WHERE type='table';") - tables = [t[0] for t in cursor.fetchall()] + # Process each table + database_data = {} + for table in tables: + database_data[table] = get_table_data(cursor, table) - # Process each table - database_data = {} - for table in tables: - database_data[table] = get_table_data(cursor, table) + conn.close() + # Store the raw parsed data + enrichment_result.results = database_data - conn.close() - # Store the raw parsed data - enrichment_result.results = database_data + # Create human-readable display + with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8") as tmp_display_file: + display = format_sqlite_data(database_data) + tmp_display_file.write(display) + tmp_display_file.flush() - # Create human-readable display - with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8") as tmp_display_file: - display = format_sqlite_data(database_data) - tmp_display_file.write(display) - tmp_display_file.flush() + object_id = self.storage.upload_file(tmp_display_file.name) - object_id = self.storage.upload_file(tmp_display_file.name) - - displayable_parsed = Transform( - type="displayable_parsed", - object_id=f"{object_id}", - metadata={ - "file_name": f"{file_enriched.file_name}.txt", - "display_type_in_dashboard": "monaco", - "default_display": True, - }, - ) - enrichment_result.transforms = [displayable_parsed] + displayable_parsed = Transform( + type="displayable_parsed", + object_id=f"{object_id}", + metadata={ + "file_name": f"{file_enriched.file_name}.txt", + "display_type_in_dashboard": "monaco", + "default_display": True, + }, + ) + enrichment_result.transforms = [displayable_parsed] return enrichment_result + except Exception as e: + logger.exception(e, message=f"Error analyzing SQLite database for {file_enriched.file_name}") + return None + + def process(self, object_id: str, file_path: str | None = None) -> EnrichmentResult | None: + """Process SQLite database file using the state store. + + Args: + object_id: The object ID of the file + file_path: Optional path to already downloaded file + + Returns: + EnrichmentResult or None if processing fails + """ + try: + file_enriched = get_file_enriched(object_id) + + # Use provided file_path if available, otherwise download + if file_path: + return self._analyze_sqlite_database(file_path, file_enriched) + else: + with self.storage.download(file_enriched.object_id) as temp_file: + return self._analyze_sqlite_database(temp_file.name, file_enriched) + except Exception as e: logger.exception(e, message="Error processing SQLite database") return None diff --git a/libs/file_enrichment_modules/file_enrichment_modules/sysprep/analyzer.py b/libs/file_enrichment_modules/file_enrichment_modules/sysprep/analyzer.py index 37ec63f..c343bb0 100644 --- a/libs/file_enrichment_modules/file_enrichment_modules/sysprep/analyzer.py +++ b/libs/file_enrichment_modules/file_enrichment_modules/sysprep/analyzer.py @@ -3,15 +3,14 @@ import tempfile import textwrap from pathlib import Path -import structlog import yara_x +from common.logger import get_logger from common.models import EnrichmentResult, FileObject, Finding, FindingCategory, FindingOrigin, Transform from common.state_helpers import get_file_enriched from common.storage import StorageMinio - from file_enrichment_modules.module_loader import EnrichmentModule -logger = structlog.get_logger(module=__name__) +logger = get_logger(__name__) # Port of https://github.com/NetSPI/PowerHuntShares/blob/46238ba37dc85f65f2c1d7960f551ea3d80c236a/Scripts/ConfigParsers/parser-sysprep.inf.ps1 # Original Author: Scott Sutherland, NetSPI (@_nullbind / nullbind) @@ -22,9 +21,12 @@ class SysprepParser(EnrichmentModule): def __init__(self): super().__init__("sysprep_parser") self.storage = StorageMinio() + # the workflows this module should automatically run in self.workflows = ["default"] + self.size_limit = 5_000_000 # 5MB size limit + # Yara rule for sysprep.inf detection self.yara_rule = yara_x.compile(""" rule Windows_Unattended_Answer_File { @@ -65,7 +67,7 @@ rule Windows_Unattended_Answer_File { } """) - def should_process(self, object_id: str) -> bool: + def should_process(self, object_id: str, file_path: str | None = None) -> bool: """Determine if this module should run based on file type.""" file_enriched = get_file_enriched(object_id) @@ -73,11 +75,17 @@ rule Windows_Unattended_Answer_File { if not (file_enriched.is_plaintext and file_enriched.file_name.lower() == "sysprep.inf"): return False - # Run Yara scan - file_bytes = self.storage.download_bytes(file_enriched.object_id) - should_run = len(self.yara_rule.scan(file_bytes).matching_rules) > 0 + if file_path: + # Use provided file path - read only the needed bytes + with open(file_path, "rb") as f: + num_bytes = min(file_enriched.size, self.size_limit) + file_bytes = f.read(num_bytes) + else: + # Fallback to downloading the file itself + num_bytes = file_enriched.size if file_enriched.size < self.size_limit else self.size_limit + file_bytes = self.storage.download_bytes(file_enriched.object_id, length=num_bytes) - logger.debug(f"SysprepParser should_run: {should_run}, file: {file_enriched.file_name}") + should_run = len(self.yara_rule.scan(file_bytes).matching_rules) > 0 return should_run def _parse_sysprep_config(self, config_content: str) -> dict: @@ -146,79 +154,108 @@ rule Windows_Unattended_Answer_File { # Return True if any credential is present and not a placeholder return any(cred for cred in credentials if cred and not is_placeholder(cred)) - def process(self, object_id: str) -> EnrichmentResult | None: - """Process sysprep config file and extract credentials.""" + def _analyze_sysprep(self, file_path: str, file_enriched) -> EnrichmentResult | None: + """Analyze sysprep config file and generate enrichment result. + + Args: + file_path: Path to the sysprep config file to analyze + file_enriched: File enrichment data + + Returns: + EnrichmentResult or None if analysis fails + """ + enrichment_result = EnrichmentResult(module_name=self.name, dependencies=self.dependencies) + + try: + content = Path(file_path).read_text(encoding="utf-8") + + # Parse the configuration + config = self._parse_sysprep_config(content) + + # Only create finding if real credentials are present + if self._has_real_credentials(config): + # Create finding summary + summary_markdown = self._create_finding_summary(config) + + # Create display data + display_data = FileObject(type="finding_summary", metadata={"summary": summary_markdown}) + + # Create finding + finding = Finding( + category=FindingCategory.CREDENTIAL, + finding_name="sysprep_credentials_detected", + origin_type=FindingOrigin.ENRICHMENT_MODULE, + origin_name=self.name, + object_id=file_enriched.object_id, + severity=8, + raw_data={"config": config}, + data=[display_data], + ) + + # Add finding to enrichment result + enrichment_result.findings = [finding] + + enrichment_result.results = config + + # Create a displayable version of the results + with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8") as tmp_display_file: + # Convert config to YAML with custom formatting + yaml_output = [] + yaml_output.append("Sysprep Configuration Analysis") + yaml_output.append("===========================\n") + + for section, values in config.items(): + yaml_output.append(f"{section}:") + for key, value in values.items(): + # Highlight sensitive fields + if key in ["AdminPassword", "DomainAdmin", "DomainAdminPassword"]: + yaml_output.append(f" {key}: !!! {value} !!!") + else: + yaml_output.append(f" {key}: {value}") + yaml_output.append("") # Add empty line between sections + + display = textwrap.indent("\n".join(yaml_output), " ") + tmp_display_file.write(display) + tmp_display_file.flush() + + object_id = self.storage.upload_file(tmp_display_file.name) + + displayable_parsed = Transform( + type="displayable_parsed", + object_id=f"{object_id}", + metadata={ + "file_name": f"{file_enriched.file_name}_analysis.txt", + "display_type_in_dashboard": "monaco", + "default_display": True, + }, + ) + + enrichment_result.transforms = [displayable_parsed] + return enrichment_result + + except Exception as e: + logger.exception(e, message=f"Error analyzing sysprep config for {file_enriched.file_name}") + return None + + def process(self, object_id: str, file_path: str | None = None) -> EnrichmentResult | None: + """Process sysprep config file and extract credentials. + + Args: + object_id: The object ID of the file + file_path: Optional path to already downloaded file + + Returns: + EnrichmentResult or None if processing fails + """ try: file_enriched = get_file_enriched(object_id) - enrichment_result = EnrichmentResult(module_name=self.name, dependencies=self.dependencies) - # Download and read the file - with self.storage.download(file_enriched.object_id) as temp_file: - content = Path(temp_file.name).read_text(encoding="utf-8") - - # Parse the configuration - config = self._parse_sysprep_config(content) - - # Only create finding if real credentials are present - if self._has_real_credentials(config): - # Create finding summary - summary_markdown = self._create_finding_summary(config) - - # Create display data - display_data = FileObject(type="finding_summary", metadata={"summary": summary_markdown}) - - # Create finding - finding = Finding( - category=FindingCategory.CREDENTIAL, - finding_name="sysprep_credentials_detected", - origin_type=FindingOrigin.ENRICHMENT_MODULE, - origin_name=self.name, - object_id=file_enriched.object_id, - severity=8, - raw_data={"config": config}, - data=[display_data], - ) - - # Add finding to enrichment result - enrichment_result.findings = [finding] - - enrichment_result.results = config - - # Create a displayable version of the results - with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8") as tmp_display_file: - # Convert config to YAML with custom formatting - yaml_output = [] - yaml_output.append("Sysprep Configuration Analysis") - yaml_output.append("===========================\n") - - for section, values in config.items(): - yaml_output.append(f"{section}:") - for key, value in values.items(): - # Highlight sensitive fields - if key in ["AdminPassword", "DomainAdmin", "DomainAdminPassword"]: - yaml_output.append(f" {key}: !!! {value} !!!") - else: - yaml_output.append(f" {key}: {value}") - yaml_output.append("") # Add empty line between sections - - display = textwrap.indent("\n".join(yaml_output), " ") - tmp_display_file.write(display) - tmp_display_file.flush() - - object_id = self.storage.upload_file(tmp_display_file.name) - - displayable_parsed = Transform( - type="displayable_parsed", - object_id=f"{object_id}", - metadata={ - "file_name": f"{file_enriched.file_name}_analysis.txt", - "display_type_in_dashboard": "monaco", - "default_display": True, - }, - ) - enrichment_result.transforms = [displayable_parsed] - - return enrichment_result + # Use provided file_path if available, otherwise download + if file_path: + return self._analyze_sysprep(file_path, file_enriched) + else: + with self.storage.download(file_enriched.object_id) as temp_file: + return self._analyze_sysprep(temp_file.name, file_enriched) except Exception as e: logger.exception(e, message="Error processing sysprep config file") diff --git a/libs/file_enrichment_modules/file_enrichment_modules/text_summarizer/analyzer.py b/libs/file_enrichment_modules/file_enrichment_modules/text_summarizer/analyzer.py deleted file mode 100644 index b86dc6e..0000000 --- a/libs/file_enrichment_modules/file_enrichment_modules/text_summarizer/analyzer.py +++ /dev/null @@ -1,288 +0,0 @@ -# enrichment_modules/text_summarizer/analyzer.py -import asyncio -import json -import logging -import os -import tempfile -from typing import Annotated, Optional - -import psycopg -import rigging as rg -import structlog -from common.models import EnrichmentResult, Transform -from common.state_helpers import get_file_enriched -from common.storage import StorageMinio -from dapr.clients import DaprClient -from pydantic import StringConstraints - -from file_enrichment_modules.module_loader import EnrichmentModule - -logger = structlog.get_logger(module=__name__) - -str_strip = Annotated[str, StringConstraints(strip_whitespace=True)] - - -class Summary(rg.Model): - content: str_strip - - -class TextSummarizer(EnrichmentModule): - def __init__(self): - super().__init__("text_summarizer") - self.storage = StorageMinio() - - logging.getLogger("litellm").setLevel(logging.INFO) # not working how it should... - - # Check if rigging generator config is available - self.rigging_generator = os.getenv("RIGGING_GENERATOR_SUMMARY") - if not self.rigging_generator: - logger.info("RIGGING_GENERATOR_SUMMARY environment variable not set - text summarization disabled") - - with DaprClient() as client: - secret = client.get_secret(store_name="nemesis-secret-store", key="POSTGRES_CONNECTION_STRING") - self.postgres_connection_string = secret.secret["POSTGRES_CONNECTION_STRING"] - - def _has_extracted_text_transform(self, object_id: str) -> tuple[bool, Optional[str]]: - """ - Check if file has an extracted_text transform. - Returns a tuple of (has_transform, transform_object_id) - """ - try: - with psycopg.connect(self.postgres_connection_string) as conn: - with conn.cursor() as cur: - cur.execute( - """ - SELECT transform_object_id - FROM transforms - WHERE object_id = %s AND type = 'extracted_text' - LIMIT 1 - """, - (object_id,), - ) - result = cur.fetchone() - if result: - return True, str(result[0]) - return False, None - except Exception as e: - logger.error(f"Error checking for extracted_text transform: {e}") - return False, None - - def should_process(self, object_id: str) -> bool: - """Determine if this module should run.""" - if not self.rigging_generator: - return False - - file_enriched = get_file_enriched(object_id) - - # Process if file is plaintext or has extracted_text transform - has_transform, _ = self._has_extracted_text_transform(object_id) - return file_enriched.is_plaintext or has_transform - - def _get_text_content(self, object_id: str) -> tuple[Optional[str], Optional[str]]: - """ - Get plaintext content from file or extracted text transform. - Returns a tuple of (content, source_object_id) - """ - try: - # Check if file is plaintext first - file_enriched = get_file_enriched(object_id) - - if file_enriched.is_plaintext: - try: - file_bytes = self.storage.download_bytes(object_id) - return file_bytes.decode("utf-8", errors="replace"), object_id - except Exception as e: - logger.warning(f"Failed to decode plaintext file content: {e}") - - # If not plaintext or decode failed, look for extracted_text transform - has_transform, transform_object_id = self._has_extracted_text_transform(object_id) - if has_transform and transform_object_id: - try: - transform_bytes = self.storage.download_bytes(transform_object_id) - return transform_bytes.decode("utf-8", errors="replace"), transform_object_id - except Exception as e: - logger.error(f"Failed to get extracted text transform content: {e}") - - return None, None - - except Exception as e: - logger.error(f"Error getting text content: {e}") - return None, None - - async def _generate_summary(self, text_content: str) -> str: - """Async function to generate summary using rigging.""" - max_retries = 3 - attempt = 0 - - while attempt < max_retries: - try: - generator = rg.get_generator(self.rigging_generator) - - response = await generator.chat( - [ - { - "role": "system", - "content": """You are a document summarization assistant. Create a concise but thorough - summary of the provided text. Focus on key points and main ideas. Include section headers - to organize the summary. Use markdown formatting.""", - }, - { - "role": "user", - "content": f"Please summarize this text, outputing the summary between {Summary.xml_start_tag()}{Summary.xml_end_tag()} tags:\n\n{text_content}", - }, - ] - ).run() - - summary = response.last.try_parse(Summary) - logger.info("Successfully generated summary") - return summary.content - - except Exception as e: - # Try to check if it's error 529 - need to handle different exception types - error_str = str(e).lower() - - # Look for indications of a 529 error in the error message or response - if "529" in error_str or "too many requests" in error_str or "rate limit" in error_str: - attempt += 1 - if attempt < max_retries: - wait_time = 15 - logger.warning( - f"Received what appears to be a rate limit error, waiting {wait_time} seconds before retry (attempt {attempt}/{max_retries})" - ) - await asyncio.sleep(wait_time) - else: - logger.error("Max retries reached after apparent rate limit errors") - raise - else: - # For any other error, log and re-raise immediately - logger.exception(e, message="Error generating summary") - - def _get_original_file_id(self, transform_object_id: str) -> Optional[str]: - """ - Find the original file ID for an extracted text transform by checking - which file has this transform_object_id in its transforms. - """ - try: - with psycopg.connect(self.postgres_connection_string) as conn: - with conn.cursor() as cur: - cur.execute( - """ - SELECT object_id - FROM transforms - WHERE transform_object_id = %s AND type = 'extracted_text' - LIMIT 1 - """, - (transform_object_id,), - ) - result = cur.fetchone() - if result: - return str(result[0]) - return None - except Exception as e: - logger.exception(e, message="Error finding original file for transform") - return None - - def process(self, object_id: str) -> EnrichmentResult | None: - """Process text content and generate summary using LLM.""" - try: - # First, check if this is an extracted text file or a regular file - # If it's an extracted_text transform, we need to find its original file - original_file_id = None - - # Check if current object is already a transform (extracted_text) - with psycopg.connect(self.postgres_connection_string) as conn: - with conn.cursor() as cur: - cur.execute( - """ - SELECT object_id FROM transforms - WHERE transform_object_id = %s AND type = 'extracted_text' - """, - (object_id,), - ) - result = cur.fetchone() - if result: - # This means we're processing an extracted_text file directly - # We should use the original file ID for our transform - original_file_id = str(result[0]) - logger.info(f"Processing extracted_text file. Original file is: {original_file_id}") - - # Get the text content to summarize - text_content, source_object_id = self._get_text_content(object_id) - if not text_content or not source_object_id: - logger.error("No text content found to summarize") - return None - - # Create the summary - summary = asyncio.run(self._generate_summary(text_content)) - - # Store the summary as a file - with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8") as tmp_summary: - tmp_summary.write(summary) - tmp_summary.flush() - summary_id = self.storage.upload_file(tmp_summary.name) - - # IMPORTANT CHANGE: Use the original_file_id (if found) for attaching the transform - # This ensures the summary is attached to the original file, not the extracted text - target_file_id = original_file_id if original_file_id else object_id - - # Create transform object - summary_transform = Transform( - type="text_summary", - object_id=f"{summary_id}", - metadata={ - "file_name": "text_summary.md", - "display_type_in_dashboard": "markdown", - "display_title": "Text Summary", - "default_display": True, - }, - ) - - # Create the result with the transform - enrichment_result = EnrichmentResult( - module_name=self.name, dependencies=self.dependencies, transforms=[summary_transform] - ) - - # Override the object_id in the result to use the original file - # This is a key fix - we're changing where the transform gets attached - if original_file_id: - logger.info(f"Attaching summary transform to original file: {original_file_id}") - - metadata = summary_transform.metadata or {} - - self._add_transform_to_file(original_file_id, "text_summary", f"{summary_id}", metadata) - - # Return None since we manually added the transform - # This prevents the transform from being added to the extracted text file - return None - - return enrichment_result - - except Exception as e: - logger.exception(e, message="Error generating text summary") - return None - - def _add_transform_to_file(self, object_id: str, transform_type: str, transform_object_id: str, metadata: dict): - """Manually add a transform to a file in the database.""" - try: - with psycopg.connect(self.postgres_connection_string) as conn: - with conn.cursor() as cur: - cur.execute( - """ - INSERT INTO transforms (object_id, type, transform_object_id, metadata) - VALUES (%s, %s, %s, %s) - """, - ( - object_id, - transform_type, - transform_object_id, - json.dumps(metadata) if metadata else None, - ), - ) - conn.commit() - logger.info(f"Added transform {transform_type} to file {object_id}") - except Exception as e: - logger.exception(e, message=f"Error adding transform to file {object_id}") - - -def create_enrichment_module() -> EnrichmentModule: - return TextSummarizer() diff --git a/libs/file_enrichment_modules/file_enrichment_modules/unattend_xml/analyzer.py b/libs/file_enrichment_modules/file_enrichment_modules/unattend_xml/analyzer.py index cecba67..4dc87f7 100644 --- a/libs/file_enrichment_modules/file_enrichment_modules/unattend_xml/analyzer.py +++ b/libs/file_enrichment_modules/file_enrichment_modules/unattend_xml/analyzer.py @@ -3,17 +3,15 @@ import base64 import tempfile import xml.etree.ElementTree as ET from pathlib import Path -from typing import Optional -import structlog import yara_x +from common.logger import get_logger from common.models import EnrichmentResult, FileObject, Finding, FindingCategory, FindingOrigin, Transform from common.state_helpers import get_file_enriched from common.storage import StorageMinio - from file_enrichment_modules.module_loader import EnrichmentModule -logger = structlog.get_logger(module=__name__) +logger = get_logger(__name__) # Port of https://github.com/NetSPI/PowerHuntShares/blob/46238ba37dc85f65f2c1d7960f551ea3d80c236a/Scripts/ConfigParsers/parser-unattend.xml.ps1 # Original Author: Scott Sutherland, NetSPI (@_nullbind / nullbind) @@ -24,9 +22,12 @@ class UnattendParser(EnrichmentModule): def __init__(self): super().__init__("unattend_parser") self.storage = StorageMinio() + # the workflows this module should automatically run in self.workflows = ["default"] + self.size_limit = 5_000_000 # 5MB size limit + # Yara rule to detect unattend.xml files self.yara_rule = yara_x.compile(""" rule Detect_Windows_Unattend_XML { @@ -63,7 +64,7 @@ rule Detect_Windows_Unattend_XML { } """) - def should_process(self, object_id: str) -> bool: + def should_process(self, object_id: str, file_path: str | None = None) -> bool: """Determine if this module should run based on file type.""" file_enriched = get_file_enriched(object_id) @@ -71,14 +72,20 @@ rule Detect_Windows_Unattend_XML { if not (file_enriched.is_plaintext and file_enriched.file_name.lower() == "unattend.xml"): return False - # Run Yara check - file_bytes = self.storage.download_bytes(file_enriched.object_id) - should_run = len(self.yara_rule.scan(file_bytes).matching_rules) > 0 + if file_path: + # Use provided file path - read only the needed bytes + with open(file_path, "rb") as f: + num_bytes = min(file_enriched.size, self.size_limit) + file_bytes = f.read(num_bytes) + else: + # Fallback to downloading the file itself + num_bytes = file_enriched.size if file_enriched.size < self.size_limit else self.size_limit + file_bytes = self.storage.download_bytes(file_enriched.object_id, length=num_bytes) - logger.debug(f"UnattendParser should_run: {should_run}") + should_run = len(self.yara_rule.scan(file_bytes).matching_rules) > 0 return should_run - def _decode_password(self, password_value: str, is_plaintext: bool) -> Optional[str]: + def _decode_password(self, password_value: str, is_plaintext: bool) -> str | None: """Decode base64 password if needed.""" if not is_plaintext: try: @@ -139,69 +146,146 @@ rule Detect_Windows_Unattend_XML { return summary - def process(self, object_id: str) -> EnrichmentResult | None: - """Process unattend.xml file and extract credentials.""" + def _analyze_unattend_xml(self, file_path: str, file_enriched) -> EnrichmentResult | None: + """Analyze unattend.xml file and generate enrichment result. + + Args: + file_path: Path to the unattend.xml file to analyze + file_enriched: File enrichment data + + Returns: + EnrichmentResult or None if analysis fails + """ + enrichment_result = EnrichmentResult(module_name=self.name, dependencies=self.dependencies) + + try: + content = Path(file_path).read_text(encoding="utf-8") + + # Parse the XML and extract credentials + credentials = self._parse_unattend_xml(content) + + if credentials: + # Create finding summary + summary_markdown = self._create_finding_summary(credentials) + + # Create display data + display_data = FileObject(type="finding_summary", metadata={"summary": summary_markdown}) + + # Create finding + finding = Finding( + category=FindingCategory.CREDENTIAL, + finding_name="unattend_credentials_detected", + origin_type=FindingOrigin.ENRICHMENT_MODULE, + origin_name=self.name, + object_id=file_enriched.object_id, + severity=8, + raw_data={"credentials": credentials}, + data=[display_data], + ) + + enrichment_result.findings = [finding] + enrichment_result.results = {"credentials": credentials} + + # Create a displayable version of the results + with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8") as tmp_display_file: + display = "Windows Unattend.xml Analysis\n" + display += "==========================\n\n" + + for cred in credentials: + display += f"Source: {cred['source']}\n" + display += f"Username: {cred['username']}\n" + display += f"Password: {cred['password']}\n" + display += "-" * 40 + "\n" + + tmp_display_file.write(display) + tmp_display_file.flush() + + object_id = self.storage.upload_file(tmp_display_file.name) + + displayable_parsed = Transform( + type="displayable_parsed", + object_id=f"{object_id}", + metadata={ + "file_name": f"{file_enriched.file_name}_analysis.txt", + "display_type_in_dashboard": "monaco", + "default_display": True, + }, + ) + enrichment_result.transforms = [displayable_parsed] + + # Create finding summary + summary_markdown = self._create_finding_summary(credentials) + + # Create display data + display_data = FileObject(type="finding_summary", metadata={"summary": summary_markdown}) + + # Create finding + finding = Finding( + category=FindingCategory.CREDENTIAL, + finding_name="unattend_credentials_detected", + origin_type=FindingOrigin.ENRICHMENT_MODULE, + origin_name=self.name, + object_id=file_enriched.object_id, + severity=8, + raw_data={"credentials": credentials}, + data=[display_data], + ) + + enrichment_result.findings = [finding] + enrichment_result.results = {"credentials": credentials} + + # Create a displayable version of the results + with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8") as tmp_display_file: + display = "Windows Unattend.xml Analysis\n" + display += "==========================\n\n" + + for cred in credentials: + display += f"Source: {cred['source']}\n" + display += f"Username: {cred['username']}\n" + display += f"Password: {cred['password']}\n" + display += "-" * 40 + "\n" + + tmp_display_file.write(display) + tmp_display_file.flush() + + object_id = self.storage.upload_file(tmp_display_file.name) + + displayable_parsed = Transform( + type="displayable_parsed", + object_id=f"{object_id}", + metadata={ + "file_name": f"{file_enriched.file_name}_analysis.txt", + "display_type_in_dashboard": "monaco", + "default_display": True, + }, + ) + enrichment_result.transforms = [displayable_parsed] + + return enrichment_result + + except Exception as e: + logger.exception(e, message=f"Error analyzing unattend.xml for {file_enriched.file_name}") + return None + + def process(self, object_id: str, file_path: str | None = None) -> EnrichmentResult | None: + """Process unattend.xml file and extract credentials. + + Args: + object_id: The object ID of the file + file_path: Optional path to already downloaded file + + Returns: + EnrichmentResult or None if processing fails + """ try: file_enriched = get_file_enriched(object_id) - enrichment_result = EnrichmentResult(module_name=self.name, dependencies=self.dependencies) - # Download and read the file - with self.storage.download(file_enriched.object_id) as temp_file: - content = Path(temp_file.name).read_text(encoding="utf-8") - - # Parse the XML and extract credentials - credentials = self._parse_unattend_xml(content) - - if credentials: - # Create finding summary - summary_markdown = self._create_finding_summary(credentials) - - # Create display data - display_data = FileObject(type="finding_summary", metadata={"summary": summary_markdown}) - - # Create finding - finding = Finding( - category=FindingCategory.CREDENTIAL, - finding_name="unattend_credentials_detected", - origin_type=FindingOrigin.ENRICHMENT_MODULE, - origin_name=self.name, - object_id=file_enriched.object_id, - severity=8, - raw_data={"credentials": credentials}, - data=[display_data], - ) - - enrichment_result.findings = [finding] - enrichment_result.results = {"credentials": credentials} - - # Create a displayable version of the results - with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8") as tmp_display_file: - display = "Windows Unattend.xml Analysis\n" - display += "==========================\n\n" - - for cred in credentials: - display += f"Source: {cred['source']}\n" - display += f"Username: {cred['username']}\n" - display += f"Password: {cred['password']}\n" - display += "-" * 40 + "\n" - - tmp_display_file.write(display) - tmp_display_file.flush() - - object_id = self.storage.upload_file(tmp_display_file.name) - - displayable_parsed = Transform( - type="displayable_parsed", - object_id=f"{object_id}", - metadata={ - "file_name": f"{file_enriched.file_name}_analysis.txt", - "display_type_in_dashboard": "monaco", - "default_display": True, - }, - ) - enrichment_result.transforms = [displayable_parsed] - - return enrichment_result + # Use provided file_path if available, otherwise download + if file_path: + return self._analyze_unattend_xml(file_path, file_enriched) + else: + with self.storage.download(file_enriched.object_id) as temp_file: + return self._analyze_unattend_xml(temp_file.name, file_enriched) except Exception as e: logger.exception(e, message="Error processing unattend.xml file") diff --git a/libs/file_enrichment_modules/file_enrichment_modules/vnc_ini/analyzer.py b/libs/file_enrichment_modules/file_enrichment_modules/vnc_ini/analyzer.py index 09c2b5f..1593e54 100644 --- a/libs/file_enrichment_modules/file_enrichment_modules/vnc_ini/analyzer.py +++ b/libs/file_enrichment_modules/file_enrichment_modules/vnc_ini/analyzer.py @@ -2,17 +2,15 @@ import tempfile import textwrap from pathlib import Path -from typing import Optional -import structlog +from common.logger import get_logger from common.models import EnrichmentResult, FileObject, Finding, FindingCategory, FindingOrigin, Transform from common.state_helpers import get_file_enriched from common.storage import StorageMinio from Crypto.Cipher import DES - from file_enrichment_modules.module_loader import EnrichmentModule -logger = structlog.get_logger(module=__name__) +logger = get_logger(__name__) # Port of https://github.com/NetSPI/PowerHuntShares/blob/46238ba37dc85f65f2c1d7960f551ea3d80c236a/Scripts/ConfigParsers/parser-vnc.ini.ps1 # Original Author: Scott Sutherland, NetSPI (@_nullbind / nullbind) @@ -23,24 +21,24 @@ class VncParser(EnrichmentModule): def __init__(self): super().__init__("vnc_parser") self.storage = StorageMinio() + # Define the fixed DES key used by VNC self.des_key = bytes([0x23, 0x52, 0x6A, 0x3B, 0x58, 0x92, 0x67, 0x34]) + # the workflows this module should automatically run in self.workflows = ["default"] - def should_process(self, state_key: str) -> bool: + def should_process(self, object_id: str, file_path: str | None = None) -> bool: """Determine if this module should run based on file type.""" - file_enriched = get_file_enriched(state_key) - # Check if file appears to be a VNC config file + file_enriched = get_file_enriched(object_id) should_run = ( file_enriched.file_name.lower().endswith(".ini") and "vnc" in file_enriched.file_name.lower() and "text" in file_enriched.magic_type.lower() ) - logger.debug(f"VncParser should_run: {should_run}, magic_type: {file_enriched.magic_type.lower()}") return should_run - def _decrypt_password(self, encrypted_hex: str) -> Optional[str]: + def _decrypt_password(self, encrypted_hex: str) -> str | None: """Decrypt the VNC password using the fixed DES key.""" try: # Convert hex string to bytes @@ -102,87 +100,116 @@ class VncParser(EnrichmentModule): return config - def process(self, state_key: str) -> EnrichmentResult | None: - """Process VNC config file and decrypt password if present.""" + def _analyze_vnc_config(self, file_path: str, file_enriched) -> EnrichmentResult | None: + """Analyze VNC config file and generate enrichment result. + + Args: + file_path: Path to the VNC config file to analyze + file_enriched: File enrichment data + + Returns: + EnrichmentResult or None if analysis fails + """ + enrichment_result = EnrichmentResult(module_name=self.name, dependencies=self.dependencies) + try: - file_enriched = get_file_enriched(state_key) - enrichment_result = EnrichmentResult(module_name=self.name, dependencies=self.dependencies) + content = Path(file_path).read_text(encoding="utf-8") - # Download and read the file - with self.storage.download(file_enriched.object_id) as temp_file: - content = Path(temp_file.name).read_text(encoding="utf-8") + # Parse the configuration + config = self._parse_vnc_config(content) - # Parse the configuration - config = self._parse_vnc_config(content) + # Extract and decrypt password if present + server_config = config.get("Server", {}) + if "Password" in server_config: + decrypted_password = self._decrypt_password(server_config["Password"]) + if decrypted_password: + server_config["DecryptedPassword"] = decrypted_password - # Extract and decrypt password if present - server_config = config.get("Server", {}) - if "Password" in server_config: - decrypted_password = self._decrypt_password(server_config["Password"]) - if decrypted_password: - server_config["DecryptedPassword"] = decrypted_password + # Create finding summary + summary_markdown = self._create_finding_summary(config, decrypted_password) - # Create finding summary - summary_markdown = self._create_finding_summary(config, decrypted_password) + # Create display data + display_data = FileObject(type="finding_summary", metadata={"summary": summary_markdown}) - # Create display data - display_data = FileObject(type="finding_summary", metadata={"summary": summary_markdown}) - - # Create finding - finding = Finding( - category=FindingCategory.CREDENTIAL, - finding_name="vnc_password_detected", - origin_type=FindingOrigin.ENRICHMENT_MODULE, - origin_name=self.name, - object_id=file_enriched.object_id, - severity=7, - raw_data={"config": config}, - data=[display_data], - ) - - # Add finding to enrichment result - if not enrichment_result.findings: - enrichment_result.findings = [] - enrichment_result.findings.append(finding) - - enrichment_result.results = config - - # Create a displayable version of the results - with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8") as tmp_display_file: - # Convert config to YAML with custom formatting - yaml_output = [] - yaml_output.append("VNC Configuration Analysis") - yaml_output.append("========================\n") - - for section, values in config.items(): - yaml_output.append(f"{section}:") - for key, value in values.items(): - # Highlight the decrypted password if present - if key == "DecryptedPassword": - yaml_output.append(f" {key}: !!! {value} !!!") - else: - yaml_output.append(f" {key}: {value}") - yaml_output.append("") # Add empty line between sections - - display = textwrap.indent("\n".join(yaml_output), " ") - tmp_display_file.write(display) - tmp_display_file.flush() - - object_id = self.storage.upload_file(tmp_display_file.name) - - displayable_parsed = Transform( - type="displayable_parsed", - object_id=f"{object_id}", - metadata={ - "file_name": f"{file_enriched.file_name}_analysis.txt", - "display_type_in_dashboard": "monaco", - "default_display": True, - }, + # Create finding + finding = Finding( + category=FindingCategory.CREDENTIAL, + finding_name="vnc_password_detected", + origin_type=FindingOrigin.ENRICHMENT_MODULE, + origin_name=self.name, + object_id=file_enriched.object_id, + severity=7, + raw_data={"config": config}, + data=[display_data], ) - enrichment_result.transforms = [displayable_parsed] + + # Add finding to enrichment result + if not enrichment_result.findings: + enrichment_result.findings = [] + enrichment_result.findings.append(finding) + + enrichment_result.results = config + + # Create a displayable version of the results + with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8") as tmp_display_file: + # Convert config to YAML with custom formatting + yaml_output = [] + yaml_output.append("VNC Configuration Analysis") + yaml_output.append("========================\n") + + for section, values in config.items(): + yaml_output.append(f"{section}:") + for key, value in values.items(): + # Highlight the decrypted password if present + if key == "DecryptedPassword": + yaml_output.append(f" {key}: !!! {value} !!!") + else: + yaml_output.append(f" {key}: {value}") + yaml_output.append("") # Add empty line between sections + + display = textwrap.indent("\n".join(yaml_output), " ") + tmp_display_file.write(display) + tmp_display_file.flush() + + object_id = self.storage.upload_file(tmp_display_file.name) + + displayable_parsed = Transform( + type="displayable_parsed", + object_id=f"{object_id}", + metadata={ + "file_name": f"{file_enriched.file_name}_analysis.txt", + "display_type_in_dashboard": "monaco", + "default_display": True, + }, + ) + enrichment_result.transforms = [displayable_parsed] return enrichment_result + except Exception as e: + logger.exception(e, message=f"Error analyzing VNC config for {file_enriched.file_name}") + return None + + def process(self, object_id: str, file_path: str | None = None) -> EnrichmentResult | None: + """Process VNC config file and decrypt password if present. + + Args: + object_id: The object ID of the file + file_path: Optional path to already downloaded file + + Returns: + EnrichmentResult or None if processing fails + """ + try: + file_enriched = get_file_enriched(object_id) + + # Use provided file_path if available, otherwise download + if file_path: + return self._analyze_vnc_config(file_path, file_enriched) + else: + with self.storage.download(file_enriched.object_id) as temp_file: + return self._analyze_vnc_config(temp_file.name, file_enriched) + except Exception as e: logger.exception(e, message="Error processing VNC config file") return None diff --git a/libs/file_enrichment_modules/file_enrichment_modules/yara/analyzer.py b/libs/file_enrichment_modules/file_enrichment_modules/yara/analyzer.py index f91bc4f..c220ec3 100644 --- a/libs/file_enrichment_modules/file_enrichment_modules/yara/analyzer.py +++ b/libs/file_enrichment_modules/file_enrichment_modules/yara/analyzer.py @@ -1,15 +1,14 @@ import base64 import binascii -import structlog +from common.logger import get_logger from common.models import EnrichmentResult, FileObject, Finding, FindingCategory, FindingOrigin from common.state_helpers import get_file_enriched from common.storage import StorageMinio - from file_enrichment_modules.module_loader import EnrichmentModule from file_enrichment_modules.yara.yara_manager import YaraRuleManager -logger = structlog.get_logger(module=__name__) +logger = get_logger(__name__) def yara_match_to_markdown(match): @@ -17,7 +16,7 @@ def yara_match_to_markdown(match): f"# Yara Rule: {match['rule_name']}", ] try: - if 'rule_description' in match and match['rule_description']: + if "rule_description" in match and match["rule_description"]: markdown.append(f"{match['rule_description']}\n") markdown.append('### Matches\nMatching strings in the form of ": ".') @@ -34,7 +33,7 @@ def yara_match_to_markdown(match): markdown.append("```\n") - if 'rule_text' in match and match['rule_text']: + if "rule_text" in match and match["rule_text"]: markdown.extend(["# Rule Text", f"```yara\n{match['rule_text']}"]) else: markdown.extend(["# Rule Text", "```text\n*Rule text not available*"]) @@ -60,116 +59,137 @@ class YaraScanner(EnrichmentModule): # the workflows this module should automatically run in self.workflows = ["default"] - def should_process(self, object_id: str) -> bool: + def should_process(self, object_id: str, file_path: str | None = None) -> bool: """Always returns True as Yara scanning should run on all files.""" return True - def process(self, object_id: str) -> EnrichmentResult | None: - """Process file using Yara scanning.""" - try: - # Get the current file_enriched from the database backend - file_enriched = get_file_enriched(object_id) + def _analyze_yara(self, file_path: str, file_enriched) -> EnrichmentResult | None: + """Analyze file using Yara rules and generate enrichment result. - with self.storage.download(file_enriched.object_id) as file: - # Get scan results - scan_results = self.rule_manager.match(file.name) + Args: + file_path: Path to the file to analyze with Yara + file_enriched: File enrichment data - enrichment_result = EnrichmentResult(module_name=self.name) + Returns: + EnrichmentResult or None if analysis fails + """ + # Get scan results + scan_results = self.rule_manager.match(file_path) - yara_matches = [] - for rule in scan_results: - rule_text = self.rule_manager.get_rule_content(rule.identifier) - yara_match = {"rule_name": rule.identifier, "rule_string_matches": [], "rule_text": rule_text} + enrichment_result = EnrichmentResult(module_name=self.name) - # Add metadata if available - metadata_dict = dict(rule.metadata) - if "description" in metadata_dict: - yara_match["rule_description"] = metadata_dict["description"] + yara_matches = [] + for rule in scan_results: + rule_text = self.rule_manager.get_rule_content(rule.identifier) + yara_match = {"rule_name": rule.identifier, "rule_string_matches": [], "rule_text": rule_text} - # Process patterns (strings in yara-x) - for pattern in rule.patterns: - if pattern.matches: # Only process patterns that had matches - string_match = { - "identifier": pattern.identifier, - "yara_string_match_instances": [], - } + # Add metadata if available + metadata_dict = dict(rule.metadata) + if "description" in metadata_dict: + yara_match["rule_description"] = metadata_dict["description"] - for match in pattern.matches: - if match.length < 1000: - # Read the matched data from the file - with open(file.name, "rb") as f: - f.seek(match.offset) - matched_data = f.read(match.length) + # Process patterns (strings in yara-x) + for pattern in rule.patterns: + if pattern.matches: # Only process patterns that had matches + string_match = { + "identifier": pattern.identifier, + "yara_string_match_instances": [], + } - string_match_instance = { - "offset": match.offset, - "length": match.length, - } + for match in pattern.matches: + if match.length < 1000: + # Read the matched data from the file + with open(file_path, "rb") as f: + f.seek(match.offset) + matched_data = f.read(match.length) - # Always include base64 representation for compatibility - string_match_instance["matched_data_b64"] = base64.b64encode( - matched_data - ).decode("utf-8") + string_match_instance = { + "offset": match.offset, + "length": match.length, + } - # Format differently based on file type - if hasattr(file_enriched, "is_plaintext") and file_enriched.is_plaintext: - try: - # Try to decode as UTF-8 - string_match_instance["matched_data_text"] = matched_data.decode( - "utf-8" - ) - except UnicodeDecodeError: - try: - # Fallback to a more lenient encoding - string_match_instance["matched_data_text"] = matched_data.decode( - "unicode_escape" - ) - except: - # If both decodings fail, use hex format - string_match_instance["matched_data_hex"] = format_hex_like_xxd( - matched_data - ) - else: - # Binary file - format as hex + # Always include base64 representation for compatibility + string_match_instance["matched_data_b64"] = base64.b64encode(matched_data).decode( + "utf-8" + ) + + # Format differently based on file type + if hasattr(file_enriched, "is_plaintext") and file_enriched.is_plaintext: + try: + # Try to decode as UTF-8 + string_match_instance["matched_data_text"] = matched_data.decode("utf-8") + except UnicodeDecodeError: + try: + # Fallback to a more lenient encoding + string_match_instance["matched_data_text"] = matched_data.decode( + "unicode_escape" + ) + except: + # If both decodings fail, use hex format string_match_instance["matched_data_hex"] = format_hex_like_xxd( matched_data ) else: - logger.warning( - f"Yara match for rule '{rule.identifier}' is length {match.length}, not including in base64 data" - ) - string_match_instance = { - "offset": match.offset, - "length": match.length, - } - string_match["yara_string_match_instances"].append(string_match_instance) - - yara_match["rule_string_matches"].append(string_match) - - summary_markdown = yara_match_to_markdown(yara_match) - display_data = FileObject(type="finding_summary", metadata={"summary": summary_markdown}) - - finding = Finding( - category=FindingCategory.YARA_MATCH, - finding_name="yara_match", - origin_type=FindingOrigin.ENRICHMENT_MODULE, - origin_name=self.name, - object_id=file_enriched.object_id, - severity=8, - raw_data={"match": yara_match}, - data=[display_data], + # Binary file - format as hex + string_match_instance["matched_data_hex"] = format_hex_like_xxd(matched_data) + else: + logger.warning( + f"Yara match for rule '{rule.identifier}' is length {match.length}, not including in base64 data" ) + string_match_instance = { + "offset": match.offset, + "length": match.length, + } + string_match["yara_string_match_instances"].append(string_match_instance) - if not enrichment_result.findings: - enrichment_result.findings = [] + yara_match["rule_string_matches"].append(string_match) - enrichment_result.findings.append(finding) + summary_markdown = yara_match_to_markdown(yara_match) + display_data = FileObject(type="finding_summary", metadata={"summary": summary_markdown}) - yara_matches.append(yara_match) + finding = Finding( + category=FindingCategory.YARA_MATCH, + finding_name="yara_match", + origin_type=FindingOrigin.ENRICHMENT_MODULE, + origin_name=self.name, + object_id=file_enriched.object_id, + severity=8, + raw_data={"match": yara_match}, + data=[display_data], + ) - if yara_matches: - enrichment_result.results = {"yara_matches": yara_matches} - return enrichment_result + if not enrichment_result.findings: + enrichment_result.findings = [] + + enrichment_result.findings.append(finding) + + yara_matches.append(yara_match) + + if yara_matches: + enrichment_result.results = {"yara_matches": yara_matches} + return enrichment_result + return None + + def process(self, object_id: str, file_path: str | None = None) -> EnrichmentResult | None: + """Process file using Yara scanning. + + Args: + object_id: The object ID of the file + file_path: Optional path to already downloaded file + + Returns: + EnrichmentResult or None if processing fails + """ + try: + # Get the current file_enriched from the database backend + file_enriched = get_file_enriched(object_id) + + # Use provided file_path if available, otherwise download + if file_path: + return self._analyze_yara(file_path, file_enriched) + else: + with self.storage.download(file_enriched.object_id) as temp_file: + return self._analyze_yara(temp_file.name, file_enriched) except Exception as e: logger.exception(e, message="Error in process()") diff --git a/libs/file_enrichment_modules/file_enrichment_modules/yara/yara_manager.py b/libs/file_enrichment_modules/file_enrichment_modules/yara/yara_manager.py index 94743f9..8d489aa 100644 --- a/libs/file_enrichment_modules/file_enrichment_modules/yara/yara_manager.py +++ b/libs/file_enrichment_modules/file_enrichment_modules/yara/yara_manager.py @@ -2,18 +2,17 @@ import glob import os import threading from datetime import UTC, datetime -from typing import Optional import plyara import psycopg -import structlog import yara_x +from common.db import get_postgres_connection_str from common.dependency_checks import check_directory_exists -from dapr.clients import DaprClient +from common.logger import get_logger from plyara import utils as plyara_utils from psycopg.rows import dict_row -logger = structlog.get_logger(module=__name__) +logger = get_logger(__name__) YARA_RULES_FOLDER_PATH = os.getenv("YARA_RULES_FOLDER_PATH", "/yara_rules/") check_directory_exists(YARA_RULES_FOLDER_PATH) @@ -25,13 +24,8 @@ class YaraRuleManager: def __init__(self): self.parser = plyara.Plyara() self._compiler = yara_x.Compiler() - self._compiled_rules: Optional[yara_x.Rules] = None - - with DaprClient() as client: - secret = client.get_secret(store_name="nemesis-secret-store", key="POSTGRES_CONNECTION_STRING") - postgres_connection_string = secret.secret["POSTGRES_CONNECTION_STRING"] - - self._conninfo = postgres_connection_string + self._compiled_rules: yara_x.Rules | None = None + self._conninfo = get_postgres_connection_str() # Load and compile rules from disk first self._process_disk_rules() @@ -39,7 +33,7 @@ class YaraRuleManager: # Then load enabled rules from database self.load_rules() - def _get_scanner(self) -> Optional[yara_x.Scanner]: + def _get_scanner(self) -> yara_x.Scanner | None: """Get or create thread-local scanner instance.""" if not hasattr(self._thread_local, "scanner"): if self._compiled_rules: @@ -132,7 +126,7 @@ class YaraRuleManager: logger.exception(e, message="Error processing disk rules") raise - def get_rule_content(self, rule_name: str) -> Optional[str]: + def get_rule_content(self, rule_name: str) -> str | None: """ Retrieve the content of a Yara rule by name. @@ -223,7 +217,7 @@ class YaraRuleManager: """ scanner = self._get_scanner() if not scanner: - logger.warning("No Yara rules compiled") + logger.debug("No Yara rules compiled") return [] try: # Check if target is a string that could be a file path diff --git a/libs/file_enrichment_modules/poetry.lock b/libs/file_enrichment_modules/poetry.lock index 86699a5..1059ab0 100644 --- a/libs/file_enrichment_modules/poetry.lock +++ b/libs/file_enrichment_modules/poetry.lock @@ -14,103 +14,137 @@ files = [ [[package]] name = "aiohttp" -version = "3.12.13" +version = "3.13.0" description = "Async http client/server framework (asyncio)" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "aiohttp-3.12.13-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5421af8f22a98f640261ee48aae3a37f0c41371e99412d55eaf2f8a46d5dad29"}, - {file = "aiohttp-3.12.13-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0fcda86f6cb318ba36ed8f1396a6a4a3fd8f856f84d426584392083d10da4de0"}, - {file = "aiohttp-3.12.13-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4cd71c9fb92aceb5a23c4c39d8ecc80389c178eba9feab77f19274843eb9412d"}, - {file = "aiohttp-3.12.13-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34ebf1aca12845066c963016655dac897651e1544f22a34c9b461ac3b4b1d3aa"}, - {file = "aiohttp-3.12.13-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:893a4639694c5b7edd4bdd8141be296042b6806e27cc1d794e585c43010cc294"}, - {file = "aiohttp-3.12.13-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:663d8ee3ffb3494502ebcccb49078faddbb84c1d870f9c1dd5a29e85d1f747ce"}, - {file = "aiohttp-3.12.13-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f0f8f6a85a0006ae2709aa4ce05749ba2cdcb4b43d6c21a16c8517c16593aabe"}, - {file = "aiohttp-3.12.13-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1582745eb63df267c92d8b61ca655a0ce62105ef62542c00a74590f306be8cb5"}, - {file = "aiohttp-3.12.13-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d59227776ee2aa64226f7e086638baa645f4b044f2947dbf85c76ab11dcba073"}, - {file = "aiohttp-3.12.13-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:06b07c418bde1c8e737d8fa67741072bd3f5b0fb66cf8c0655172188c17e5fa6"}, - {file = "aiohttp-3.12.13-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:9445c1842680efac0f81d272fd8db7163acfcc2b1436e3f420f4c9a9c5a50795"}, - {file = "aiohttp-3.12.13-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:09c4767af0b0b98c724f5d47f2bf33395c8986995b0a9dab0575ca81a554a8c0"}, - {file = "aiohttp-3.12.13-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f3854fbde7a465318ad8d3fc5bef8f059e6d0a87e71a0d3360bb56c0bf87b18a"}, - {file = "aiohttp-3.12.13-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2332b4c361c05ecd381edb99e2a33733f3db906739a83a483974b3df70a51b40"}, - {file = "aiohttp-3.12.13-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:1561db63fa1b658cd94325d303933553ea7d89ae09ff21cc3bcd41b8521fbbb6"}, - {file = "aiohttp-3.12.13-cp310-cp310-win32.whl", hash = "sha256:a0be857f0b35177ba09d7c472825d1b711d11c6d0e8a2052804e3b93166de1ad"}, - {file = "aiohttp-3.12.13-cp310-cp310-win_amd64.whl", hash = "sha256:fcc30ad4fb5cb41a33953292d45f54ef4066746d625992aeac33b8c681173178"}, - {file = "aiohttp-3.12.13-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7c229b1437aa2576b99384e4be668af1db84b31a45305d02f61f5497cfa6f60c"}, - {file = "aiohttp-3.12.13-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:04076d8c63471e51e3689c93940775dc3d12d855c0c80d18ac5a1c68f0904358"}, - {file = "aiohttp-3.12.13-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:55683615813ce3601640cfaa1041174dc956d28ba0511c8cbd75273eb0587014"}, - {file = "aiohttp-3.12.13-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:921bc91e602d7506d37643e77819cb0b840d4ebb5f8d6408423af3d3bf79a7b7"}, - {file = "aiohttp-3.12.13-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e72d17fe0974ddeae8ed86db297e23dba39c7ac36d84acdbb53df2e18505a013"}, - {file = "aiohttp-3.12.13-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0653d15587909a52e024a261943cf1c5bdc69acb71f411b0dd5966d065a51a47"}, - {file = "aiohttp-3.12.13-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a77b48997c66722c65e157c06c74332cdf9c7ad00494b85ec43f324e5c5a9b9a"}, - {file = "aiohttp-3.12.13-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d6946bae55fd36cfb8e4092c921075cde029c71c7cb571d72f1079d1e4e013bc"}, - {file = "aiohttp-3.12.13-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f95db8c8b219bcf294a53742c7bda49b80ceb9d577c8e7aa075612b7f39ffb7"}, - {file = "aiohttp-3.12.13-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:03d5eb3cfb4949ab4c74822fb3326cd9655c2b9fe22e4257e2100d44215b2e2b"}, - {file = "aiohttp-3.12.13-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:6383dd0ffa15515283c26cbf41ac8e6705aab54b4cbb77bdb8935a713a89bee9"}, - {file = "aiohttp-3.12.13-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:6548a411bc8219b45ba2577716493aa63b12803d1e5dc70508c539d0db8dbf5a"}, - {file = "aiohttp-3.12.13-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:81b0fcbfe59a4ca41dc8f635c2a4a71e63f75168cc91026c61be665945739e2d"}, - {file = "aiohttp-3.12.13-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:6a83797a0174e7995e5edce9dcecc517c642eb43bc3cba296d4512edf346eee2"}, - {file = "aiohttp-3.12.13-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a5734d8469a5633a4e9ffdf9983ff7cdb512524645c7a3d4bc8a3de45b935ac3"}, - {file = "aiohttp-3.12.13-cp311-cp311-win32.whl", hash = "sha256:fef8d50dfa482925bb6b4c208b40d8e9fa54cecba923dc65b825a72eed9a5dbd"}, - {file = "aiohttp-3.12.13-cp311-cp311-win_amd64.whl", hash = "sha256:9a27da9c3b5ed9d04c36ad2df65b38a96a37e9cfba6f1381b842d05d98e6afe9"}, - {file = "aiohttp-3.12.13-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0aa580cf80558557285b49452151b9c69f2fa3ad94c5c9e76e684719a8791b73"}, - {file = "aiohttp-3.12.13-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b103a7e414b57e6939cc4dece8e282cfb22043efd0c7298044f6594cf83ab347"}, - {file = "aiohttp-3.12.13-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:78f64e748e9e741d2eccff9597d09fb3cd962210e5b5716047cbb646dc8fe06f"}, - {file = "aiohttp-3.12.13-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29c955989bf4c696d2ededc6b0ccb85a73623ae6e112439398935362bacfaaf6"}, - {file = "aiohttp-3.12.13-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d640191016763fab76072c87d8854a19e8e65d7a6fcfcbf017926bdbbb30a7e5"}, - {file = "aiohttp-3.12.13-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4dc507481266b410dede95dd9f26c8d6f5a14315372cc48a6e43eac652237d9b"}, - {file = "aiohttp-3.12.13-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8a94daa873465d518db073bd95d75f14302e0208a08e8c942b2f3f1c07288a75"}, - {file = "aiohttp-3.12.13-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:177f52420cde4ce0bb9425a375d95577fe082cb5721ecb61da3049b55189e4e6"}, - {file = "aiohttp-3.12.13-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0f7df1f620ec40f1a7fbcb99ea17d7326ea6996715e78f71a1c9a021e31b96b8"}, - {file = "aiohttp-3.12.13-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3062d4ad53b36e17796dce1c0d6da0ad27a015c321e663657ba1cc7659cfc710"}, - {file = "aiohttp-3.12.13-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:8605e22d2a86b8e51ffb5253d9045ea73683d92d47c0b1438e11a359bdb94462"}, - {file = "aiohttp-3.12.13-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:54fbbe6beafc2820de71ece2198458a711e224e116efefa01b7969f3e2b3ddae"}, - {file = "aiohttp-3.12.13-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:050bd277dfc3768b606fd4eae79dd58ceda67d8b0b3c565656a89ae34525d15e"}, - {file = "aiohttp-3.12.13-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:2637a60910b58f50f22379b6797466c3aa6ae28a6ab6404e09175ce4955b4e6a"}, - {file = "aiohttp-3.12.13-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e986067357550d1aaa21cfe9897fa19e680110551518a5a7cf44e6c5638cb8b5"}, - {file = "aiohttp-3.12.13-cp312-cp312-win32.whl", hash = "sha256:ac941a80aeea2aaae2875c9500861a3ba356f9ff17b9cb2dbfb5cbf91baaf5bf"}, - {file = "aiohttp-3.12.13-cp312-cp312-win_amd64.whl", hash = "sha256:671f41e6146a749b6c81cb7fd07f5a8356d46febdaaaf07b0e774ff04830461e"}, - {file = "aiohttp-3.12.13-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d4a18e61f271127465bdb0e8ff36e8f02ac4a32a80d8927aa52371e93cd87938"}, - {file = "aiohttp-3.12.13-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:532542cb48691179455fab429cdb0d558b5e5290b033b87478f2aa6af5d20ace"}, - {file = "aiohttp-3.12.13-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d7eea18b52f23c050ae9db5d01f3d264ab08f09e7356d6f68e3f3ac2de9dfabb"}, - {file = "aiohttp-3.12.13-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad7c8e5c25f2a26842a7c239de3f7b6bfb92304593ef997c04ac49fb703ff4d7"}, - {file = "aiohttp-3.12.13-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6af355b483e3fe9d7336d84539fef460120c2f6e50e06c658fe2907c69262d6b"}, - {file = "aiohttp-3.12.13-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a95cf9f097498f35c88e3609f55bb47b28a5ef67f6888f4390b3d73e2bac6177"}, - {file = "aiohttp-3.12.13-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b8ed8c38a1c584fe99a475a8f60eefc0b682ea413a84c6ce769bb19a7ff1c5ef"}, - {file = "aiohttp-3.12.13-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a0b9170d5d800126b5bc89d3053a2363406d6e327afb6afaeda2d19ee8bb103"}, - {file = "aiohttp-3.12.13-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:372feeace612ef8eb41f05ae014a92121a512bd5067db8f25101dd88a8db11da"}, - {file = "aiohttp-3.12.13-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a946d3702f7965d81f7af7ea8fb03bb33fe53d311df48a46eeca17e9e0beed2d"}, - {file = "aiohttp-3.12.13-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:a0c4725fae86555bbb1d4082129e21de7264f4ab14baf735278c974785cd2041"}, - {file = "aiohttp-3.12.13-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:9b28ea2f708234f0a5c44eb6c7d9eb63a148ce3252ba0140d050b091b6e842d1"}, - {file = "aiohttp-3.12.13-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d4f5becd2a5791829f79608c6f3dc745388162376f310eb9c142c985f9441cc1"}, - {file = "aiohttp-3.12.13-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:60f2ce6b944e97649051d5f5cc0f439360690b73909230e107fd45a359d3e911"}, - {file = "aiohttp-3.12.13-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:69fc1909857401b67bf599c793f2183fbc4804717388b0b888f27f9929aa41f3"}, - {file = "aiohttp-3.12.13-cp313-cp313-win32.whl", hash = "sha256:7d7e68787a2046b0e44ba5587aa723ce05d711e3a3665b6b7545328ac8e3c0dd"}, - {file = "aiohttp-3.12.13-cp313-cp313-win_amd64.whl", hash = "sha256:5a178390ca90419bfd41419a809688c368e63c86bd725e1186dd97f6b89c2706"}, - {file = "aiohttp-3.12.13-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:36f6c973e003dc9b0bb4e8492a643641ea8ef0e97ff7aaa5c0f53d68839357b4"}, - {file = "aiohttp-3.12.13-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6cbfc73179bd67c229eb171e2e3745d2afd5c711ccd1e40a68b90427f282eab1"}, - {file = "aiohttp-3.12.13-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1e8b27b2d414f7e3205aa23bb4a692e935ef877e3a71f40d1884f6e04fd7fa74"}, - {file = "aiohttp-3.12.13-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eabded0c2b2ef56243289112c48556c395d70150ce4220d9008e6b4b3dd15690"}, - {file = "aiohttp-3.12.13-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:003038e83f1a3ff97409999995ec02fe3008a1d675478949643281141f54751d"}, - {file = "aiohttp-3.12.13-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1b6f46613031dbc92bdcaad9c4c22c7209236ec501f9c0c5f5f0b6a689bf50f3"}, - {file = "aiohttp-3.12.13-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c332c6bb04650d59fb94ed96491f43812549a3ba6e7a16a218e612f99f04145e"}, - {file = "aiohttp-3.12.13-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3fea41a2c931fb582cb15dc86a3037329e7b941df52b487a9f8b5aa960153cbd"}, - {file = "aiohttp-3.12.13-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:846104f45d18fb390efd9b422b27d8f3cf8853f1218c537f36e71a385758c896"}, - {file = "aiohttp-3.12.13-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5d6c85ac7dd350f8da2520bac8205ce99df4435b399fa7f4dc4a70407073e390"}, - {file = "aiohttp-3.12.13-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:5a1ecce0ed281bec7da8550da052a6b89552db14d0a0a45554156f085a912f48"}, - {file = "aiohttp-3.12.13-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:5304d74867028cca8f64f1cc1215eb365388033c5a691ea7aa6b0dc47412f495"}, - {file = "aiohttp-3.12.13-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:64d1f24ee95a2d1e094a4cd7a9b7d34d08db1bbcb8aa9fb717046b0a884ac294"}, - {file = "aiohttp-3.12.13-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:119c79922a7001ca6a9e253228eb39b793ea994fd2eccb79481c64b5f9d2a055"}, - {file = "aiohttp-3.12.13-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:bb18f00396d22e2f10cd8825d671d9f9a3ba968d708a559c02a627536b36d91c"}, - {file = "aiohttp-3.12.13-cp39-cp39-win32.whl", hash = "sha256:0022de47ef63fd06b065d430ac79c6b0bd24cdae7feaf0e8c6bac23b805a23a8"}, - {file = "aiohttp-3.12.13-cp39-cp39-win_amd64.whl", hash = "sha256:29e08111ccf81b2734ae03f1ad1cb03b9615e7d8f616764f22f71209c094f122"}, - {file = "aiohttp-3.12.13.tar.gz", hash = "sha256:47e2da578528264a12e4e3dd8dd72a7289e5f812758fe086473fab037a10fcce"}, + {file = "aiohttp-3.13.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ca69ec38adf5cadcc21d0b25e2144f6a25b7db7bea7e730bac25075bc305eff0"}, + {file = "aiohttp-3.13.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:240f99f88a9a6beb53ebadac79a2e3417247aa756202ed234b1dbae13d248092"}, + {file = "aiohttp-3.13.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a4676b978a9711531e7cea499d4cdc0794c617a1c0579310ab46c9fdf5877702"}, + {file = "aiohttp-3.13.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:48fcdd5bc771cbbab8ccc9588b8b6447f6a30f9fe00898b1a5107098e00d6793"}, + {file = "aiohttp-3.13.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:eeea0cdd2f687e210c8f605f322d7b0300ba55145014a5dbe98bd4be6fff1f6c"}, + {file = "aiohttp-3.13.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:10b3f01d5aeb632adaaf39c5e93f040a550464a768d54c514050c635adcbb9d0"}, + {file = "aiohttp-3.13.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a4dc0b83e25267f42ef065ea57653de4365b56d7bc4e4cfc94fabe56998f8ee6"}, + {file = "aiohttp-3.13.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:72714919ed9b90f030f761c20670e529c4af96c31bd000917dd0c9afd1afb731"}, + {file = "aiohttp-3.13.0-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:564be41e85318403fdb176e9e5b3e852d528392f42f2c1d1efcbeeed481126d7"}, + {file = "aiohttp-3.13.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:84912962071087286333f70569362e10793f73f45c48854e6859df11001eb2d3"}, + {file = "aiohttp-3.13.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:90b570f1a146181c3d6ae8f755de66227ded49d30d050479b5ae07710f7894c5"}, + {file = "aiohttp-3.13.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:2d71ca30257ce756e37a6078b1dff2d9475fee13609ad831eac9a6531bea903b"}, + {file = "aiohttp-3.13.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:cd45eb70eca63f41bb156b7dffbe1a7760153b69892d923bdb79a74099e2ed90"}, + {file = "aiohttp-3.13.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:5ae3a19949a27982c7425a7a5a963c1268fdbabf0be15ab59448cbcf0f992519"}, + {file = "aiohttp-3.13.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ea6df292013c9f050cbf3f93eee9953d6e5acd9e64a0bf4ca16404bfd7aa9bcc"}, + {file = "aiohttp-3.13.0-cp310-cp310-win32.whl", hash = "sha256:3b64f22fbb6dcd5663de5ef2d847a5638646ef99112503e6f7704bdecb0d1c4d"}, + {file = "aiohttp-3.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:f8d877aa60d80715b2afc565f0f1aea66565824c229a2d065b31670e09fed6d7"}, + {file = "aiohttp-3.13.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:99eb94e97a42367fef5fc11e28cb2362809d3e70837f6e60557816c7106e2e20"}, + {file = "aiohttp-3.13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4696665b2713021c6eba3e2b882a86013763b442577fe5d2056a42111e732eca"}, + {file = "aiohttp-3.13.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3e6a38366f7f0d0f6ed7a1198055150c52fda552b107dad4785c0852ad7685d1"}, + {file = "aiohttp-3.13.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aab715b1a0c37f7f11f9f1f579c6fbaa51ef569e47e3c0a4644fba46077a9409"}, + {file = "aiohttp-3.13.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7972c82bed87d7bd8e374b60a6b6e816d75ba4f7c2627c2d14eed216e62738e1"}, + {file = "aiohttp-3.13.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ca8313cb852af788c78d5afdea24c40172cbfff8b35e58b407467732fde20390"}, + {file = "aiohttp-3.13.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6c333a2385d2a6298265f4b3e960590f787311b87f6b5e6e21bb8375914ef504"}, + {file = "aiohttp-3.13.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cc6d5fc5edbfb8041d9607f6a417997fa4d02de78284d386bea7ab767b5ea4f3"}, + {file = "aiohttp-3.13.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7ddedba3d0043349edc79df3dc2da49c72b06d59a45a42c1c8d987e6b8d175b8"}, + {file = "aiohttp-3.13.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:23ca762140159417a6bbc959ca1927f6949711851e56f2181ddfe8d63512b5ad"}, + {file = "aiohttp-3.13.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:bfe824d6707a5dc3c5676685f624bc0c63c40d79dc0239a7fd6c034b98c25ebe"}, + {file = "aiohttp-3.13.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:3c11fa5dd2ef773a8a5a6daa40243d83b450915992eab021789498dc87acc114"}, + {file = "aiohttp-3.13.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:00fdfe370cffede3163ba9d3f190b32c0cfc8c774f6f67395683d7b0e48cdb8a"}, + {file = "aiohttp-3.13.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:6475e42ef92717a678bfbf50885a682bb360a6f9c8819fb1a388d98198fdcb80"}, + {file = "aiohttp-3.13.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:77da5305a410910218b99f2a963092f4277d8a9c1f429c1ff1b026d1826bd0b6"}, + {file = "aiohttp-3.13.0-cp311-cp311-win32.whl", hash = "sha256:2f9d9ea547618d907f2ee6670c9a951f059c5994e4b6de8dcf7d9747b420c820"}, + {file = "aiohttp-3.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:0f19f7798996d4458c669bd770504f710014926e9970f4729cf55853ae200469"}, + {file = "aiohttp-3.13.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1c272a9a18a5ecc48a7101882230046b83023bb2a662050ecb9bfcb28d9ab53a"}, + {file = "aiohttp-3.13.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:97891a23d7fd4e1afe9c2f4473e04595e4acb18e4733b910b6577b74e7e21985"}, + {file = "aiohttp-3.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:475bd56492ce5f4cffe32b5533c6533ee0c406d1d0e6924879f83adcf51da0ae"}, + {file = "aiohttp-3.13.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c32ada0abb4bc94c30be2b681c42f058ab104d048da6f0148280a51ce98add8c"}, + {file = "aiohttp-3.13.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:4af1f8877ca46ecdd0bc0d4a6b66d4b2bddc84a79e2e8366bc0d5308e76bceb8"}, + {file = "aiohttp-3.13.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e04ab827ec4f775817736b20cdc8350f40327f9b598dec4e18c9ffdcbea88a93"}, + {file = "aiohttp-3.13.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a6d9487b9471ec36b0faedf52228cd732e89be0a2bbd649af890b5e2ce422353"}, + {file = "aiohttp-3.13.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e66c57416352f36bf98f6641ddadd47c93740a22af7150d3e9a1ef6e983f9a8"}, + {file = "aiohttp-3.13.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:469167d5372f5bb3aedff4fc53035d593884fff2617a75317740e885acd48b04"}, + {file = "aiohttp-3.13.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a9f3546b503975a69b547c9fd1582cad10ede1ce6f3e313a2f547c73a3d7814f"}, + {file = "aiohttp-3.13.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:6b4174fcec98601f0cfdf308ee29a6ae53c55f14359e848dab4e94009112ee7d"}, + {file = "aiohttp-3.13.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a533873a7a4ec2270fb362ee5a0d3b98752e4e1dc9042b257cd54545a96bd8ed"}, + {file = "aiohttp-3.13.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:ce887c5e54411d607ee0959cac15bb31d506d86a9bcaddf0b7e9d63325a7a802"}, + {file = "aiohttp-3.13.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:d871f6a30d43e32fc9252dc7b9febe1a042b3ff3908aa83868d7cf7c9579a59b"}, + {file = "aiohttp-3.13.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:222c828243b4789d79a706a876910f656fad4381661691220ba57b2ab4547865"}, + {file = "aiohttp-3.13.0-cp312-cp312-win32.whl", hash = "sha256:682d2e434ff2f1108314ff7f056ce44e457f12dbed0249b24e106e385cf154b9"}, + {file = "aiohttp-3.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:0a2be20eb23888df130214b91c262a90e2de1553d6fb7de9e9010cec994c0ff2"}, + {file = "aiohttp-3.13.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:00243e51f16f6ec0fb021659d4af92f675f3cf9f9b39efd142aa3ad641d8d1e6"}, + {file = "aiohttp-3.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:059978d2fddc462e9211362cbc8446747ecd930537fa559d3d25c256f032ff54"}, + {file = "aiohttp-3.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:564b36512a7da3b386143c611867e3f7cfb249300a1bf60889bd9985da67ab77"}, + {file = "aiohttp-3.13.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4aa995b9156ae499393d949a456a7ab0b994a8241a96db73a3b73c7a090eff6a"}, + {file = "aiohttp-3.13.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:55ca0e95a3905f62f00900255ed807c580775174252999286f283e646d675a49"}, + {file = "aiohttp-3.13.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:49ce7525853a981fc35d380aa2353536a01a9ec1b30979ea4e35966316cace7e"}, + {file = "aiohttp-3.13.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2117be9883501eaf95503bd313eb4c7a23d567edd44014ba15835a1e9ec6d852"}, + {file = "aiohttp-3.13.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d169c47e40c911f728439da853b6fd06da83761012e6e76f11cb62cddae7282b"}, + {file = "aiohttp-3.13.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:703ad3f742fc81e543638a7bebddd35acadaa0004a5e00535e795f4b6f2c25ca"}, + {file = "aiohttp-3.13.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5bf635c3476f4119b940cc8d94ad454cbe0c377e61b4527f0192aabeac1e9370"}, + {file = "aiohttp-3.13.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:cfe6285ef99e7ee51cef20609be2bc1dd0e8446462b71c9db8bb296ba632810a"}, + {file = "aiohttp-3.13.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:34d8af6391c5f2e69749d7f037b614b8c5c42093c251f336bdbfa4b03c57d6c4"}, + {file = "aiohttp-3.13.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:12f5d820fadc5848d4559ea838aef733cf37ed2a1103bba148ac2f5547c14c29"}, + {file = "aiohttp-3.13.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:0f1338b61ea66f4757a0544ed8a02ccbf60e38d9cfb3225888888dd4475ebb96"}, + {file = "aiohttp-3.13.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:582770f82513419512da096e8df21ca44f86a2e56e25dc93c5ab4df0fe065bf0"}, + {file = "aiohttp-3.13.0-cp313-cp313-win32.whl", hash = "sha256:3194b8cab8dbc882f37c13ef1262e0a3d62064fa97533d3aa124771f7bf1ecee"}, + {file = "aiohttp-3.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:7897298b3eedc790257fef8a6ec582ca04e9dbe568ba4a9a890913b925b8ea21"}, + {file = "aiohttp-3.13.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:c417f8c2e1137775569297c584a8a7144e5d1237789eae56af4faf1894a0b861"}, + {file = "aiohttp-3.13.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:f84b53326abf8e56ebc28a35cebf4a0f396a13a76300f500ab11fe0573bf0b52"}, + {file = "aiohttp-3.13.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:990a53b9d6a30b2878789e490758e568b12b4a7fb2527d0c89deb9650b0e5813"}, + {file = "aiohttp-3.13.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c811612711e01b901e18964b3e5dec0d35525150f5f3f85d0aee2935f059910a"}, + {file = "aiohttp-3.13.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:ee433e594d7948e760b5c2a78cc06ac219df33b0848793cf9513d486a9f90a52"}, + {file = "aiohttp-3.13.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:19bb08e56f57c215e9572cd65cb6f8097804412c54081d933997ddde3e5ac579"}, + {file = "aiohttp-3.13.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f27b7488144eb5dd9151cf839b195edd1569629d90ace4c5b6b18e4e75d1e63a"}, + {file = "aiohttp-3.13.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d812838c109757a11354a161c95708ae4199c4fd4d82b90959b20914c1d097f6"}, + {file = "aiohttp-3.13.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7c20db99da682f9180fa5195c90b80b159632fb611e8dbccdd99ba0be0970620"}, + {file = "aiohttp-3.13.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:cf8b0870047900eb1f17f453b4b3953b8ffbf203ef56c2f346780ff930a4d430"}, + {file = "aiohttp-3.13.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:5b8a5557d5af3f4e3add52a58c4cf2b8e6e59fc56b261768866f5337872d596d"}, + {file = "aiohttp-3.13.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:052bcdd80c1c54b8a18a9ea0cd5e36f473dc8e38d51b804cea34841f677a9971"}, + {file = "aiohttp-3.13.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:76484ba17b2832776581b7ab466d094e48eba74cb65a60aea20154dae485e8bd"}, + {file = "aiohttp-3.13.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:62d8a0adcdaf62ee56bfb37737153251ac8e4b27845b3ca065862fb01d99e247"}, + {file = "aiohttp-3.13.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5004d727499ecb95f7c9147dd0bfc5b5670f71d355f0bd26d7af2d3af8e07d2f"}, + {file = "aiohttp-3.13.0-cp314-cp314-win32.whl", hash = "sha256:a1c20c26af48aea984f63f96e5d7af7567c32cb527e33b60a0ef0a6313cf8b03"}, + {file = "aiohttp-3.13.0-cp314-cp314-win_amd64.whl", hash = "sha256:56f7d230ec66e799fbfd8350e9544f8a45a4353f1cf40c1fea74c1780f555b8f"}, + {file = "aiohttp-3.13.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:2fd35177dc483ae702f07b86c782f4f4b100a8ce4e7c5778cea016979023d9fd"}, + {file = "aiohttp-3.13.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:4df1984c8804ed336089e88ac81a9417b1fd0db7c6f867c50a9264488797e778"}, + {file = "aiohttp-3.13.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:e68c0076052dd911a81d3acc4ef2911cc4ef65bf7cadbfbc8ae762da24da858f"}, + {file = "aiohttp-3.13.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bc95c49853cd29613e4fe4ff96d73068ff89b89d61e53988442e127e8da8e7ba"}, + {file = "aiohttp-3.13.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3b3bdc89413117b40cc39baae08fd09cbdeb839d421c4e7dce6a34f6b54b3ac1"}, + {file = "aiohttp-3.13.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3e77a729df23be2116acc4e9de2767d8e92445fbca68886dd991dc912f473755"}, + {file = "aiohttp-3.13.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e88ab34826d6eeb6c67e6e92400b9ec653faf5092a35f07465f44c9f1c429f82"}, + {file = "aiohttp-3.13.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:019dbef24fe28ce2301419dd63a2b97250d9760ca63ee2976c2da2e3f182f82e"}, + {file = "aiohttp-3.13.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:2c4aeaedd20771b7b4bcdf0ae791904445df6d856c02fc51d809d12d17cffdc7"}, + {file = "aiohttp-3.13.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:b3a8e6a2058a0240cfde542b641d0e78b594311bc1a710cbcb2e1841417d5cb3"}, + {file = "aiohttp-3.13.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:f8e38d55ca36c15f36d814ea414ecb2401d860de177c49f84a327a25b3ee752b"}, + {file = "aiohttp-3.13.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:a921edbe971aade1bf45bcbb3494e30ba6863a5c78f28be992c42de980fd9108"}, + {file = "aiohttp-3.13.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:474cade59a447cb4019c0dce9f0434bf835fb558ea932f62c686fe07fe6db6a1"}, + {file = "aiohttp-3.13.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:99a303ad960747c33b65b1cb65d01a62ac73fa39b72f08a2e1efa832529b01ed"}, + {file = "aiohttp-3.13.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:bb34001fc1f05f6b323e02c278090c07a47645caae3aa77ed7ed8a3ce6abcce9"}, + {file = "aiohttp-3.13.0-cp314-cp314t-win32.whl", hash = "sha256:dea698b64235d053def7d2f08af9302a69fcd760d1c7bd9988fd5d3b6157e657"}, + {file = "aiohttp-3.13.0-cp314-cp314t-win_amd64.whl", hash = "sha256:1f164699a060c0b3616459d13c1464a981fddf36f892f0a5027cbd45121fb14b"}, + {file = "aiohttp-3.13.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:fcc425fb6fd2a00c6d91c85d084c6b75a61bc8bc12159d08e17c5711df6c5ba4"}, + {file = "aiohttp-3.13.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7c2c4c9ce834801651f81d6760d0a51035b8b239f58f298de25162fcf6f8bb64"}, + {file = "aiohttp-3.13.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f91e8f9053a07177868e813656ec57599cd2a63238844393cd01bd69c2e40147"}, + {file = "aiohttp-3.13.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:df46d9a3d78ec19b495b1107bf26e4fcf97c900279901f4f4819ac5bb2a02a4c"}, + {file = "aiohttp-3.13.0-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3b1eb9871cbe43b6ca6fac3544682971539d8a1d229e6babe43446279679609d"}, + {file = "aiohttp-3.13.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:62a3cddf8d9a2eae1f79585fa81d32e13d0c509bb9e7ad47d33c83b45a944df7"}, + {file = "aiohttp-3.13.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0f735e680c323ee7e9ef8e2ea26425c7dbc2ede0086fa83ce9d7ccab8a089f26"}, + {file = "aiohttp-3.13.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6a51839f778b0e283b43cd82bb17f1835ee2cc1bf1101765e90ae886e53e751c"}, + {file = "aiohttp-3.13.0-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ac90cfab65bc281d6752f22db5fa90419e33220af4b4fa53b51f5948f414c0e7"}, + {file = "aiohttp-3.13.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:62fd54f3e6f17976962ba67f911d62723c760a69d54f5d7b74c3ceb1a4e9ef8d"}, + {file = "aiohttp-3.13.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:cf2b60b65df05b6b2fa0d887f2189991a0dbf44a0dd18359001dc8fcdb7f1163"}, + {file = "aiohttp-3.13.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:1ccedfe280e804d9a9d7fe8b8c4309d28e364b77f40309c86596baa754af50b1"}, + {file = "aiohttp-3.13.0-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:ea01ffbe23df53ece0c8732d1585b3d6079bb8c9ee14f3745daf000051415a31"}, + {file = "aiohttp-3.13.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:19ba8625fa69523627b67f7e9901b587a4952470f68814d79cdc5bc460e9b885"}, + {file = "aiohttp-3.13.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:4b14bfae90598d331b5061fd15a7c290ea0c15b34aeb1cf620464bb5ec02a602"}, + {file = "aiohttp-3.13.0-cp39-cp39-win32.whl", hash = "sha256:cf7a4b976da219e726d0043fc94ae8169c0dba1d3a059b3c1e2c964bafc5a77d"}, + {file = "aiohttp-3.13.0-cp39-cp39-win_amd64.whl", hash = "sha256:6b9697d15231aeaed4786f090c9c8bc3ab5f0e0a6da1e76c135a310def271020"}, + {file = "aiohttp-3.13.0.tar.gz", hash = "sha256:378dbc57dd8cf341ce243f13fa1fa5394d68e2e02c15cd5f28eae35a70ec7f67"}, ] [package.dependencies] aiohappyeyeballs = ">=2.5.0" -aiosignal = ">=1.1.2" +aiosignal = ">=1.4.0" attrs = ">=17.3.0" frozenlist = ">=1.1.1" multidict = ">=4.5,<7.0" @@ -118,22 +152,23 @@ propcache = ">=0.2.0" yarl = ">=1.17.0,<2.0" [package.extras] -speedups = ["Brotli", "aiodns (>=3.3.0)", "brotlicffi"] +speedups = ["Brotli", "aiodns (>=3.3.0)", "brotlicffi", "zstandard"] [[package]] name = "aiosignal" -version = "1.3.2" +version = "1.4.0" description = "aiosignal: a list of registered asynchronous callbacks" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "aiosignal-1.3.2-py2.py3-none-any.whl", hash = "sha256:45cde58e409a301715980c2b01d0c28bdde3770d8290b5eb2173759d9acb31a5"}, - {file = "aiosignal-1.3.2.tar.gz", hash = "sha256:a8c255c66fafb1e499c9351d0bf32ff2d8a0321595ebac3b93713656d2436f54"}, + {file = "aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e"}, + {file = "aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7"}, ] [package.dependencies] frozenlist = ">=1.1.0" +typing-extensions = {version = ">=4.2", markers = "python_version < \"3.13\""} [[package]] name = "annotated-types" @@ -149,14 +184,14 @@ files = [ [[package]] name = "anyio" -version = "4.9.0" -description = "High level compatibility layer for multiple asynchronous event loop implementations" +version = "4.11.0" +description = "High-level concurrency and networking framework on top of asyncio or Trio" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "anyio-4.9.0-py3-none-any.whl", hash = "sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c"}, - {file = "anyio-4.9.0.tar.gz", hash = "sha256:673c0c244e15788651a4ff38710fea9675823028a6f08a5eda409e0c9840a028"}, + {file = "anyio-4.11.0-py3-none-any.whl", hash = "sha256:0287e96f4d26d4149305414d4e3bc32f0dcd0862365a4bddea19d7a1ec38c4fc"}, + {file = "anyio-4.11.0.tar.gz", hash = "sha256:82a8d0b81e318cc5ce71a5f1f8b5c4e63619620b63141ef8c995fa0db95a57c4"}, ] [package.dependencies] @@ -165,9 +200,7 @@ sniffio = ">=1.1" typing_extensions = {version = ">=4.5", markers = "python_version < \"3.13\""} [package.extras] -doc = ["Sphinx (>=8.2,<9.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx_rtd_theme"] -test = ["anyio[trio]", "blockbuster (>=1.5.23)", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "trustme", "truststore (>=0.9.1)", "uvloop (>=0.21)"] -trio = ["trio (>=0.26.1)"] +trio = ["trio (>=0.31.0)"] [[package]] name = "argon2-cffi" @@ -186,41 +219,45 @@ argon2-cffi-bindings = "*" [[package]] name = "argon2-cffi-bindings" -version = "21.2.0" +version = "25.1.0" description = "Low-level CFFI bindings for Argon2" optional = false -python-versions = ">=3.6" +python-versions = ">=3.9" groups = ["main"] files = [ - {file = "argon2-cffi-bindings-21.2.0.tar.gz", hash = "sha256:bb89ceffa6c791807d1305ceb77dbfacc5aa499891d2c55661c6459651fc39e3"}, - {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ccb949252cb2ab3a08c02024acb77cfb179492d5701c7cbdbfd776124d4d2367"}, - {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9524464572e12979364b7d600abf96181d3541da11e23ddf565a32e70bd4dc0d"}, - {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b746dba803a79238e925d9046a63aa26bf86ab2a2fe74ce6b009a1c3f5c8f2ae"}, - {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:58ed19212051f49a523abb1dbe954337dc82d947fb6e5a0da60f7c8471a8476c"}, - {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:bd46088725ef7f58b5a1ef7ca06647ebaf0eb4baff7d1d0d177c6cc8744abd86"}, - {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_i686.whl", hash = "sha256:8cd69c07dd875537a824deec19f978e0f2078fdda07fd5c42ac29668dda5f40f"}, - {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:f1152ac548bd5b8bcecfb0b0371f082037e47128653df2e8ba6e914d384f3c3e"}, - {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-win32.whl", hash = "sha256:603ca0aba86b1349b147cab91ae970c63118a0f30444d4bc80355937c950c082"}, - {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-win_amd64.whl", hash = "sha256:b2ef1c30440dbbcba7a5dc3e319408b59676e2e039e2ae11a8775ecf482b192f"}, - {file = "argon2_cffi_bindings-21.2.0-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:e415e3f62c8d124ee16018e491a009937f8cf7ebf5eb430ffc5de21b900dad93"}, - {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3e385d1c39c520c08b53d63300c3ecc28622f076f4c2b0e6d7e796e9f6502194"}, - {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c3e3cc67fdb7d82c4718f19b4e7a87123caf8a93fde7e23cf66ac0337d3cb3f"}, - {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6a22ad9800121b71099d0fb0a65323810a15f2e292f2ba450810a7316e128ee5"}, - {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f9f8b450ed0547e3d473fdc8612083fd08dd2120d6ac8f73828df9b7d45bb351"}, - {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:93f9bf70084f97245ba10ee36575f0c3f1e7d7724d67d8e5b08e61787c320ed7"}, - {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3b9ef65804859d335dc6b31582cad2c5166f0c3e7975f324d9ffaa34ee7e6583"}, - {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4966ef5848d820776f5f562a7d45fdd70c2f330c961d0d745b784034bd9f48d"}, - {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20ef543a89dee4db46a1a6e206cd015360e5a75822f76df533845c3cbaf72670"}, - {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed2937d286e2ad0cc79a7087d3c272832865f779430e0cc2b4f3718d3159b0cb"}, - {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:5e00316dabdaea0b2dd82d141cc66889ced0cdcbfa599e8b471cf22c620c329a"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:3d3f05610594151994ca9ccb3c771115bdb4daef161976a266f0dd8aa9996b8f"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:8b8efee945193e667a396cbc7b4fb7d357297d6234d30a489905d96caabde56b"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:3c6702abc36bf3ccba3f802b799505def420a1b7039862014a65db3205967f5a"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a1c70058c6ab1e352304ac7e3b52554daadacd8d453c1752e547c76e9c99ac44"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e2fd3bfbff3c5d74fef31a722f729bf93500910db650c925c2d6ef879a7e51cb"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c4f9665de60b1b0e99bcd6be4f17d90339698ce954cfd8d9cf4f91c995165a92"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ba92837e4a9aa6a508c8d2d7883ed5a8f6c308c89a4790e1e447a220deb79a85"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-win32.whl", hash = "sha256:84a461d4d84ae1295871329b346a97f68eade8c53b6ed9a7ca2d7467f3c8ff6f"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b55aec3565b65f56455eebc9b9f34130440404f27fe21c3b375bf1ea4d8fbae6"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:87c33a52407e4c41f3b70a9c2d3f6056d88b10dad7695be708c5021673f55623"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:aecba1723ae35330a008418a91ea6cfcedf6d31e5fbaa056a166462ff066d500"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:2630b6240b495dfab90aebe159ff784d08ea999aa4b0d17efa734055a07d2f44"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:7aef0c91e2c0fbca6fc68e7555aa60ef7008a739cbe045541e438373bc54d2b0"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e021e87faa76ae0d413b619fe2b65ab9a037f24c60a1e6cc43457ae20de6dc6"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d3e924cfc503018a714f94a49a149fdc0b644eaead5d1f089330399134fa028a"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:c87b72589133f0346a1cb8d5ecca4b933e3c9b64656c9d175270a000e73b288d"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1db89609c06afa1a214a69a462ea741cf735b29a57530478c06eb81dd403de99"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-win32.whl", hash = "sha256:473bcb5f82924b1becbb637b63303ec8d10e84c8d241119419897a26116515d2"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-win_amd64.whl", hash = "sha256:a98cd7d17e9f7ce244c0803cad3c23a7d379c301ba618a5fa76a67d116618b98"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-win_arm64.whl", hash = "sha256:b0fdbcf513833809c882823f98dc2f931cf659d9a1429616ac3adebb49f5db94"}, + {file = "argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:6dca33a9859abf613e22733131fc9194091c1fa7cb3e131c143056b4856aa47e"}, + {file = "argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:21378b40e1b8d1655dd5310c84a40fc19a9aa5e6366e835ceb8576bf0fea716d"}, + {file = "argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d588dec224e2a83edbdc785a5e6f3c6cd736f46bfd4b441bbb5aa1f5085e584"}, + {file = "argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5acb4e41090d53f17ca1110c3427f0a130f944b896fc8c83973219c97f57b690"}, + {file = "argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:da0c79c23a63723aa5d782250fbf51b768abca630285262fb5144ba5ae01e520"}, + {file = "argon2_cffi_bindings-25.1.0.tar.gz", hash = "sha256:b957f3e6ea4d55d820e40ff76f450952807013d361a65d7f28acc0acbf29229d"}, ] [package.dependencies] -cffi = ">=1.0.1" - -[package.extras] -dev = ["cogapp", "pre-commit", "pytest", "wheel"] -tests = ["pytest"] +cffi = [ + {version = ">=1.0.1", markers = "python_version < \"3.14\""}, + {version = ">=2.0.0b1", markers = "python_version >= \"3.14\""}, +] [[package]] name = "asyncpg" @@ -288,24 +325,16 @@ test = ["distro (>=1.9.0,<1.10.0)", "flake8 (>=6.1,<7.0)", "flake8-pyi (>=24.1.0 [[package]] name = "attrs" -version = "25.3.0" +version = "25.4.0" description = "Classes Without Boilerplate" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["main"] files = [ - {file = "attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3"}, - {file = "attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b"}, + {file = "attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373"}, + {file = "attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11"}, ] -[package.extras] -benchmark = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -cov = ["cloudpickle", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier"] -tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"] - [[package]] name = "blinker" version = "1.9.0" @@ -320,208 +349,235 @@ files = [ [[package]] name = "certifi" -version = "2025.4.26" +version = "2025.10.5" description = "Python package for providing Mozilla's CA Bundle." optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" groups = ["main"] files = [ - {file = "certifi-2025.4.26-py3-none-any.whl", hash = "sha256:30350364dfe371162649852c63336a15c70c6510c2ad5015b21c2345311805f3"}, - {file = "certifi-2025.4.26.tar.gz", hash = "sha256:0a816057ea3cdefcef70270d2c515e4506bbc954f417fa5ade2021213bb8f0c6"}, + {file = "certifi-2025.10.5-py3-none-any.whl", hash = "sha256:0f212c2744a9bb6de0c56639a6f68afe01ecd92d91f14ae897c4fe7bbeeef0de"}, + {file = "certifi-2025.10.5.tar.gz", hash = "sha256:47c09d31ccf2acf0be3f701ea53595ee7e0b8fa08801c6624be771df09ae7b43"}, ] [[package]] name = "cffi" -version = "1.17.1" +version = "2.0.0" description = "Foreign Function Interface for Python calling C code." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["main"] files = [ - {file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"}, - {file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"}, - {file = "cffi-1.17.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382"}, - {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702"}, - {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3"}, - {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6"}, - {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17"}, - {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8"}, - {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e"}, - {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be"}, - {file = "cffi-1.17.1-cp310-cp310-win32.whl", hash = "sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c"}, - {file = "cffi-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15"}, - {file = "cffi-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401"}, - {file = "cffi-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf"}, - {file = "cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4"}, - {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41"}, - {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1"}, - {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6"}, - {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d"}, - {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6"}, - {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f"}, - {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b"}, - {file = "cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655"}, - {file = "cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0"}, - {file = "cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4"}, - {file = "cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c"}, - {file = "cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36"}, - {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5"}, - {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff"}, - {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99"}, - {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93"}, - {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3"}, - {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8"}, - {file = "cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65"}, - {file = "cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903"}, - {file = "cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e"}, - {file = "cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2"}, - {file = "cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3"}, - {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683"}, - {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5"}, - {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4"}, - {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd"}, - {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed"}, - {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9"}, - {file = "cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d"}, - {file = "cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a"}, - {file = "cffi-1.17.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:636062ea65bd0195bc012fea9321aca499c0504409f413dc88af450b57ffd03b"}, - {file = "cffi-1.17.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7eac2ef9b63c79431bc4b25f1cd649d7f061a28808cbc6c47b534bd789ef964"}, - {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e221cf152cff04059d011ee126477f0d9588303eb57e88923578ace7baad17f9"}, - {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:31000ec67d4221a71bd3f67df918b1f88f676f1c3b535a7eb473255fdc0b83fc"}, - {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f17be4345073b0a7b8ea599688f692ac3ef23ce28e5df79c04de519dbc4912c"}, - {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2b1fac190ae3ebfe37b979cc1ce69c81f4e4fe5746bb401dca63a9062cdaf1"}, - {file = "cffi-1.17.1-cp38-cp38-win32.whl", hash = "sha256:7596d6620d3fa590f677e9ee430df2958d2d6d6de2feeae5b20e82c00b76fbf8"}, - {file = "cffi-1.17.1-cp38-cp38-win_amd64.whl", hash = "sha256:78122be759c3f8a014ce010908ae03364d00a1f81ab5c7f4a7a5120607ea56e1"}, - {file = "cffi-1.17.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b2ab587605f4ba0bf81dc0cb08a41bd1c0a5906bd59243d56bad7668a6fc6c16"}, - {file = "cffi-1.17.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:28b16024becceed8c6dfbc75629e27788d8a3f9030691a1dbf9821a128b22c36"}, - {file = "cffi-1.17.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d599671f396c4723d016dbddb72fe8e0397082b0a77a4fab8028923bec050e8"}, - {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca74b8dbe6e8e8263c0ffd60277de77dcee6c837a3d0881d8c1ead7268c9e576"}, - {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87"}, - {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98e3969bcff97cae1b2def8ba499ea3d6f31ddfdb7635374834cf89a1a08ecf0"}, - {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdf5ce3acdfd1661132f2a9c19cac174758dc2352bfe37d98aa7512c6b7178b3"}, - {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9755e4345d1ec879e3849e62222a18c7174d65a6a92d5b346b1863912168b595"}, - {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f1e22e8c4419538cb197e4dd60acc919d7696e5ef98ee4da4e01d3f8cfa4cc5a"}, - {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c03e868a0b3bc35839ba98e74211ed2b05d2119be4e8a0f224fba9384f1fe02e"}, - {file = "cffi-1.17.1-cp39-cp39-win32.whl", hash = "sha256:e31ae45bc2e29f6b2abd0de1cc3b9d5205aa847cafaecb8af1476a609a2f6eb7"}, - {file = "cffi-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662"}, - {file = "cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824"}, + {file = "cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44"}, + {file = "cffi-2.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49"}, + {file = "cffi-2.0.0-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c"}, + {file = "cffi-2.0.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb"}, + {file = "cffi-2.0.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0"}, + {file = "cffi-2.0.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4"}, + {file = "cffi-2.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453"}, + {file = "cffi-2.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495"}, + {file = "cffi-2.0.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5"}, + {file = "cffi-2.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb"}, + {file = "cffi-2.0.0-cp310-cp310-win32.whl", hash = "sha256:1f72fb8906754ac8a2cc3f9f5aaa298070652a0ffae577e0ea9bd480dc3c931a"}, + {file = "cffi-2.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:b18a3ed7d5b3bd8d9ef7a8cb226502c6bf8308df1525e1cc676c3680e7176739"}, + {file = "cffi-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe"}, + {file = "cffi-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c"}, + {file = "cffi-2.0.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92"}, + {file = "cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93"}, + {file = "cffi-2.0.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5"}, + {file = "cffi-2.0.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664"}, + {file = "cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26"}, + {file = "cffi-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9"}, + {file = "cffi-2.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414"}, + {file = "cffi-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743"}, + {file = "cffi-2.0.0-cp311-cp311-win32.whl", hash = "sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5"}, + {file = "cffi-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5"}, + {file = "cffi-2.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d"}, + {file = "cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d"}, + {file = "cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c"}, + {file = "cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe"}, + {file = "cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062"}, + {file = "cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e"}, + {file = "cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037"}, + {file = "cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba"}, + {file = "cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94"}, + {file = "cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187"}, + {file = "cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18"}, + {file = "cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5"}, + {file = "cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6"}, + {file = "cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb"}, + {file = "cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca"}, + {file = "cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b"}, + {file = "cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b"}, + {file = "cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2"}, + {file = "cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3"}, + {file = "cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26"}, + {file = "cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c"}, + {file = "cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b"}, + {file = "cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27"}, + {file = "cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75"}, + {file = "cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91"}, + {file = "cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5"}, + {file = "cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13"}, + {file = "cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b"}, + {file = "cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c"}, + {file = "cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef"}, + {file = "cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775"}, + {file = "cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205"}, + {file = "cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1"}, + {file = "cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f"}, + {file = "cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25"}, + {file = "cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad"}, + {file = "cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9"}, + {file = "cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d"}, + {file = "cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c"}, + {file = "cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8"}, + {file = "cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc"}, + {file = "cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592"}, + {file = "cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512"}, + {file = "cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4"}, + {file = "cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e"}, + {file = "cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6"}, + {file = "cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9"}, + {file = "cffi-2.0.0-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:fe562eb1a64e67dd297ccc4f5addea2501664954f2692b69a76449ec7913ecbf"}, + {file = "cffi-2.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:de8dad4425a6ca6e4e5e297b27b5c824ecc7581910bf9aee86cb6835e6812aa7"}, + {file = "cffi-2.0.0-cp39-cp39-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:4647afc2f90d1ddd33441e5b0e85b16b12ddec4fca55f0d9671fef036ecca27c"}, + {file = "cffi-2.0.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3f4d46d8b35698056ec29bca21546e1551a205058ae1a181d871e278b0b28165"}, + {file = "cffi-2.0.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:e6e73b9e02893c764e7e8d5bb5ce277f1a009cd5243f8228f75f842bf937c534"}, + {file = "cffi-2.0.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:cb527a79772e5ef98fb1d700678fe031e353e765d1ca2d409c92263c6d43e09f"}, + {file = "cffi-2.0.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:61d028e90346df14fedc3d1e5441df818d095f3b87d286825dfcbd6459b7ef63"}, + {file = "cffi-2.0.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0f6084a0ea23d05d20c3edcda20c3d006f9b6f3fefeac38f59262e10cef47ee2"}, + {file = "cffi-2.0.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:1cd13c99ce269b3ed80b417dcd591415d3372bcac067009b6e0f59c7d4015e65"}, + {file = "cffi-2.0.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:89472c9762729b5ae1ad974b777416bfda4ac5642423fa93bd57a09204712322"}, + {file = "cffi-2.0.0-cp39-cp39-win32.whl", hash = "sha256:2081580ebb843f759b9f617314a24ed5738c51d2aee65d31e02f6f7a2b97707a"}, + {file = "cffi-2.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:b882b3df248017dba09d6b16defe9b5c407fe32fc7c65a9c69798e6175601be9"}, + {file = "cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529"}, ] [package.dependencies] -pycparser = "*" +pycparser = {version = "*", markers = "implementation_name != \"PyPy\""} [[package]] name = "charset-normalizer" -version = "3.4.2" +version = "3.4.3" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7" groups = ["main"] files = [ - {file = "charset_normalizer-3.4.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c48ed483eb946e6c04ccbe02c6b4d1d48e51944b6db70f697e089c193404941"}, - {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2d318c11350e10662026ad0eb71bb51c7812fc8590825304ae0bdd4ac283acd"}, - {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9cbfacf36cb0ec2897ce0ebc5d08ca44213af24265bd56eca54bee7923c48fd6"}, - {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18dd2e350387c87dabe711b86f83c9c78af772c748904d372ade190b5c7c9d4d"}, - {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8075c35cd58273fee266c58c0c9b670947c19df5fb98e7b66710e04ad4e9ff86"}, - {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5bf4545e3b962767e5c06fe1738f951f77d27967cb2caa64c28be7c4563e162c"}, - {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7a6ab32f7210554a96cd9e33abe3ddd86732beeafc7a28e9955cdf22ffadbab0"}, - {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b33de11b92e9f75a2b545d6e9b6f37e398d86c3e9e9653c4864eb7e89c5773ef"}, - {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8755483f3c00d6c9a77f490c17e6ab0c8729e39e6390328e42521ef175380ae6"}, - {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:68a328e5f55ec37c57f19ebb1fdc56a248db2e3e9ad769919a58672958e8f366"}, - {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:21b2899062867b0e1fde9b724f8aecb1af14f2778d69aacd1a5a1853a597a5db"}, - {file = "charset_normalizer-3.4.2-cp310-cp310-win32.whl", hash = "sha256:e8082b26888e2f8b36a042a58307d5b917ef2b1cacab921ad3323ef91901c71a"}, - {file = "charset_normalizer-3.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:f69a27e45c43520f5487f27627059b64aaf160415589230992cec34c5e18a509"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:be1e352acbe3c78727a16a455126d9ff83ea2dfdcbc83148d2982305a04714c2"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa88ca0b1932e93f2d961bf3addbb2db902198dca337d88c89e1559e066e7645"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d524ba3f1581b35c03cb42beebab4a13e6cdad7b36246bd22541fa585a56cccd"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28a1005facc94196e1fb3e82a3d442a9d9110b8434fc1ded7a24a2983c9888d8"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdb20a30fe1175ecabed17cbf7812f7b804b8a315a25f24678bcdf120a90077f"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0f5d9ed7f254402c9e7d35d2f5972c9bbea9040e99cd2861bd77dc68263277c7"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:efd387a49825780ff861998cd959767800d54f8308936b21025326de4b5a42b9"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f0aa37f3c979cf2546b73e8222bbfa3dc07a641585340179d768068e3455e544"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e70e990b2137b29dc5564715de1e12701815dacc1d056308e2b17e9095372a82"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0c8c57f84ccfc871a48a47321cfa49ae1df56cd1d965a09abe84066f6853b9c0"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6b66f92b17849b85cad91259efc341dce9c1af48e2173bf38a85c6329f1033e5"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-win32.whl", hash = "sha256:daac4765328a919a805fa5e2720f3e94767abd632ae410a9062dff5412bae65a"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:e53efc7c7cee4c1e70661e2e112ca46a575f90ed9ae3fef200f2a25e954f4b28"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0c29de6a1a95f24b9a1aa7aefd27d2487263f00dfd55a77719b530788f75cff7"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cddf7bd982eaa998934a91f69d182aec997c6c468898efe6679af88283b498d3"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcbe676a55d7445b22c10967bceaaf0ee69407fbe0ece4d032b6eb8d4565982a"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d41c4d287cfc69060fa91cae9683eacffad989f1a10811995fa309df656ec214"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e594135de17ab3866138f496755f302b72157d115086d100c3f19370839dd3a"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf713fe9a71ef6fd5adf7a79670135081cd4431c2943864757f0fa3a65b1fafd"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a370b3e078e418187da8c3674eddb9d983ec09445c99a3a263c2011993522981"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a955b438e62efdf7e0b7b52a64dc5c3396e2634baa62471768a64bc2adb73d5c"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:7222ffd5e4de8e57e03ce2cef95a4c43c98fcb72ad86909abdfc2c17d227fc1b"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:bee093bf902e1d8fc0ac143c88902c3dfc8941f7ea1d6a8dd2bcb786d33db03d"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb8adb91d11846ee08bec4c8236c8549ac721c245678282dcb06b221aab59f"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-win32.whl", hash = "sha256:db4c7bf0e07fc3b7d89ac2a5880a6a8062056801b83ff56d8464b70f65482b6c"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:5a9979887252a82fefd3d3ed2a8e3b937a7a809f65dcb1e068b090e165bbe99e"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:926ca93accd5d36ccdabd803392ddc3e03e6d4cd1cf17deff3b989ab8e9dbcf0"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eba9904b0f38a143592d9fc0e19e2df0fa2e41c3c3745554761c5f6447eedabf"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fddb7e2c84ac87ac3a947cb4e66d143ca5863ef48e4a5ecb83bd48619e4634e"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98f862da73774290f251b9df8d11161b6cf25b599a66baf087c1ffe340e9bfd1"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c9379d65defcab82d07b2a9dfbfc2e95bc8fe0ebb1b176a3190230a3ef0e07c"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e635b87f01ebc977342e2697d05b56632f5f879a4f15955dfe8cef2448b51691"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1c95a1e2902a8b722868587c0e1184ad5c55631de5afc0eb96bc4b0d738092c0"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ef8de666d6179b009dce7bcb2ad4c4a779f113f12caf8dc77f0162c29d20490b"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:32fc0341d72e0f73f80acb0a2c94216bd704f4f0bce10aedea38f30502b271ff"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:289200a18fa698949d2b39c671c2cc7a24d44096784e76614899a7ccf2574b7b"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4a476b06fbcf359ad25d34a057b7219281286ae2477cc5ff5e3f70a246971148"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-win32.whl", hash = "sha256:aaeeb6a479c7667fbe1099af9617c83aaca22182d6cf8c53966491a0f1b7ffb7"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:aa6af9e7d59f9c12b33ae4e9450619cf2488e2bbe9b44030905877f0b2324980"}, - {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cad5f45b3146325bb38d6855642f6fd609c3f7cad4dbaf75549bf3b904d3184"}, - {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b2680962a4848b3c4f155dc2ee64505a9c57186d0d56b43123b17ca3de18f0fa"}, - {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:36b31da18b8890a76ec181c3cf44326bf2c48e36d393ca1b72b3f484113ea344"}, - {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f4074c5a429281bf056ddd4c5d3b740ebca4d43ffffe2ef4bf4d2d05114299da"}, - {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c9e36a97bee9b86ef9a1cf7bb96747eb7a15c2f22bdb5b516434b00f2a599f02"}, - {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:1b1bde144d98e446b056ef98e59c256e9294f6b74d7af6846bf5ffdafd687a7d"}, - {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:915f3849a011c1f593ab99092f3cecfcb4d65d8feb4a64cf1bf2d22074dc0ec4"}, - {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:fb707f3e15060adf5b7ada797624a6c6e0138e2a26baa089df64c68ee98e040f"}, - {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:25a23ea5c7edc53e0f29bae2c44fcb5a1aa10591aae107f2a2b2583a9c5cbc64"}, - {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:770cab594ecf99ae64c236bc9ee3439c3f46be49796e265ce0cc8bc17b10294f"}, - {file = "charset_normalizer-3.4.2-cp37-cp37m-win32.whl", hash = "sha256:6a0289e4589e8bdfef02a80478f1dfcb14f0ab696b5a00e1f4b8a14a307a3c58"}, - {file = "charset_normalizer-3.4.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6fc1f5b51fa4cecaa18f2bd7a003f3dd039dd615cd69a2afd6d3b19aed6775f2"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:76af085e67e56c8816c3ccf256ebd136def2ed9654525348cfa744b6802b69eb"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e45ba65510e2647721e35323d6ef54c7974959f6081b58d4ef5d87c60c84919a"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:046595208aae0120559a67693ecc65dd75d46f7bf687f159127046628178dc45"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75d10d37a47afee94919c4fab4c22b9bc2a8bf7d4f46f87363bcf0573f3ff4f5"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6333b3aa5a12c26b2a4d4e7335a28f1475e0e5e17d69d55141ee3cab736f66d1"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e8323a9b031aa0393768b87f04b4164a40037fb2a3c11ac06a03ffecd3618027"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:24498ba8ed6c2e0b56d4acbf83f2d989720a93b41d712ebd4f4979660db4417b"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:844da2b5728b5ce0e32d863af26f32b5ce61bc4273a9c720a9f3aa9df73b1455"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:65c981bdbd3f57670af8b59777cbfae75364b483fa8a9f420f08094531d54a01"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:3c21d4fca343c805a52c0c78edc01e3477f6dd1ad7c47653241cf2a206d4fc58"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:dc7039885fa1baf9be153a0626e337aa7ec8bf96b0128605fb0d77788ddc1681"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-win32.whl", hash = "sha256:8272b73e1c5603666618805fe821edba66892e2870058c94c53147602eab29c7"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-win_amd64.whl", hash = "sha256:70f7172939fdf8790425ba31915bfbe8335030f05b9913d7ae00a87d4395620a"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:005fa3432484527f9732ebd315da8da8001593e2cf46a3d817669f062c3d9ed4"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e92fca20c46e9f5e1bb485887d074918b13543b1c2a1185e69bb8d17ab6236a7"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:50bf98d5e563b83cc29471fa114366e6806bc06bc7a25fd59641e41445327836"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:721c76e84fe669be19c5791da68232ca2e05ba5185575086e384352e2c309597"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82d8fd25b7f4675d0c47cf95b594d4e7b158aca33b76aa63d07186e13c0e0ab7"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3daeac64d5b371dea99714f08ffc2c208522ec6b06fbc7866a450dd446f5c0f"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:dccab8d5fa1ef9bfba0590ecf4d46df048d18ffe3eec01eeb73a42e0d9e7a8ba"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:aaf27faa992bfee0264dc1f03f4c75e9fcdda66a519db6b957a3f826e285cf12"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:eb30abc20df9ab0814b5a2524f23d75dcf83cde762c161917a2b4b7b55b1e518"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:c72fbbe68c6f32f251bdc08b8611c7b3060612236e960ef848e0a517ddbe76c5"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:982bb1e8b4ffda883b3d0a521e23abcd6fd17418f6d2c4118d257a10199c0ce3"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-win32.whl", hash = "sha256:43e0933a0eff183ee85833f341ec567c0980dae57c464d8a508e1b2ceb336471"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:d11b54acf878eef558599658b0ffca78138c8c3655cf4f3a4a673c437e67732e"}, - {file = "charset_normalizer-3.4.2-py3-none-any.whl", hash = "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0"}, - {file = "charset_normalizer-3.4.2.tar.gz", hash = "sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fb7f67a1bfa6e40b438170ebdc8158b78dc465a5a67b6dde178a46987b244a72"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cc9370a2da1ac13f0153780040f465839e6cccb4a1e44810124b4e22483c93fe"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:07a0eae9e2787b586e129fdcbe1af6997f8d0e5abaa0bc98c0e20e124d67e601"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:74d77e25adda8581ffc1c720f1c81ca082921329452eba58b16233ab1842141c"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d0e909868420b7049dafd3a31d45125b31143eec59235311fc4c57ea26a4acd2"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c6f162aabe9a91a309510d74eeb6507fab5fff92337a15acbe77753d88d9dcf0"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:4ca4c094de7771a98d7fbd67d9e5dbf1eb73efa4f744a730437d8a3a5cf994f0"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:02425242e96bcf29a49711b0ca9f37e451da7c70562bc10e8ed992a5a7a25cc0"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:78deba4d8f9590fe4dae384aeff04082510a709957e968753ff3c48399f6f92a"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-win32.whl", hash = "sha256:d79c198e27580c8e958906f803e63cddb77653731be08851c7df0b1a14a8fc0f"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-win_amd64.whl", hash = "sha256:c6e490913a46fa054e03699c70019ab869e990270597018cef1d8562132c2669"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b256ee2e749283ef3ddcff51a675ff43798d92d746d1a6e4631bf8c707d22d0b"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:13faeacfe61784e2559e690fc53fa4c5ae97c6fcedb8eb6fb8d0a15b475d2c64"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:00237675befef519d9af72169d8604a067d92755e84fe76492fef5441db05b91"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:585f3b2a80fbd26b048a0be90c5aae8f06605d3c92615911c3a2b03a8a3b796f"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e78314bdc32fa80696f72fa16dc61168fda4d6a0c014e0380f9d02f0e5d8a07"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:96b2b3d1a83ad55310de8c7b4a2d04d9277d5591f40761274856635acc5fcb30"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:939578d9d8fd4299220161fdd76e86c6a251987476f5243e8864a7844476ba14"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:fd10de089bcdcd1be95a2f73dbe6254798ec1bda9f450d5828c96f93e2536b9c"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1e8ac75d72fa3775e0b7cb7e4629cec13b7514d928d15ef8ea06bca03ef01cae"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-win32.whl", hash = "sha256:6cf8fd4c04756b6b60146d98cd8a77d0cdae0e1ca20329da2ac85eed779b6849"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:31a9a6f775f9bcd865d88ee350f0ffb0e25936a7f930ca98995c05abf1faf21c"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e28e334d3ff134e88989d90ba04b47d84382a828c061d0d1027b1b12a62b39b1"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0cacf8f7297b0c4fcb74227692ca46b4a5852f8f4f24b3c766dd94a1075c4884"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c6fd51128a41297f5409deab284fecbe5305ebd7e5a1f959bee1c054622b7018"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3cfb2aad70f2c6debfbcb717f23b7eb55febc0bb23dcffc0f076009da10c6392"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1606f4a55c0fd363d754049cdf400175ee96c992b1f8018b993941f221221c5f"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:027b776c26d38b7f15b26a5da1044f376455fb3766df8fc38563b4efbc515154"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:42e5088973e56e31e4fa58eb6bd709e42fc03799c11c42929592889a2e54c491"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:cc34f233c9e71701040d772aa7490318673aa7164a0efe3172b2981218c26d93"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:320e8e66157cc4e247d9ddca8e21f427efc7a04bbd0ac8a9faf56583fa543f9f"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-win32.whl", hash = "sha256:fb6fecfd65564f208cbf0fba07f107fb661bcd1a7c389edbced3f7a493f70e37"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:86df271bf921c2ee3818f0522e9a5b8092ca2ad8b065ece5d7d9d0e9f4849bcc"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:14c2a87c65b351109f6abfc424cab3927b3bdece6f706e4d12faaf3d52ee5efe"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:41d1fc408ff5fdfb910200ec0e74abc40387bccb3252f3f27c0676731df2b2c8"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1bb60174149316da1c35fa5233681f7c0f9f514509b8e399ab70fea5f17e45c9"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:30d006f98569de3459c2fc1f2acde170b7b2bd265dc1943e87e1a4efe1b67c31"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:416175faf02e4b0810f1f38bcb54682878a4af94059a1cd63b8747244420801f"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6aab0f181c486f973bc7262a97f5aca3ee7e1437011ef0c2ec04b5a11d16c927"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:fdabf8315679312cfa71302f9bd509ded4f2f263fb5b765cf1433b39106c3cc9"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:bd28b817ea8c70215401f657edef3a8aa83c29d447fb0b622c35403780ba11d5"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:18343b2d246dc6761a249ba1fb13f9ee9a2bcd95decc767319506056ea4ad4dc"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-win32.whl", hash = "sha256:6fb70de56f1859a3f71261cbe41005f56a7842cc348d3aeb26237560bfa5e0ce"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-win_amd64.whl", hash = "sha256:cf1ebb7d78e1ad8ec2a8c4732c7be2e736f6e5123a4146c5b89c9d1f585f8cef"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3cd35b7e8aedeb9e34c41385fda4f73ba609e561faedfae0a9e75e44ac558a15"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b89bc04de1d83006373429975f8ef9e7932534b8cc9ca582e4db7d20d91816db"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2001a39612b241dae17b4687898843f254f8748b796a2e16f1051a17078d991d"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8dcfc373f888e4fb39a7bc57e93e3b845e7f462dacc008d9749568b1c4ece096"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:18b97b8404387b96cdbd30ad660f6407799126d26a39ca65729162fd810a99aa"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ccf600859c183d70eb47e05a44cd80a4ce77394d1ac0f79dbd2dd90a69a3a049"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:53cd68b185d98dde4ad8990e56a58dea83a4162161b1ea9272e5c9182ce415e0"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:30a96e1e1f865f78b030d65241c1ee850cdf422d869e9028e2fc1d5e4db73b92"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d716a916938e03231e86e43782ca7878fb602a125a91e7acb8b5112e2e96ac16"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-win32.whl", hash = "sha256:c6dbd0ccdda3a2ba7c2ecd9d77b37f3b5831687d8dc1b6ca5f56a4880cc7b7ce"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-win_amd64.whl", hash = "sha256:73dc19b562516fc9bcf6e5d6e596df0b4eb98d87e4f79f3ae71840e6ed21361c"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0f2be7e0cf7754b9a30eb01f4295cc3d4358a479843b31f328afd210e2c7598c"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c60e092517a73c632ec38e290eba714e9627abe9d301c8c8a12ec32c314a2a4b"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:252098c8c7a873e17dd696ed98bbe91dbacd571da4b87df3736768efa7a792e4"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3653fad4fe3ed447a596ae8638b437f827234f01a8cd801842e43f3d0a6b281b"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8999f965f922ae054125286faf9f11bc6932184b93011d138925a1773830bbe9"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d95bfb53c211b57198bb91c46dd5a2d8018b3af446583aab40074bf7988401cb"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:5b413b0b1bfd94dbf4023ad6945889f374cd24e3f62de58d6bb102c4d9ae534a"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:b5e3b2d152e74e100a9e9573837aba24aab611d39428ded46f4e4022ea7d1942"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:a2d08ac246bb48479170408d6c19f6385fa743e7157d716e144cad849b2dd94b"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-win32.whl", hash = "sha256:ec557499516fc90fd374bf2e32349a2887a876fbf162c160e3c01b6849eaf557"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:5d8d01eac18c423815ed4f4a2ec3b439d654e55ee4ad610e153cf02faf67ea40"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:70bfc5f2c318afece2f5838ea5e4c3febada0be750fcf4775641052bbba14d05"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:23b6b24d74478dc833444cbd927c338349d6ae852ba53a0d02a2de1fce45b96e"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:34a7f768e3f985abdb42841e20e17b330ad3aaf4bb7e7aeeb73db2e70f077b99"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fb731e5deb0c7ef82d698b0f4c5bb724633ee2a489401594c5c88b02e6cb15f7"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:257f26fed7d7ff59921b78244f3cd93ed2af1800ff048c33f624c87475819dd7"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1ef99f0456d3d46a50945c98de1774da86f8e992ab5c77865ea8b8195341fc19"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:2c322db9c8c89009a990ef07c3bcc9f011a3269bc06782f916cd3d9eed7c9312"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:511729f456829ef86ac41ca78c63a5cb55240ed23b4b737faca0eb1abb1c41bc"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:88ab34806dea0671532d3f82d82b85e8fc23d7b2dd12fa837978dad9bb392a34"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-win32.whl", hash = "sha256:16a8770207946ac75703458e2c743631c79c59c5890c80011d536248f8eaa432"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:d22dbedd33326a4a5190dd4fe9e9e693ef12160c77382d9e87919bce54f3d4ca"}, + {file = "charset_normalizer-3.4.3-py3-none-any.whl", hash = "sha256:ce571ab16d890d23b5c278547ba694193a45011ff86a9162a71307ed9f86759a"}, + {file = "charset_normalizer-3.4.3.tar.gz", hash = "sha256:6fce4b8500244f6fcb71465d4a4930d132ba9ab8e71a7859e6a5d59851068d14"}, ] +[[package]] +name = "chromium" +version = "0.1.0" +description = "Modules Nemesis uses to handle Chromium files" +optional = false +python-versions = ">=3.12,<4.0" +groups = ["main"] +files = [] +develop = true + +[package.dependencies] +common = {path = "../../libs/common", develop = true} +dapr = "1.16.0" +file_linking = {path = "../file_linking", develop = true} +impacket = ">=0.12.0,<0.13.0" +nemesis_dpapi = {path = "../nemesis_dpapi", develop = true} +psycopg = {version = ">=3.0.0,<4.0.0", extras = ["binary"]} +structlog = ">=20.0.0,<30.0.0" + +[package.source] +type = "directory" +url = "../chromium" + [[package]] name = "click" -version = "8.2.1" +version = "8.3.0" description = "Composable command line interface toolkit" optional = false python-versions = ">=3.10" groups = ["main"] files = [ - {file = "click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b"}, - {file = "click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202"}, + {file = "click-8.3.0-py3-none-any.whl", hash = "sha256:9b9f285302c6e3064f4330c05f05b81945b2a39544279343e6e7c5f27a9baddc"}, + {file = "click-8.3.0.tar.gz", hash = "sha256:e7b8232224eba16f4ebe410c25ced9f7875cb5f3263ffc93cc3e8da705e229c4"}, ] [package.dependencies] @@ -533,12 +589,12 @@ version = "0.4.6" description = "Cross-platform colored terminal text." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -groups = ["main"] -markers = "platform_system == \"Windows\"" +groups = ["main", "dev"] files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] +markers = {main = "platform_system == \"Windows\" or sys_platform == \"win32\"", dev = "sys_platform == \"win32\""} [[package]] name = "common" @@ -551,7 +607,7 @@ files = [] develop = true [package.dependencies] -dapr = "^1.14.0" +dapr = "1.16.0" fastapi = "^0.115.6" minio = "^7.2.14" pydantic = "^2.10.5" @@ -618,14 +674,14 @@ test-randomorder = ["pytest-randomly"] [[package]] name = "dapr" -version = "1.15.0" +version = "1.16.0" description = "The official release of Dapr Python SDK." optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "dapr-1.15.0-py3-none-any.whl", hash = "sha256:0093bf6df5eb9a14fbab60191a619438e0b6b336f60a7994e184276bcc35d5fb"}, - {file = "dapr-1.15.0.tar.gz", hash = "sha256:6b2373084143f164cb00702758b17a14fc4442314a1f3e2be36ee008d486c47a"}, + {file = "dapr-1.16.0-py3-none-any.whl", hash = "sha256:076dd559a0b450eae24b1c2ae779c9299ed3e06a05c1f72719a6613af8d19ced"}, + {file = "dapr-1.16.0.tar.gz", hash = "sha256:c7e3d005552a598d07608d0d502b2bc432e86678f94b2beccc13a096b9198684"}, ] [package.dependencies] @@ -653,35 +709,64 @@ pefile = ">=2019.4.18" [[package]] name = "dnspython" -version = "2.7.0" +version = "2.8.0" description = "DNS toolkit" optional = false -python-versions = ">=3.9" +python-versions = ">=3.10" groups = ["main"] files = [ - {file = "dnspython-2.7.0-py3-none-any.whl", hash = "sha256:b4c34b7d10b51bcc3a5071e7b8dee77939f1e878477eeecc965e9835f63c6c86"}, - {file = "dnspython-2.7.0.tar.gz", hash = "sha256:ce9c432eda0dc91cf618a5cedf1a4e142651196bbcd2c80e89ed5a907e5cfaf1"}, + {file = "dnspython-2.8.0-py3-none-any.whl", hash = "sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af"}, + {file = "dnspython-2.8.0.tar.gz", hash = "sha256:181d3c6996452cb1189c4046c61599b84a5a86e099562ffde77d26984ff26d0f"}, ] [package.extras] -dev = ["black (>=23.1.0)", "coverage (>=7.0)", "flake8 (>=7)", "hypercorn (>=0.16.0)", "mypy (>=1.8)", "pylint (>=3)", "pytest (>=7.4)", "pytest-cov (>=4.1.0)", "quart-trio (>=0.11.0)", "sphinx (>=7.2.0)", "sphinx-rtd-theme (>=2.0.0)", "twine (>=4.0.0)", "wheel (>=0.42.0)"] -dnssec = ["cryptography (>=43)"] -doh = ["h2 (>=4.1.0)", "httpcore (>=1.0.0)", "httpx (>=0.26.0)"] -doq = ["aioquic (>=1.0.0)"] -idna = ["idna (>=3.7)"] -trio = ["trio (>=0.23)"] +dev = ["black (>=25.1.0)", "coverage (>=7.0)", "flake8 (>=7)", "hypercorn (>=0.17.0)", "mypy (>=1.17)", "pylint (>=3)", "pytest (>=8.4)", "pytest-cov (>=6.2.0)", "quart-trio (>=0.12.0)", "sphinx (>=8.2.0)", "sphinx-rtd-theme (>=3.0.0)", "twine (>=6.1.0)", "wheel (>=0.45.0)"] +dnssec = ["cryptography (>=45)"] +doh = ["h2 (>=4.2.0)", "httpcore (>=1.0.0)", "httpx (>=0.28.0)"] +doq = ["aioquic (>=1.2.0)"] +idna = ["idna (>=3.10)"] +trio = ["trio (>=0.30)"] wmi = ["wmi (>=1.5.1)"] +[[package]] +name = "dpapick3" +version = "0.7.1" +description = "A native implementation of DPAPI" +optional = false +python-versions = ">=3.2" +groups = ["main"] +files = [ + {file = "dpapick3-0.7.1-py3-none-any.whl", hash = "sha256:61999f6d4d08231799d3d62e3a48502476fcc0c4d29f12bb8ed064e3352f5da9"}, + {file = "dpapick3-0.7.1.tar.gz", hash = "sha256:3449366800d5bb313dd6d8d9d259d1b94498881ac74ced163749617a431921cb"}, +] + +[package.dependencies] +pyasn1 = "*" +pycryptodome = "*" +python-registry = "*" + +[[package]] +name = "enum-compat" +version = "0.0.3" +description = "enum/enum34 compatibility package" +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "enum-compat-0.0.3.tar.gz", hash = "sha256:3677daabed56a6f724451d585662253d8fb4e5569845aafa8bb0da36b1a8751e"}, + {file = "enum_compat-0.0.3-py3-none-any.whl", hash = "sha256:88091b617c7fc3bbbceae50db5958023c48dc40b50520005aa3bf27f8f7ea157"}, +] + [[package]] name = "fastapi" -version = "0.115.12" +version = "0.115.14" description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" optional = false python-versions = ">=3.8" groups = ["main"] files = [ - {file = "fastapi-0.115.12-py3-none-any.whl", hash = "sha256:e94613d6c05e27be7ffebdd6ea5f388112e5e430c8f7d6494a9d1d88d43e814d"}, - {file = "fastapi-0.115.12.tar.gz", hash = "sha256:1e2c2a2646905f9e83d32f04a3f86aff4a286669c6c950ca95b5fd68c2602681"}, + {file = "fastapi-0.115.14-py3-none-any.whl", hash = "sha256:6c0c8bf9420bd58f565e585036d971872472b4f7d3f6c73b698e10cffdefb3ca"}, + {file = "fastapi-0.115.14.tar.gz", hash = "sha256:b1de15cdc1c499a4da47914db35d0e4ef8f1ce62b624e94e0e5824421df99739"}, ] [package.dependencies] @@ -693,16 +778,39 @@ typing-extensions = ">=4.8.0" all = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=3.1.5)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.18)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"] standard = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "jinja2 (>=3.1.5)", "python-multipart (>=0.0.18)", "uvicorn[standard] (>=0.12.0)"] +[[package]] +name = "file-linking" +version = "0.1.0" +description = "Modules Nemesis uses to handle file links and listings" +optional = false +python-versions = ">=3.12,<4.0" +groups = ["main"] +files = [] +develop = true + +[package.dependencies] +asyncpg = "^0.30.0" +common = {path = "../common", develop = true} +dapr = "1.16.0" +psycopg = {version = ">=3.0.0,<4.0.0", extras = ["binary"]} +pytest-asyncio = "^1.2.0" +pyyaml = "^6.0.3" +structlog = ">=20.0.0,<30.0.0" + +[package.source] +type = "directory" +url = "../file_linking" + [[package]] name = "flask" -version = "3.1.1" +version = "3.1.2" description = "A simple framework for building complex web applications." optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "flask-3.1.1-py3-none-any.whl", hash = "sha256:07aae2bb5eaf77993ef57e357491839f5fd9f4dc281593a81a9e4d79a24f295c"}, - {file = "flask-3.1.1.tar.gz", hash = "sha256:284c7b8f2f58cb737f0cf1c30fd7eaf0ccfcde196099d24ecede3fc2005aa59e"}, + {file = "flask-3.1.2-py3-none-any.whl", hash = "sha256:ca1d8112ec8a6158cc29ea4858963350011b5c846a414cdb7a954aa9e967d03c"}, + {file = "flask-3.1.2.tar.gz", hash = "sha256:bf656c15c80190ed628ad08cdfd3aaa35beb087855e2f494910aa3774cc4fd87"}, ] [package.dependencies] @@ -719,116 +827,142 @@ dotenv = ["python-dotenv"] [[package]] name = "frozenlist" -version = "1.7.0" +version = "1.8.0" description = "A list-like structure which implements collections.abc.MutableSequence" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "frozenlist-1.7.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cc4df77d638aa2ed703b878dd093725b72a824c3c546c076e8fdf276f78ee84a"}, - {file = "frozenlist-1.7.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:716a9973a2cc963160394f701964fe25012600f3d311f60c790400b00e568b61"}, - {file = "frozenlist-1.7.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a0fd1bad056a3600047fb9462cff4c5322cebc59ebf5d0a3725e0ee78955001d"}, - {file = "frozenlist-1.7.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3789ebc19cb811163e70fe2bd354cea097254ce6e707ae42e56f45e31e96cb8e"}, - {file = "frozenlist-1.7.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:af369aa35ee34f132fcfad5be45fbfcde0e3a5f6a1ec0712857f286b7d20cca9"}, - {file = "frozenlist-1.7.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac64b6478722eeb7a3313d494f8342ef3478dff539d17002f849101b212ef97c"}, - {file = "frozenlist-1.7.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f89f65d85774f1797239693cef07ad4c97fdd0639544bad9ac4b869782eb1981"}, - {file = "frozenlist-1.7.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1073557c941395fdfcfac13eb2456cb8aad89f9de27bae29fabca8e563b12615"}, - {file = "frozenlist-1.7.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ed8d2fa095aae4bdc7fdd80351009a48d286635edffee66bf865e37a9125c50"}, - {file = "frozenlist-1.7.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:24c34bea555fe42d9f928ba0a740c553088500377448febecaa82cc3e88aa1fa"}, - {file = "frozenlist-1.7.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:69cac419ac6a6baad202c85aaf467b65ac860ac2e7f2ac1686dc40dbb52f6577"}, - {file = "frozenlist-1.7.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:960d67d0611f4c87da7e2ae2eacf7ea81a5be967861e0c63cf205215afbfac59"}, - {file = "frozenlist-1.7.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:41be2964bd4b15bf575e5daee5a5ce7ed3115320fb3c2b71fca05582ffa4dc9e"}, - {file = "frozenlist-1.7.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:46d84d49e00c9429238a7ce02dc0be8f6d7cd0cd405abd1bebdc991bf27c15bd"}, - {file = "frozenlist-1.7.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:15900082e886edb37480335d9d518cec978afc69ccbc30bd18610b7c1b22a718"}, - {file = "frozenlist-1.7.0-cp310-cp310-win32.whl", hash = "sha256:400ddd24ab4e55014bba442d917203c73b2846391dd42ca5e38ff52bb18c3c5e"}, - {file = "frozenlist-1.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:6eb93efb8101ef39d32d50bce242c84bcbddb4f7e9febfa7b524532a239b4464"}, - {file = "frozenlist-1.7.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:aa51e147a66b2d74de1e6e2cf5921890de6b0f4820b257465101d7f37b49fb5a"}, - {file = "frozenlist-1.7.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9b35db7ce1cd71d36ba24f80f0c9e7cff73a28d7a74e91fe83e23d27c7828750"}, - {file = "frozenlist-1.7.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:34a69a85e34ff37791e94542065c8416c1afbf820b68f720452f636d5fb990cd"}, - {file = "frozenlist-1.7.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a646531fa8d82c87fe4bb2e596f23173caec9185bfbca5d583b4ccfb95183e2"}, - {file = "frozenlist-1.7.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:79b2ffbba483f4ed36a0f236ccb85fbb16e670c9238313709638167670ba235f"}, - {file = "frozenlist-1.7.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a26f205c9ca5829cbf82bb2a84b5c36f7184c4316617d7ef1b271a56720d6b30"}, - {file = "frozenlist-1.7.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bcacfad3185a623fa11ea0e0634aac7b691aa925d50a440f39b458e41c561d98"}, - {file = "frozenlist-1.7.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:72c1b0fe8fe451b34f12dce46445ddf14bd2a5bcad7e324987194dc8e3a74c86"}, - {file = "frozenlist-1.7.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61d1a5baeaac6c0798ff6edfaeaa00e0e412d49946c53fae8d4b8e8b3566c4ae"}, - {file = "frozenlist-1.7.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7edf5c043c062462f09b6820de9854bf28cc6cc5b6714b383149745e287181a8"}, - {file = "frozenlist-1.7.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:d50ac7627b3a1bd2dcef6f9da89a772694ec04d9a61b66cf87f7d9446b4a0c31"}, - {file = "frozenlist-1.7.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ce48b2fece5aeb45265bb7a58259f45027db0abff478e3077e12b05b17fb9da7"}, - {file = "frozenlist-1.7.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:fe2365ae915a1fafd982c146754e1de6ab3478def8a59c86e1f7242d794f97d5"}, - {file = "frozenlist-1.7.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:45a6f2fdbd10e074e8814eb98b05292f27bad7d1883afbe009d96abdcf3bc898"}, - {file = "frozenlist-1.7.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:21884e23cffabb157a9dd7e353779077bf5b8f9a58e9b262c6caad2ef5f80a56"}, - {file = "frozenlist-1.7.0-cp311-cp311-win32.whl", hash = "sha256:284d233a8953d7b24f9159b8a3496fc1ddc00f4db99c324bd5fb5f22d8698ea7"}, - {file = "frozenlist-1.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:387cbfdcde2f2353f19c2f66bbb52406d06ed77519ac7ee21be0232147c2592d"}, - {file = "frozenlist-1.7.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:3dbf9952c4bb0e90e98aec1bd992b3318685005702656bc6f67c1a32b76787f2"}, - {file = "frozenlist-1.7.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:1f5906d3359300b8a9bb194239491122e6cf1444c2efb88865426f170c262cdb"}, - {file = "frozenlist-1.7.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3dabd5a8f84573c8d10d8859a50ea2dec01eea372031929871368c09fa103478"}, - {file = "frozenlist-1.7.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa57daa5917f1738064f302bf2626281a1cb01920c32f711fbc7bc36111058a8"}, - {file = "frozenlist-1.7.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c193dda2b6d49f4c4398962810fa7d7c78f032bf45572b3e04dd5249dff27e08"}, - {file = "frozenlist-1.7.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bfe2b675cf0aaa6d61bf8fbffd3c274b3c9b7b1623beb3809df8a81399a4a9c4"}, - {file = "frozenlist-1.7.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8fc5d5cda37f62b262405cf9652cf0856839c4be8ee41be0afe8858f17f4c94b"}, - {file = "frozenlist-1.7.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b0d5ce521d1dd7d620198829b87ea002956e4319002ef0bc8d3e6d045cb4646e"}, - {file = "frozenlist-1.7.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:488d0a7d6a0008ca0db273c542098a0fa9e7dfaa7e57f70acef43f32b3f69dca"}, - {file = "frozenlist-1.7.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:15a7eaba63983d22c54d255b854e8108e7e5f3e89f647fc854bd77a237e767df"}, - {file = "frozenlist-1.7.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:1eaa7e9c6d15df825bf255649e05bd8a74b04a4d2baa1ae46d9c2d00b2ca2cb5"}, - {file = "frozenlist-1.7.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e4389e06714cfa9d47ab87f784a7c5be91d3934cd6e9a7b85beef808297cc025"}, - {file = "frozenlist-1.7.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:73bd45e1488c40b63fe5a7df892baf9e2a4d4bb6409a2b3b78ac1c6236178e01"}, - {file = "frozenlist-1.7.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:99886d98e1643269760e5fe0df31e5ae7050788dd288947f7f007209b8c33f08"}, - {file = "frozenlist-1.7.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:290a172aae5a4c278c6da8a96222e6337744cd9c77313efe33d5670b9f65fc43"}, - {file = "frozenlist-1.7.0-cp312-cp312-win32.whl", hash = "sha256:426c7bc70e07cfebc178bc4c2bf2d861d720c4fff172181eeb4a4c41d4ca2ad3"}, - {file = "frozenlist-1.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:563b72efe5da92e02eb68c59cb37205457c977aa7a449ed1b37e6939e5c47c6a"}, - {file = "frozenlist-1.7.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ee80eeda5e2a4e660651370ebffd1286542b67e268aa1ac8d6dbe973120ef7ee"}, - {file = "frozenlist-1.7.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d1a81c85417b914139e3a9b995d4a1c84559afc839a93cf2cb7f15e6e5f6ed2d"}, - {file = "frozenlist-1.7.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cbb65198a9132ebc334f237d7b0df163e4de83fb4f2bdfe46c1e654bdb0c5d43"}, - {file = "frozenlist-1.7.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dab46c723eeb2c255a64f9dc05b8dd601fde66d6b19cdb82b2e09cc6ff8d8b5d"}, - {file = "frozenlist-1.7.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6aeac207a759d0dedd2e40745575ae32ab30926ff4fa49b1635def65806fddee"}, - {file = "frozenlist-1.7.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bd8c4e58ad14b4fa7802b8be49d47993182fdd4023393899632c88fd8cd994eb"}, - {file = "frozenlist-1.7.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04fb24d104f425da3540ed83cbfc31388a586a7696142004c577fa61c6298c3f"}, - {file = "frozenlist-1.7.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6a5c505156368e4ea6b53b5ac23c92d7edc864537ff911d2fb24c140bb175e60"}, - {file = "frozenlist-1.7.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8bd7eb96a675f18aa5c553eb7ddc24a43c8c18f22e1f9925528128c052cdbe00"}, - {file = "frozenlist-1.7.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:05579bf020096fe05a764f1f84cd104a12f78eaab68842d036772dc6d4870b4b"}, - {file = "frozenlist-1.7.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:376b6222d114e97eeec13d46c486facd41d4f43bab626b7c3f6a8b4e81a5192c"}, - {file = "frozenlist-1.7.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:0aa7e176ebe115379b5b1c95b4096fb1c17cce0847402e227e712c27bdb5a949"}, - {file = "frozenlist-1.7.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3fbba20e662b9c2130dc771e332a99eff5da078b2b2648153a40669a6d0e36ca"}, - {file = "frozenlist-1.7.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:f3f4410a0a601d349dd406b5713fec59b4cee7e71678d5b17edda7f4655a940b"}, - {file = "frozenlist-1.7.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e2cdfaaec6a2f9327bf43c933c0319a7c429058e8537c508964a133dffee412e"}, - {file = "frozenlist-1.7.0-cp313-cp313-win32.whl", hash = "sha256:5fc4df05a6591c7768459caba1b342d9ec23fa16195e744939ba5914596ae3e1"}, - {file = "frozenlist-1.7.0-cp313-cp313-win_amd64.whl", hash = "sha256:52109052b9791a3e6b5d1b65f4b909703984b770694d3eb64fad124c835d7cba"}, - {file = "frozenlist-1.7.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:a6f86e4193bb0e235ef6ce3dde5cbabed887e0b11f516ce8a0f4d3b33078ec2d"}, - {file = "frozenlist-1.7.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:82d664628865abeb32d90ae497fb93df398a69bb3434463d172b80fc25b0dd7d"}, - {file = "frozenlist-1.7.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:912a7e8375a1c9a68325a902f3953191b7b292aa3c3fb0d71a216221deca460b"}, - {file = "frozenlist-1.7.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9537c2777167488d539bc5de2ad262efc44388230e5118868e172dd4a552b146"}, - {file = "frozenlist-1.7.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:f34560fb1b4c3e30ba35fa9a13894ba39e5acfc5f60f57d8accde65f46cc5e74"}, - {file = "frozenlist-1.7.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:acd03d224b0175f5a850edc104ac19040d35419eddad04e7cf2d5986d98427f1"}, - {file = "frozenlist-1.7.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2038310bc582f3d6a09b3816ab01737d60bf7b1ec70f5356b09e84fb7408ab1"}, - {file = "frozenlist-1.7.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b8c05e4c8e5f36e5e088caa1bf78a687528f83c043706640a92cb76cd6999384"}, - {file = "frozenlist-1.7.0-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:765bb588c86e47d0b68f23c1bee323d4b703218037765dcf3f25c838c6fecceb"}, - {file = "frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:32dc2e08c67d86d0969714dd484fd60ff08ff81d1a1e40a77dd34a387e6ebc0c"}, - {file = "frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:c0303e597eb5a5321b4de9c68e9845ac8f290d2ab3f3e2c864437d3c5a30cd65"}, - {file = "frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:a47f2abb4e29b3a8d0b530f7c3598badc6b134562b1a5caee867f7c62fee51e3"}, - {file = "frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:3d688126c242a6fabbd92e02633414d40f50bb6002fa4cf995a1d18051525657"}, - {file = "frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:4e7e9652b3d367c7bd449a727dc79d5043f48b88d0cbfd4f9f1060cf2b414104"}, - {file = "frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:1a85e345b4c43db8b842cab1feb41be5cc0b10a1830e6295b69d7310f99becaf"}, - {file = "frozenlist-1.7.0-cp313-cp313t-win32.whl", hash = "sha256:3a14027124ddb70dfcee5148979998066897e79f89f64b13328595c4bdf77c81"}, - {file = "frozenlist-1.7.0-cp313-cp313t-win_amd64.whl", hash = "sha256:3bf8010d71d4507775f658e9823210b7427be36625b387221642725b515dcf3e"}, - {file = "frozenlist-1.7.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:cea3dbd15aea1341ea2de490574a4a37ca080b2ae24e4b4f4b51b9057b4c3630"}, - {file = "frozenlist-1.7.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7d536ee086b23fecc36c2073c371572374ff50ef4db515e4e503925361c24f71"}, - {file = "frozenlist-1.7.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:dfcebf56f703cb2e346315431699f00db126d158455e513bd14089d992101e44"}, - {file = "frozenlist-1.7.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:974c5336e61d6e7eb1ea5b929cb645e882aadab0095c5a6974a111e6479f8878"}, - {file = "frozenlist-1.7.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c70db4a0ab5ab20878432c40563573229a7ed9241506181bba12f6b7d0dc41cb"}, - {file = "frozenlist-1.7.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1137b78384eebaf70560a36b7b229f752fb64d463d38d1304939984d5cb887b6"}, - {file = "frozenlist-1.7.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e793a9f01b3e8b5c0bc646fb59140ce0efcc580d22a3468d70766091beb81b35"}, - {file = "frozenlist-1.7.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:74739ba8e4e38221d2c5c03d90a7e542cb8ad681915f4ca8f68d04f810ee0a87"}, - {file = "frozenlist-1.7.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e63344c4e929b1a01e29bc184bbb5fd82954869033765bfe8d65d09e336a677"}, - {file = "frozenlist-1.7.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2ea2a7369eb76de2217a842f22087913cdf75f63cf1307b9024ab82dfb525938"}, - {file = "frozenlist-1.7.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:836b42f472a0e006e02499cef9352ce8097f33df43baaba3e0a28a964c26c7d2"}, - {file = "frozenlist-1.7.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e22b9a99741294b2571667c07d9f8cceec07cb92aae5ccda39ea1b6052ed4319"}, - {file = "frozenlist-1.7.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:9a19e85cc503d958abe5218953df722748d87172f71b73cf3c9257a91b999890"}, - {file = "frozenlist-1.7.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:f22dac33bb3ee8fe3e013aa7b91dc12f60d61d05b7fe32191ffa84c3aafe77bd"}, - {file = "frozenlist-1.7.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:9ccec739a99e4ccf664ea0775149f2749b8a6418eb5b8384b4dc0a7d15d304cb"}, - {file = "frozenlist-1.7.0-cp39-cp39-win32.whl", hash = "sha256:b3950f11058310008a87757f3eee16a8e1ca97979833239439586857bc25482e"}, - {file = "frozenlist-1.7.0-cp39-cp39-win_amd64.whl", hash = "sha256:43a82fce6769c70f2f5a06248b614a7d268080a9d20f7457ef10ecee5af82b63"}, - {file = "frozenlist-1.7.0-py3-none-any.whl", hash = "sha256:9a5af342e34f7e97caf8c995864c7a396418ae2859cc6fdf1b1073020d516a7e"}, - {file = "frozenlist-1.7.0.tar.gz", hash = "sha256:2e310d81923c2437ea8670467121cc3e9b0f76d3043cc1d2331d56c7fb7a3a8f"}, + {file = "frozenlist-1.8.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b37f6d31b3dcea7deb5e9696e529a6aa4a898adc33db82da12e4c60a7c4d2011"}, + {file = "frozenlist-1.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ef2b7b394f208233e471abc541cc6991f907ffd47dc72584acee3147899d6565"}, + {file = "frozenlist-1.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a88f062f072d1589b7b46e951698950e7da00442fc1cacbe17e19e025dc327ad"}, + {file = "frozenlist-1.8.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f57fb59d9f385710aa7060e89410aeb5058b99e62f4d16b08b91986b9a2140c2"}, + {file = "frozenlist-1.8.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:799345ab092bee59f01a915620b5d014698547afd011e691a208637312db9186"}, + {file = "frozenlist-1.8.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c23c3ff005322a6e16f71bf8692fcf4d5a304aaafe1e262c98c6d4adc7be863e"}, + {file = "frozenlist-1.8.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8a76ea0f0b9dfa06f254ee06053d93a600865b3274358ca48a352ce4f0798450"}, + {file = "frozenlist-1.8.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c7366fe1418a6133d5aa824ee53d406550110984de7637d65a178010f759c6ef"}, + {file = "frozenlist-1.8.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:13d23a45c4cebade99340c4165bd90eeb4a56c6d8a9d8aa49568cac19a6d0dc4"}, + {file = "frozenlist-1.8.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:e4a3408834f65da56c83528fb52ce7911484f0d1eaf7b761fc66001db1646eff"}, + {file = "frozenlist-1.8.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:42145cd2748ca39f32801dad54aeea10039da6f86e303659db90db1c4b614c8c"}, + {file = "frozenlist-1.8.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:e2de870d16a7a53901e41b64ffdf26f2fbb8917b3e6ebf398098d72c5b20bd7f"}, + {file = "frozenlist-1.8.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:20e63c9493d33ee48536600d1a5c95eefc870cd71e7ab037763d1fbb89cc51e7"}, + {file = "frozenlist-1.8.0-cp310-cp310-win32.whl", hash = "sha256:adbeebaebae3526afc3c96fad434367cafbfd1b25d72369a9e5858453b1bb71a"}, + {file = "frozenlist-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:667c3777ca571e5dbeb76f331562ff98b957431df140b54c85fd4d52eea8d8f6"}, + {file = "frozenlist-1.8.0-cp310-cp310-win_arm64.whl", hash = "sha256:80f85f0a7cc86e7a54c46d99c9e1318ff01f4687c172ede30fd52d19d1da1c8e"}, + {file = "frozenlist-1.8.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:09474e9831bc2b2199fad6da3c14c7b0fbdd377cce9d3d77131be28906cb7d84"}, + {file = "frozenlist-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:17c883ab0ab67200b5f964d2b9ed6b00971917d5d8a92df149dc2c9779208ee9"}, + {file = "frozenlist-1.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fa47e444b8ba08fffd1c18e8cdb9a75db1b6a27f17507522834ad13ed5922b93"}, + {file = "frozenlist-1.8.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2552f44204b744fba866e573be4c1f9048d6a324dfe14475103fd51613eb1d1f"}, + {file = "frozenlist-1.8.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:957e7c38f250991e48a9a73e6423db1bb9dd14e722a10f6b8bb8e16a0f55f695"}, + {file = "frozenlist-1.8.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8585e3bb2cdea02fc88ffa245069c36555557ad3609e83be0ec71f54fd4abb52"}, + {file = "frozenlist-1.8.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:edee74874ce20a373d62dc28b0b18b93f645633c2943fd90ee9d898550770581"}, + {file = "frozenlist-1.8.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c9a63152fe95756b85f31186bddf42e4c02c6321207fd6601a1c89ebac4fe567"}, + {file = "frozenlist-1.8.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b6db2185db9be0a04fecf2f241c70b63b1a242e2805be291855078f2b404dd6b"}, + {file = "frozenlist-1.8.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:f4be2e3d8bc8aabd566f8d5b8ba7ecc09249d74ba3c9ed52e54dc23a293f0b92"}, + {file = "frozenlist-1.8.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:c8d1634419f39ea6f5c427ea2f90ca85126b54b50837f31497f3bf38266e853d"}, + {file = "frozenlist-1.8.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:1a7fa382a4a223773ed64242dbe1c9c326ec09457e6b8428efb4118c685c3dfd"}, + {file = "frozenlist-1.8.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:11847b53d722050808926e785df837353bd4d75f1d494377e59b23594d834967"}, + {file = "frozenlist-1.8.0-cp311-cp311-win32.whl", hash = "sha256:27c6e8077956cf73eadd514be8fb04d77fc946a7fe9f7fe167648b0b9085cc25"}, + {file = "frozenlist-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:ac913f8403b36a2c8610bbfd25b8013488533e71e62b4b4adce9c86c8cea905b"}, + {file = "frozenlist-1.8.0-cp311-cp311-win_arm64.whl", hash = "sha256:d4d3214a0f8394edfa3e303136d0575eece0745ff2b47bd2cb2e66dd92d4351a"}, + {file = "frozenlist-1.8.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:78f7b9e5d6f2fdb88cdde9440dc147259b62b9d3b019924def9f6478be254ac1"}, + {file = "frozenlist-1.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:229bf37d2e4acdaf808fd3f06e854a4a7a3661e871b10dc1f8f1896a3b05f18b"}, + {file = "frozenlist-1.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f833670942247a14eafbb675458b4e61c82e002a148f49e68257b79296e865c4"}, + {file = "frozenlist-1.8.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:494a5952b1c597ba44e0e78113a7266e656b9794eec897b19ead706bd7074383"}, + {file = "frozenlist-1.8.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96f423a119f4777a4a056b66ce11527366a8bb92f54e541ade21f2374433f6d4"}, + {file = "frozenlist-1.8.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3462dd9475af2025c31cc61be6652dfa25cbfb56cbbf52f4ccfe029f38decaf8"}, + {file = "frozenlist-1.8.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4c800524c9cd9bac5166cd6f55285957fcfc907db323e193f2afcd4d9abd69b"}, + {file = "frozenlist-1.8.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d6a5df73acd3399d893dafc71663ad22534b5aa4f94e8a2fabfe856c3c1b6a52"}, + {file = "frozenlist-1.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:405e8fe955c2280ce66428b3ca55e12b3c4e9c336fb2103a4937e891c69a4a29"}, + {file = "frozenlist-1.8.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:908bd3f6439f2fef9e85031b59fd4f1297af54415fb60e4254a95f75b3cab3f3"}, + {file = "frozenlist-1.8.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:294e487f9ec720bd8ffcebc99d575f7eff3568a08a253d1ee1a0378754b74143"}, + {file = "frozenlist-1.8.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:74c51543498289c0c43656701be6b077f4b265868fa7f8a8859c197006efb608"}, + {file = "frozenlist-1.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:776f352e8329135506a1d6bf16ac3f87bc25b28e765949282dcc627af36123aa"}, + {file = "frozenlist-1.8.0-cp312-cp312-win32.whl", hash = "sha256:433403ae80709741ce34038da08511d4a77062aa924baf411ef73d1146e74faf"}, + {file = "frozenlist-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:34187385b08f866104f0c0617404c8eb08165ab1272e884abc89c112e9c00746"}, + {file = "frozenlist-1.8.0-cp312-cp312-win_arm64.whl", hash = "sha256:fe3c58d2f5db5fbd18c2987cba06d51b0529f52bc3a6cdc33d3f4eab725104bd"}, + {file = "frozenlist-1.8.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8d92f1a84bb12d9e56f818b3a746f3efba93c1b63c8387a73dde655e1e42282a"}, + {file = "frozenlist-1.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:96153e77a591c8adc2ee805756c61f59fef4cf4073a9275ee86fe8cba41241f7"}, + {file = "frozenlist-1.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f21f00a91358803399890ab167098c131ec2ddd5f8f5fd5fe9c9f2c6fcd91e40"}, + {file = "frozenlist-1.8.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fb30f9626572a76dfe4293c7194a09fb1fe93ba94c7d4f720dfae3b646b45027"}, + {file = "frozenlist-1.8.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eaa352d7047a31d87dafcacbabe89df0aa506abb5b1b85a2fb91bc3faa02d822"}, + {file = "frozenlist-1.8.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:03ae967b4e297f58f8c774c7eabcce57fe3c2434817d4385c50661845a058121"}, + {file = "frozenlist-1.8.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f6292f1de555ffcc675941d65fffffb0a5bcd992905015f85d0592201793e0e5"}, + {file = "frozenlist-1.8.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29548f9b5b5e3460ce7378144c3010363d8035cea44bc0bf02d57f5a685e084e"}, + {file = "frozenlist-1.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ec3cc8c5d4084591b4237c0a272cc4f50a5b03396a47d9caaf76f5d7b38a4f11"}, + {file = "frozenlist-1.8.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:517279f58009d0b1f2e7c1b130b377a349405da3f7621ed6bfae50b10adf20c1"}, + {file = "frozenlist-1.8.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:db1e72ede2d0d7ccb213f218df6a078a9c09a7de257c2fe8fcef16d5925230b1"}, + {file = "frozenlist-1.8.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b4dec9482a65c54a5044486847b8a66bf10c9cb4926d42927ec4e8fd5db7fed8"}, + {file = "frozenlist-1.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:21900c48ae04d13d416f0e1e0c4d81f7931f73a9dfa0b7a8746fb2fe7dd970ed"}, + {file = "frozenlist-1.8.0-cp313-cp313-win32.whl", hash = "sha256:8b7b94a067d1c504ee0b16def57ad5738701e4ba10cec90529f13fa03c833496"}, + {file = "frozenlist-1.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:878be833caa6a3821caf85eb39c5ba92d28e85df26d57afb06b35b2efd937231"}, + {file = "frozenlist-1.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:44389d135b3ff43ba8cc89ff7f51f5a0bb6b63d829c8300f79a2fe4fe61bcc62"}, + {file = "frozenlist-1.8.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:e25ac20a2ef37e91c1b39938b591457666a0fa835c7783c3a8f33ea42870db94"}, + {file = "frozenlist-1.8.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:07cdca25a91a4386d2e76ad992916a85038a9b97561bf7a3fd12d5d9ce31870c"}, + {file = "frozenlist-1.8.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4e0c11f2cc6717e0a741f84a527c52616140741cd812a50422f83dc31749fb52"}, + {file = "frozenlist-1.8.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b3210649ee28062ea6099cfda39e147fa1bc039583c8ee4481cb7811e2448c51"}, + {file = "frozenlist-1.8.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:581ef5194c48035a7de2aefc72ac6539823bb71508189e5de01d60c9dcd5fa65"}, + {file = "frozenlist-1.8.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3ef2d026f16a2b1866e1d86fc4e1291e1ed8a387b2c333809419a2f8b3a77b82"}, + {file = "frozenlist-1.8.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5500ef82073f599ac84d888e3a8c1f77ac831183244bfd7f11eaa0289fb30714"}, + {file = "frozenlist-1.8.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:50066c3997d0091c411a66e710f4e11752251e6d2d73d70d8d5d4c76442a199d"}, + {file = "frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:5c1c8e78426e59b3f8005e9b19f6ff46e5845895adbde20ece9218319eca6506"}, + {file = "frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:eefdba20de0d938cec6a89bd4d70f346a03108a19b9df4248d3cf0d88f1b0f51"}, + {file = "frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:cf253e0e1c3ceb4aaff6df637ce033ff6535fb8c70a764a8f46aafd3d6ab798e"}, + {file = "frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:032efa2674356903cd0261c4317a561a6850f3ac864a63fc1583147fb05a79b0"}, + {file = "frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6da155091429aeba16851ecb10a9104a108bcd32f6c1642867eadaee401c1c41"}, + {file = "frozenlist-1.8.0-cp313-cp313t-win32.whl", hash = "sha256:0f96534f8bfebc1a394209427d0f8a63d343c9779cda6fc25e8e121b5fd8555b"}, + {file = "frozenlist-1.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5d63a068f978fc69421fb0e6eb91a9603187527c86b7cd3f534a5b77a592b888"}, + {file = "frozenlist-1.8.0-cp313-cp313t-win_arm64.whl", hash = "sha256:bf0a7e10b077bf5fb9380ad3ae8ce20ef919a6ad93b4552896419ac7e1d8e042"}, + {file = "frozenlist-1.8.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cee686f1f4cadeb2136007ddedd0aaf928ab95216e7691c63e50a8ec066336d0"}, + {file = "frozenlist-1.8.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:119fb2a1bd47307e899c2fac7f28e85b9a543864df47aa7ec9d3c1b4545f096f"}, + {file = "frozenlist-1.8.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4970ece02dbc8c3a92fcc5228e36a3e933a01a999f7094ff7c23fbd2beeaa67c"}, + {file = "frozenlist-1.8.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:cba69cb73723c3f329622e34bdbf5ce1f80c21c290ff04256cff1cd3c2036ed2"}, + {file = "frozenlist-1.8.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:778a11b15673f6f1df23d9586f83c4846c471a8af693a22e066508b77d201ec8"}, + {file = "frozenlist-1.8.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0325024fe97f94c41c08872db482cf8ac4800d80e79222c6b0b7b162d5b13686"}, + {file = "frozenlist-1.8.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:97260ff46b207a82a7567b581ab4190bd4dfa09f4db8a8b49d1a958f6aa4940e"}, + {file = "frozenlist-1.8.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:54b2077180eb7f83dd52c40b2750d0a9f175e06a42e3213ce047219de902717a"}, + {file = "frozenlist-1.8.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2f05983daecab868a31e1da44462873306d3cbfd76d1f0b5b69c473d21dbb128"}, + {file = "frozenlist-1.8.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:33f48f51a446114bc5d251fb2954ab0164d5be02ad3382abcbfe07e2531d650f"}, + {file = "frozenlist-1.8.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:154e55ec0655291b5dd1b8731c637ecdb50975a2ae70c606d100750a540082f7"}, + {file = "frozenlist-1.8.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:4314debad13beb564b708b4a496020e5306c7333fa9a3ab90374169a20ffab30"}, + {file = "frozenlist-1.8.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:073f8bf8becba60aa931eb3bc420b217bb7d5b8f4750e6f8b3be7f3da85d38b7"}, + {file = "frozenlist-1.8.0-cp314-cp314-win32.whl", hash = "sha256:bac9c42ba2ac65ddc115d930c78d24ab8d4f465fd3fc473cdedfccadb9429806"}, + {file = "frozenlist-1.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:3e0761f4d1a44f1d1a47996511752cf3dcec5bbdd9cc2b4fe595caf97754b7a0"}, + {file = "frozenlist-1.8.0-cp314-cp314-win_arm64.whl", hash = "sha256:d1eaff1d00c7751b7c6662e9c5ba6eb2c17a2306ba5e2a37f24ddf3cc953402b"}, + {file = "frozenlist-1.8.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:d3bb933317c52d7ea5004a1c442eef86f426886fba134ef8cf4226ea6ee1821d"}, + {file = "frozenlist-1.8.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:8009897cdef112072f93a0efdce29cd819e717fd2f649ee3016efd3cd885a7ed"}, + {file = "frozenlist-1.8.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2c5dcbbc55383e5883246d11fd179782a9d07a986c40f49abe89ddf865913930"}, + {file = "frozenlist-1.8.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:39ecbc32f1390387d2aa4f5a995e465e9e2f79ba3adcac92d68e3e0afae6657c"}, + {file = "frozenlist-1.8.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92db2bf818d5cc8d9c1f1fc56b897662e24ea5adb36ad1f1d82875bd64e03c24"}, + {file = "frozenlist-1.8.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2dc43a022e555de94c3b68a4ef0b11c4f747d12c024a520c7101709a2144fb37"}, + {file = "frozenlist-1.8.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cb89a7f2de3602cfed448095bab3f178399646ab7c61454315089787df07733a"}, + {file = "frozenlist-1.8.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:33139dc858c580ea50e7e60a1b0ea003efa1fd42e6ec7fdbad78fff65fad2fd2"}, + {file = "frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:168c0969a329b416119507ba30b9ea13688fafffac1b7822802537569a1cb0ef"}, + {file = "frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:28bd570e8e189d7f7b001966435f9dac6718324b5be2990ac496cf1ea9ddb7fe"}, + {file = "frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b2a095d45c5d46e5e79ba1e5b9cb787f541a8dee0433836cea4b96a2c439dcd8"}, + {file = "frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:eab8145831a0d56ec9c4139b6c3e594c7a83c2c8be25d5bcf2d86136a532287a"}, + {file = "frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:974b28cf63cc99dfb2188d8d222bc6843656188164848c4f679e63dae4b0708e"}, + {file = "frozenlist-1.8.0-cp314-cp314t-win32.whl", hash = "sha256:342c97bf697ac5480c0a7ec73cd700ecfa5a8a40ac923bd035484616efecc2df"}, + {file = "frozenlist-1.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:06be8f67f39c8b1dc671f5d83aaefd3358ae5cdcf8314552c57e7ed3e6475bdd"}, + {file = "frozenlist-1.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:102e6314ca4da683dca92e3b1355490fed5f313b768500084fbe6371fddfdb79"}, + {file = "frozenlist-1.8.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:d8b7138e5cd0647e4523d6685b0eac5d4be9a184ae9634492f25c6eb38c12a47"}, + {file = "frozenlist-1.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a6483e309ca809f1efd154b4d37dc6d9f61037d6c6a81c2dc7a15cb22c8c5dca"}, + {file = "frozenlist-1.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1b9290cf81e95e93fdf90548ce9d3c1211cf574b8e3f4b3b7cb0537cf2227068"}, + {file = "frozenlist-1.8.0-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:59a6a5876ca59d1b63af8cd5e7ffffb024c3dc1e9cf9301b21a2e76286505c95"}, + {file = "frozenlist-1.8.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6dc4126390929823e2d2d9dc79ab4046ed74680360fc5f38b585c12c66cdf459"}, + {file = "frozenlist-1.8.0-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:332db6b2563333c5671fecacd085141b5800cb866be16d5e3eb15a2086476675"}, + {file = "frozenlist-1.8.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9ff15928d62a0b80bb875655c39bf517938c7d589554cbd2669be42d97c2cb61"}, + {file = "frozenlist-1.8.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7bf6cdf8e07c8151fba6fe85735441240ec7f619f935a5205953d58009aef8c6"}, + {file = "frozenlist-1.8.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:48e6d3f4ec5c7273dfe83ff27c91083c6c9065af655dc2684d2c200c94308bb5"}, + {file = "frozenlist-1.8.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:1a7607e17ad33361677adcd1443edf6f5da0ce5e5377b798fba20fae194825f3"}, + {file = "frozenlist-1.8.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:5a3a935c3a4e89c733303a2d5a7c257ea44af3a56c8202df486b7f5de40f37e1"}, + {file = "frozenlist-1.8.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:940d4a017dbfed9daf46a3b086e1d2167e7012ee297fef9e1c545c4d022f5178"}, + {file = "frozenlist-1.8.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:b9be22a69a014bc47e78072d0ecae716f5eb56c15238acca0f43d6eb8e4a5bda"}, + {file = "frozenlist-1.8.0-cp39-cp39-win32.whl", hash = "sha256:1aa77cb5697069af47472e39612976ed05343ff2e84a3dcf15437b232cbfd087"}, + {file = "frozenlist-1.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:7398c222d1d405e796970320036b1b563892b65809d9e5261487bb2c7f7b5c6a"}, + {file = "frozenlist-1.8.0-cp39-cp39-win_arm64.whl", hash = "sha256:b4f3b365f31c6cd4af24545ca0a244a53688cad8834e32f56831c4923b50a103"}, + {file = "frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d"}, + {file = "frozenlist-1.8.0.tar.gz", hash = "sha256:3ede829ed8d842f6cd48fc7081d7a41001a56f1f38603f9d49bf3020d59a31ad"}, ] [[package]] @@ -851,84 +985,97 @@ grpc = ["grpcio (>=1.44.0,<2.0.0)"] [[package]] name = "grpcio" -version = "1.73.0" +version = "1.75.1" description = "HTTP/2-based RPC framework" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "grpcio-1.73.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:d050197eeed50f858ef6c51ab09514856f957dba7b1f7812698260fc9cc417f6"}, - {file = "grpcio-1.73.0-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:ebb8d5f4b0200916fb292a964a4d41210de92aba9007e33d8551d85800ea16cb"}, - {file = "grpcio-1.73.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:c0811331b469e3f15dda5f90ab71bcd9681189a83944fd6dc908e2c9249041ef"}, - {file = "grpcio-1.73.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:12787c791c3993d0ea1cc8bf90393647e9a586066b3b322949365d2772ba965b"}, - {file = "grpcio-1.73.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c17771e884fddf152f2a0df12478e8d02853e5b602a10a9a9f1f52fa02b1d32"}, - {file = "grpcio-1.73.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:275e23d4c428c26b51857bbd95fcb8e528783597207ec592571e4372b300a29f"}, - {file = "grpcio-1.73.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:9ffc972b530bf73ef0f948f799482a1bf12d9b6f33406a8e6387c0ca2098a833"}, - {file = "grpcio-1.73.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ebd8d269df64aff092b2cec5e015d8ae09c7e90888b5c35c24fdca719a2c9f35"}, - {file = "grpcio-1.73.0-cp310-cp310-win32.whl", hash = "sha256:072d8154b8f74300ed362c01d54af8b93200c1a9077aeaea79828d48598514f1"}, - {file = "grpcio-1.73.0-cp310-cp310-win_amd64.whl", hash = "sha256:ce953d9d2100e1078a76a9dc2b7338d5415924dc59c69a15bf6e734db8a0f1ca"}, - {file = "grpcio-1.73.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:51036f641f171eebe5fa7aaca5abbd6150f0c338dab3a58f9111354240fe36ec"}, - {file = "grpcio-1.73.0-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:d12bbb88381ea00bdd92c55aff3da3391fd85bc902c41275c8447b86f036ce0f"}, - {file = "grpcio-1.73.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:483c507c2328ed0e01bc1adb13d1eada05cc737ec301d8e5a8f4a90f387f1790"}, - {file = "grpcio-1.73.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c201a34aa960c962d0ce23fe5f423f97e9d4b518ad605eae6d0a82171809caaa"}, - {file = "grpcio-1.73.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:859f70c8e435e8e1fa060e04297c6818ffc81ca9ebd4940e180490958229a45a"}, - {file = "grpcio-1.73.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e2459a27c6886e7e687e4e407778425f3c6a971fa17a16420227bda39574d64b"}, - {file = "grpcio-1.73.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:e0084d4559ee3dbdcce9395e1bc90fdd0262529b32c417a39ecbc18da8074ac7"}, - {file = "grpcio-1.73.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ef5fff73d5f724755693a464d444ee0a448c6cdfd3c1616a9223f736c622617d"}, - {file = "grpcio-1.73.0-cp311-cp311-win32.whl", hash = "sha256:965a16b71a8eeef91fc4df1dc40dc39c344887249174053814f8a8e18449c4c3"}, - {file = "grpcio-1.73.0-cp311-cp311-win_amd64.whl", hash = "sha256:b71a7b4483d1f753bbc11089ff0f6fa63b49c97a9cc20552cded3fcad466d23b"}, - {file = "grpcio-1.73.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:fb9d7c27089d9ba3746f18d2109eb530ef2a37452d2ff50f5a6696cd39167d3b"}, - {file = "grpcio-1.73.0-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:128ba2ebdac41e41554d492b82c34586a90ebd0766f8ebd72160c0e3a57b9155"}, - {file = "grpcio-1.73.0-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:068ecc415f79408d57a7f146f54cdf9f0acb4b301a52a9e563973dc981e82f3d"}, - {file = "grpcio-1.73.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ddc1cfb2240f84d35d559ade18f69dcd4257dbaa5ba0de1a565d903aaab2968"}, - {file = "grpcio-1.73.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e53007f70d9783f53b41b4cf38ed39a8e348011437e4c287eee7dd1d39d54b2f"}, - {file = "grpcio-1.73.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4dd8d8d092efede7d6f48d695ba2592046acd04ccf421436dd7ed52677a9ad29"}, - {file = "grpcio-1.73.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:70176093d0a95b44d24baa9c034bb67bfe2b6b5f7ebc2836f4093c97010e17fd"}, - {file = "grpcio-1.73.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:085ebe876373ca095e24ced95c8f440495ed0b574c491f7f4f714ff794bbcd10"}, - {file = "grpcio-1.73.0-cp312-cp312-win32.whl", hash = "sha256:cfc556c1d6aef02c727ec7d0016827a73bfe67193e47c546f7cadd3ee6bf1a60"}, - {file = "grpcio-1.73.0-cp312-cp312-win_amd64.whl", hash = "sha256:bbf45d59d090bf69f1e4e1594832aaf40aa84b31659af3c5e2c3f6a35202791a"}, - {file = "grpcio-1.73.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:da1d677018ef423202aca6d73a8d3b2cb245699eb7f50eb5f74cae15a8e1f724"}, - {file = "grpcio-1.73.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:36bf93f6a657f37c131d9dd2c391b867abf1426a86727c3575393e9e11dadb0d"}, - {file = "grpcio-1.73.0-cp313-cp313-manylinux_2_17_aarch64.whl", hash = "sha256:d84000367508ade791d90c2bafbd905574b5ced8056397027a77a215d601ba15"}, - {file = "grpcio-1.73.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c98ba1d928a178ce33f3425ff823318040a2b7ef875d30a0073565e5ceb058d9"}, - {file = "grpcio-1.73.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a73c72922dfd30b396a5f25bb3a4590195ee45ecde7ee068acb0892d2900cf07"}, - {file = "grpcio-1.73.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:10e8edc035724aba0346a432060fd192b42bd03675d083c01553cab071a28da5"}, - {file = "grpcio-1.73.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:f5cdc332b503c33b1643b12ea933582c7b081957c8bc2ea4cc4bc58054a09288"}, - {file = "grpcio-1.73.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:07ad7c57233c2109e4ac999cb9c2710c3b8e3f491a73b058b0ce431f31ed8145"}, - {file = "grpcio-1.73.0-cp313-cp313-win32.whl", hash = "sha256:0eb5df4f41ea10bda99a802b2a292d85be28958ede2a50f2beb8c7fc9a738419"}, - {file = "grpcio-1.73.0-cp313-cp313-win_amd64.whl", hash = "sha256:38cf518cc54cd0c47c9539cefa8888549fcc067db0b0c66a46535ca8032020c4"}, - {file = "grpcio-1.73.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:1284850607901cfe1475852d808e5a102133461ec9380bc3fc9ebc0686ee8e32"}, - {file = "grpcio-1.73.0-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:0e092a4b28eefb63eec00d09ef33291cd4c3a0875cde29aec4d11d74434d222c"}, - {file = "grpcio-1.73.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:33577fe7febffe8ebad458744cfee8914e0c10b09f0ff073a6b149a84df8ab8f"}, - {file = "grpcio-1.73.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:60813d8a16420d01fa0da1fc7ebfaaa49a7e5051b0337cd48f4f950eb249a08e"}, - {file = "grpcio-1.73.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2a9c957dc65e5d474378d7bcc557e9184576605d4b4539e8ead6e351d7ccce20"}, - {file = "grpcio-1.73.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:3902b71407d021163ea93c70c8531551f71ae742db15b66826cf8825707d2908"}, - {file = "grpcio-1.73.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:1dd7fa7276dcf061e2d5f9316604499eea06b1b23e34a9380572d74fe59915a8"}, - {file = "grpcio-1.73.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2d1510c4ea473110cb46a010555f2c1a279d1c256edb276e17fa571ba1e8927c"}, - {file = "grpcio-1.73.0-cp39-cp39-win32.whl", hash = "sha256:d0a1517b2005ba1235a1190b98509264bf72e231215dfeef8db9a5a92868789e"}, - {file = "grpcio-1.73.0-cp39-cp39-win_amd64.whl", hash = "sha256:6228f7eb6d9f785f38b589d49957fca5df3d5b5349e77d2d89b14e390165344c"}, - {file = "grpcio-1.73.0.tar.gz", hash = "sha256:3af4c30918a7f0d39de500d11255f8d9da4f30e94a2033e70fe2a720e184bd8e"}, + {file = "grpcio-1.75.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:1712b5890b22547dd29f3215c5788d8fc759ce6dd0b85a6ba6e2731f2d04c088"}, + {file = "grpcio-1.75.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:8d04e101bba4b55cea9954e4aa71c24153ba6182481b487ff376da28d4ba46cf"}, + {file = "grpcio-1.75.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:683cfc70be0c1383449097cba637317e4737a357cfc185d887fd984206380403"}, + {file = "grpcio-1.75.1-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:491444c081a54dcd5e6ada57314321ae526377f498d4aa09d975c3241c5b9e1c"}, + {file = "grpcio-1.75.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ce08d4e112d0d38487c2b631ec8723deac9bc404e9c7b1011426af50a79999e4"}, + {file = "grpcio-1.75.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5a2acda37fc926ccc4547977ac3e56b1df48fe200de968e8c8421f6e3093df6c"}, + {file = "grpcio-1.75.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:745c5fe6bf05df6a04bf2d11552c7d867a2690759e7ab6b05c318a772739bd75"}, + {file = "grpcio-1.75.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:259526a7159d39e2db40d566fe3e8f8e034d0fb2db5bf9c00e09aace655a4c2b"}, + {file = "grpcio-1.75.1-cp310-cp310-win32.whl", hash = "sha256:f4b29b9aabe33fed5df0a85e5f13b09ff25e2c05bd5946d25270a8bd5682dac9"}, + {file = "grpcio-1.75.1-cp310-cp310-win_amd64.whl", hash = "sha256:cf2e760978dcce7ff7d465cbc7e276c3157eedc4c27aa6de7b594c7a295d3d61"}, + {file = "grpcio-1.75.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:573855ca2e58e35032aff30bfbd1ee103fbcf4472e4b28d4010757700918e326"}, + {file = "grpcio-1.75.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:6a4996a2c8accc37976dc142d5991adf60733e223e5c9a2219e157dc6a8fd3a2"}, + {file = "grpcio-1.75.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b1ea1bbe77ecbc1be00af2769f4ae4a88ce93be57a4f3eebd91087898ed749f9"}, + {file = "grpcio-1.75.1-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:e5b425aee54cc5e3e3c58f00731e8a33f5567965d478d516d35ef99fd648ab68"}, + {file = "grpcio-1.75.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0049a7bf547dafaeeb1db17079ce79596c298bfe308fc084d023c8907a845b9a"}, + {file = "grpcio-1.75.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5b8ea230c7f77c0a1a3208a04a1eda164633fb0767b4cefd65a01079b65e5b1f"}, + {file = "grpcio-1.75.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:36990d629c3c9fb41e546414e5af52d0a7af37ce7113d9682c46d7e2919e4cca"}, + {file = "grpcio-1.75.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b10ad908118d38c2453ade7ff790e5bce36580c3742919007a2a78e3a1e521ca"}, + {file = "grpcio-1.75.1-cp311-cp311-win32.whl", hash = "sha256:d6be2b5ee7bea656c954dcf6aa8093c6f0e6a3ef9945c99d99fcbfc88c5c0bfe"}, + {file = "grpcio-1.75.1-cp311-cp311-win_amd64.whl", hash = "sha256:61c692fb05956b17dd6d1ab480f7f10ad0536dba3bc8fd4e3c7263dc244ed772"}, + {file = "grpcio-1.75.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:7b888b33cd14085d86176b1628ad2fcbff94cfbbe7809465097aa0132e58b018"}, + {file = "grpcio-1.75.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:8775036efe4ad2085975531d221535329f5dac99b6c2a854a995456098f99546"}, + {file = "grpcio-1.75.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:bb658f703468d7fbb5dcc4037c65391b7dc34f808ac46ed9136c24fc5eeb041d"}, + {file = "grpcio-1.75.1-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:4b7177a1cdb3c51b02b0c0a256b0a72fdab719600a693e0e9037949efffb200b"}, + {file = "grpcio-1.75.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7d4fa6ccc3ec2e68a04f7b883d354d7fea22a34c44ce535a2f0c0049cf626ddf"}, + {file = "grpcio-1.75.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3d86880ecaeb5b2f0a8afa63824de93adb8ebe4e49d0e51442532f4e08add7d6"}, + {file = "grpcio-1.75.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a8041d2f9e8a742aeae96f4b047ee44e73619f4f9d24565e84d5446c623673b6"}, + {file = "grpcio-1.75.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3652516048bf4c314ce12be37423c79829f46efffb390ad64149a10c6071e8de"}, + {file = "grpcio-1.75.1-cp312-cp312-win32.whl", hash = "sha256:44b62345d8403975513af88da2f3d5cc76f73ca538ba46596f92a127c2aea945"}, + {file = "grpcio-1.75.1-cp312-cp312-win_amd64.whl", hash = "sha256:b1e191c5c465fa777d4cafbaacf0c01e0d5278022082c0abbd2ee1d6454ed94d"}, + {file = "grpcio-1.75.1-cp313-cp313-linux_armv7l.whl", hash = "sha256:3bed22e750d91d53d9e31e0af35a7b0b51367e974e14a4ff229db5b207647884"}, + {file = "grpcio-1.75.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:5b8f381eadcd6ecaa143a21e9e80a26424c76a0a9b3d546febe6648f3a36a5ac"}, + {file = "grpcio-1.75.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5bf4001d3293e3414d0cf99ff9b1139106e57c3a66dfff0c5f60b2a6286ec133"}, + {file = "grpcio-1.75.1-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:9f82ff474103e26351dacfe8d50214e7c9322960d8d07ba7fa1d05ff981c8b2d"}, + {file = "grpcio-1.75.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0ee119f4f88d9f75414217823d21d75bfe0e6ed40135b0cbbfc6376bc9f7757d"}, + {file = "grpcio-1.75.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:664eecc3abe6d916fa6cf8dd6b778e62fb264a70f3430a3180995bf2da935446"}, + {file = "grpcio-1.75.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:c32193fa08b2fbebf08fe08e84f8a0aad32d87c3ad42999c65e9449871b1c66e"}, + {file = "grpcio-1.75.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5cebe13088b9254f6e615bcf1da9131d46cfa4e88039454aca9cb65f639bd3bc"}, + {file = "grpcio-1.75.1-cp313-cp313-win32.whl", hash = "sha256:4b4c678e7ed50f8ae8b8dbad15a865ee73ce12668b6aaf411bf3258b5bc3f970"}, + {file = "grpcio-1.75.1-cp313-cp313-win_amd64.whl", hash = "sha256:5573f51e3f296a1bcf71e7a690c092845fb223072120f4bdb7a5b48e111def66"}, + {file = "grpcio-1.75.1-cp314-cp314-linux_armv7l.whl", hash = "sha256:c05da79068dd96723793bffc8d0e64c45f316248417515f28d22204d9dae51c7"}, + {file = "grpcio-1.75.1-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:06373a94fd16ec287116a825161dca179a0402d0c60674ceeec8c9fba344fe66"}, + {file = "grpcio-1.75.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4484f4b7287bdaa7a5b3980f3c7224c3c622669405d20f69549f5fb956ad0421"}, + {file = "grpcio-1.75.1-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:2720c239c1180eee69f7883c1d4c83fc1a495a2535b5fa322887c70bf02b16e8"}, + {file = "grpcio-1.75.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:07a554fa31c668cf0e7a188678ceeca3cb8fead29bbe455352e712ec33ca701c"}, + {file = "grpcio-1.75.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:3e71a2105210366bfc398eef7f57a664df99194f3520edb88b9c3a7e46ee0d64"}, + {file = "grpcio-1.75.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:8679aa8a5b67976776d3c6b0521e99d1c34db8a312a12bcfd78a7085cb9b604e"}, + {file = "grpcio-1.75.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:aad1c774f4ebf0696a7f148a56d39a3432550612597331792528895258966dc0"}, + {file = "grpcio-1.75.1-cp314-cp314-win32.whl", hash = "sha256:62ce42d9994446b307649cb2a23335fa8e927f7ab2cbf5fcb844d6acb4d85f9c"}, + {file = "grpcio-1.75.1-cp314-cp314-win_amd64.whl", hash = "sha256:f86e92275710bea3000cb79feca1762dc0ad3b27830dd1a74e82ab321d4ee464"}, + {file = "grpcio-1.75.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:c09fba33327c3ac11b5c33dbdd8218eef8990d78f83b1656d628831812a8c0fb"}, + {file = "grpcio-1.75.1-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:7e21400b037be29545704889e72e586c238e346dcb2d08d8a7288d16c883a9ec"}, + {file = "grpcio-1.75.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c12121e509b9f8b0914d10054d24120237d19e870b1cd82acbb8a9b9ddd198a3"}, + {file = "grpcio-1.75.1-cp39-cp39-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:73577a93e692b3474b1bfe84285d098de36705dbd838bb4d6a056d326e4dc880"}, + {file = "grpcio-1.75.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e19e7dfa0d7ca7dea22be464339e18ac608fd75d88c56770c646cdabe54bc724"}, + {file = "grpcio-1.75.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:4e1c28f51c1cf67eccdfc1065e8e866c9ed622f09773ca60947089c117f848a1"}, + {file = "grpcio-1.75.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:030a6164bc2ca726052778c0cf8e3249617a34e368354f9e6107c27ad4af8c28"}, + {file = "grpcio-1.75.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:67697efef5a98d46d5db7b1720fa4043536f8b8e5072a5d61cfca762f287e939"}, + {file = "grpcio-1.75.1-cp39-cp39-win32.whl", hash = "sha256:52015cf73eb5d76f6404e0ce0505a69b51fd1f35810b3a01233b34b10baafb41"}, + {file = "grpcio-1.75.1-cp39-cp39-win_amd64.whl", hash = "sha256:9fe51e4a1f896ea84ac750900eae34d9e9b896b5b1e4a30b02dc31ad29f36383"}, + {file = "grpcio-1.75.1.tar.gz", hash = "sha256:3e81d89ece99b9ace23a6916880baca613c03a799925afb2857887efa8b1b3d2"}, ] +[package.dependencies] +typing-extensions = ">=4.12,<5.0" + [package.extras] -protobuf = ["grpcio-tools (>=1.73.0)"] +protobuf = ["grpcio-tools (>=1.75.1)"] [[package]] name = "grpcio-status" -version = "1.73.0" +version = "1.75.1" description = "Status proto mapping for gRPC" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "grpcio_status-1.73.0-py3-none-any.whl", hash = "sha256:a3f3a9994b44c364f014e806114ba44cc52e50c426779f958c8b22f14ff0d892"}, - {file = "grpcio_status-1.73.0.tar.gz", hash = "sha256:a2b7f430568217f884fe52a5a0133b6f4c9338beae33fb5370134a8eaf58f974"}, + {file = "grpcio_status-1.75.1-py3-none-any.whl", hash = "sha256:f681b301be26dcf7abf5c765d4a22e4098765e1a65cbdfa3efca384edf8e4e3c"}, + {file = "grpcio_status-1.75.1.tar.gz", hash = "sha256:8162afa21833a2085c91089cc395ad880fac1378a1d60233d976649ed724cbf8"}, ] [package.dependencies] googleapis-common-protos = ">=1.5.5" -grpcio = ">=1.73.0" -protobuf = ">=6.30.0,<7.0.0" +grpcio = ">=1.75.1" +protobuf = ">=6.31.1,<7.0.0" [[package]] name = "idna" @@ -969,6 +1116,18 @@ pyreadline3 = {version = "*", markers = "sys_platform == \"win32\""} setuptools = "*" six = "*" +[[package]] +name = "iniconfig" +version = "2.1.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.8" +groups = ["main", "dev"] +files = [ + {file = "iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760"}, + {file = "iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"}, +] + [[package]] name = "itsdangerous" version = "2.2.0" @@ -1047,85 +1206,113 @@ pyyaml = "*" [[package]] name = "markupsafe" -version = "3.0.2" +version = "3.0.3" description = "Safely add untrusted strings to HTML/XML markup." optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-win32.whl", hash = "sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a"}, - {file = "markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0"}, + {file = "markupsafe-3.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559"}, + {file = "markupsafe-3.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419"}, + {file = "markupsafe-3.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695"}, + {file = "markupsafe-3.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591"}, + {file = "markupsafe-3.0.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c"}, + {file = "markupsafe-3.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f"}, + {file = "markupsafe-3.0.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6"}, + {file = "markupsafe-3.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1"}, + {file = "markupsafe-3.0.3-cp310-cp310-win32.whl", hash = "sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa"}, + {file = "markupsafe-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8"}, + {file = "markupsafe-3.0.3-cp310-cp310-win_arm64.whl", hash = "sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1"}, + {file = "markupsafe-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad"}, + {file = "markupsafe-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a"}, + {file = "markupsafe-3.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50"}, + {file = "markupsafe-3.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf"}, + {file = "markupsafe-3.0.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f"}, + {file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a"}, + {file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115"}, + {file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a"}, + {file = "markupsafe-3.0.3-cp311-cp311-win32.whl", hash = "sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19"}, + {file = "markupsafe-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01"}, + {file = "markupsafe-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c"}, + {file = "markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e"}, + {file = "markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce"}, + {file = "markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d"}, + {file = "markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d"}, + {file = "markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a"}, + {file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b"}, + {file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f"}, + {file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b"}, + {file = "markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d"}, + {file = "markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c"}, + {file = "markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f"}, + {file = "markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795"}, + {file = "markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219"}, + {file = "markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6"}, + {file = "markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676"}, + {file = "markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9"}, + {file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1"}, + {file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc"}, + {file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12"}, + {file = "markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed"}, + {file = "markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5"}, + {file = "markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485"}, + {file = "markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73"}, + {file = "markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37"}, + {file = "markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19"}, + {file = "markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025"}, + {file = "markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6"}, + {file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f"}, + {file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb"}, + {file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009"}, + {file = "markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354"}, + {file = "markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218"}, + {file = "markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287"}, + {file = "markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe"}, + {file = "markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026"}, + {file = "markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737"}, + {file = "markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97"}, + {file = "markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d"}, + {file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda"}, + {file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf"}, + {file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe"}, + {file = "markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9"}, + {file = "markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581"}, + {file = "markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4"}, + {file = "markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab"}, + {file = "markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175"}, + {file = "markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634"}, + {file = "markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50"}, + {file = "markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e"}, + {file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5"}, + {file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523"}, + {file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc"}, + {file = "markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d"}, + {file = "markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9"}, + {file = "markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa"}, + {file = "markupsafe-3.0.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:15d939a21d546304880945ca1ecb8a039db6b4dc49b2c5a400387cdae6a62e26"}, + {file = "markupsafe-3.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f71a396b3bf33ecaa1626c255855702aca4d3d9fea5e051b41ac59a9c1c41edc"}, + {file = "markupsafe-3.0.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f4b68347f8c5eab4a13419215bdfd7f8c9b19f2b25520968adfad23eb0ce60c"}, + {file = "markupsafe-3.0.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e8fc20152abba6b83724d7ff268c249fa196d8259ff481f3b1476383f8f24e42"}, + {file = "markupsafe-3.0.3-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:949b8d66bc381ee8b007cd945914c721d9aba8e27f71959d750a46f7c282b20b"}, + {file = "markupsafe-3.0.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:3537e01efc9d4dccdf77221fb1cb3b8e1a38d5428920e0657ce299b20324d758"}, + {file = "markupsafe-3.0.3-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:591ae9f2a647529ca990bc681daebdd52c8791ff06c2bfa05b65163e28102ef2"}, + {file = "markupsafe-3.0.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a320721ab5a1aba0a233739394eb907f8c8da5c98c9181d1161e77a0c8e36f2d"}, + {file = "markupsafe-3.0.3-cp39-cp39-win32.whl", hash = "sha256:df2449253ef108a379b8b5d6b43f4b1a8e81a061d6537becd5582fba5f9196d7"}, + {file = "markupsafe-3.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:7c3fb7d25180895632e5d3148dbdc29ea38ccb7fd210aa27acbd1201a1902c6e"}, + {file = "markupsafe-3.0.3-cp39-cp39-win_arm64.whl", hash = "sha256:38664109c14ffc9e7437e86b4dceb442b0096dfe3541d7864d9cbe1da4cf36c8"}, + {file = "markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698"}, ] [[package]] name = "minio" -version = "7.2.15" +version = "7.2.18" description = "MinIO Python SDK for Amazon S3 Compatible Cloud Storage" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "minio-7.2.15-py3-none-any.whl", hash = "sha256:c06ef7a43e5d67107067f77b6c07ebdd68733e5aa7eed03076472410ca19d876"}, - {file = "minio-7.2.15.tar.gz", hash = "sha256:5247df5d4dca7bfa4c9b20093acd5ad43e82d8710ceb059d79c6eea970f49f79"}, + {file = "minio-7.2.18-py3-none-any.whl", hash = "sha256:f23a6edbff8d0bc4b5c1a61b2628a01c5a3342aefc613ff9c276012e6321108f"}, + {file = "minio-7.2.18.tar.gz", hash = "sha256:173402a5716099159c5659f9de75be204ebe248557b9f1cc9cf45aa70e9d3024"}, ] [package.dependencies] @@ -1137,116 +1324,193 @@ urllib3 = "*" [[package]] name = "multidict" -version = "6.4.4" +version = "6.7.0" description = "multidict implementation" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "multidict-6.4.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8adee3ac041145ffe4488ea73fa0a622b464cc25340d98be76924d0cda8545ff"}, - {file = "multidict-6.4.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b61e98c3e2a861035aaccd207da585bdcacef65fe01d7a0d07478efac005e028"}, - {file = "multidict-6.4.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:75493f28dbadecdbb59130e74fe935288813301a8554dc32f0c631b6bdcdf8b0"}, - {file = "multidict-6.4.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ffc3c6a37e048b5395ee235e4a2a0d639c2349dffa32d9367a42fc20d399772"}, - {file = "multidict-6.4.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:87cb72263946b301570b0f63855569a24ee8758aaae2cd182aae7d95fbc92ca7"}, - {file = "multidict-6.4.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9bbf7bd39822fd07e3609b6b4467af4c404dd2b88ee314837ad1830a7f4a8299"}, - {file = "multidict-6.4.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d1f7cbd4f1f44ddf5fd86a8675b7679176eae770f2fc88115d6dddb6cefb59bc"}, - {file = "multidict-6.4.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb5ac9e5bfce0e6282e7f59ff7b7b9a74aa8e5c60d38186a4637f5aa764046ad"}, - {file = "multidict-6.4.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4efc31dfef8c4eeb95b6b17d799eedad88c4902daba39ce637e23a17ea078915"}, - {file = "multidict-6.4.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:9fcad2945b1b91c29ef2b4050f590bfcb68d8ac8e0995a74e659aa57e8d78e01"}, - {file = "multidict-6.4.4-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:d877447e7368c7320832acb7159557e49b21ea10ffeb135c1077dbbc0816b598"}, - {file = "multidict-6.4.4-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:33a12ebac9f380714c298cbfd3e5b9c0c4e89c75fe612ae496512ee51028915f"}, - {file = "multidict-6.4.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:0f14ea68d29b43a9bf37953881b1e3eb75b2739e896ba4a6aa4ad4c5b9ffa145"}, - {file = "multidict-6.4.4-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:0327ad2c747a6600e4797d115d3c38a220fdb28e54983abe8964fd17e95ae83c"}, - {file = "multidict-6.4.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d1a20707492db9719a05fc62ee215fd2c29b22b47c1b1ba347f9abc831e26683"}, - {file = "multidict-6.4.4-cp310-cp310-win32.whl", hash = "sha256:d83f18315b9fca5db2452d1881ef20f79593c4aa824095b62cb280019ef7aa3d"}, - {file = "multidict-6.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:9c17341ee04545fd962ae07330cb5a39977294c883485c8d74634669b1f7fe04"}, - {file = "multidict-6.4.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4f5f29794ac0e73d2a06ac03fd18870adc0135a9d384f4a306a951188ed02f95"}, - {file = "multidict-6.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c04157266344158ebd57b7120d9b0b35812285d26d0e78193e17ef57bfe2979a"}, - {file = "multidict-6.4.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bb61ffd3ab8310d93427e460f565322c44ef12769f51f77277b4abad7b6f7223"}, - {file = "multidict-6.4.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e0ba18a9afd495f17c351d08ebbc4284e9c9f7971d715f196b79636a4d0de44"}, - {file = "multidict-6.4.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:9faf1b1dcaadf9f900d23a0e6d6c8eadd6a95795a0e57fcca73acce0eb912065"}, - {file = "multidict-6.4.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a4d1cb1327c6082c4fce4e2a438483390964c02213bc6b8d782cf782c9b1471f"}, - {file = "multidict-6.4.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:941f1bec2f5dbd51feeb40aea654c2747f811ab01bdd3422a48a4e4576b7d76a"}, - {file = "multidict-6.4.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e5f8a146184da7ea12910a4cec51ef85e44f6268467fb489c3caf0cd512f29c2"}, - {file = "multidict-6.4.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:232b7237e57ec3c09be97206bfb83a0aa1c5d7d377faa019c68a210fa35831f1"}, - {file = "multidict-6.4.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:55ae0721c1513e5e3210bca4fc98456b980b0c2c016679d3d723119b6b202c42"}, - {file = "multidict-6.4.4-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:51d662c072579f63137919d7bb8fc250655ce79f00c82ecf11cab678f335062e"}, - {file = "multidict-6.4.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:0e05c39962baa0bb19a6b210e9b1422c35c093b651d64246b6c2e1a7e242d9fd"}, - {file = "multidict-6.4.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:d5b1cc3ab8c31d9ebf0faa6e3540fb91257590da330ffe6d2393d4208e638925"}, - {file = "multidict-6.4.4-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:93ec84488a384cd7b8a29c2c7f467137d8a73f6fe38bb810ecf29d1ade011a7c"}, - {file = "multidict-6.4.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b308402608493638763abc95f9dc0030bbd6ac6aff784512e8ac3da73a88af08"}, - {file = "multidict-6.4.4-cp311-cp311-win32.whl", hash = "sha256:343892a27d1a04d6ae455ecece12904d242d299ada01633d94c4f431d68a8c49"}, - {file = "multidict-6.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:73484a94f55359780c0f458bbd3c39cb9cf9c182552177d2136e828269dee529"}, - {file = "multidict-6.4.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:dc388f75a1c00000824bf28b7633e40854f4127ede80512b44c3cfeeea1839a2"}, - {file = "multidict-6.4.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:98af87593a666f739d9dba5d0ae86e01b0e1a9cfcd2e30d2d361fbbbd1a9162d"}, - {file = "multidict-6.4.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:aff4cafea2d120327d55eadd6b7f1136a8e5a0ecf6fb3b6863e8aca32cd8e50a"}, - {file = "multidict-6.4.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:169c4ba7858176b797fe551d6e99040c531c775d2d57b31bcf4de6d7a669847f"}, - {file = "multidict-6.4.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b9eb4c59c54421a32b3273d4239865cb14ead53a606db066d7130ac80cc8ec93"}, - {file = "multidict-6.4.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7cf3bd54c56aa16fdb40028d545eaa8d051402b61533c21e84046e05513d5780"}, - {file = "multidict-6.4.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f682c42003c7264134bfe886376299db4cc0c6cd06a3295b41b347044bcb5482"}, - {file = "multidict-6.4.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a920f9cf2abdf6e493c519492d892c362007f113c94da4c239ae88429835bad1"}, - {file = "multidict-6.4.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:530d86827a2df6504526106b4c104ba19044594f8722d3e87714e847c74a0275"}, - {file = "multidict-6.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ecde56ea2439b96ed8a8d826b50c57364612ddac0438c39e473fafad7ae1c23b"}, - {file = "multidict-6.4.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:dc8c9736d8574b560634775ac0def6bdc1661fc63fa27ffdfc7264c565bcb4f2"}, - {file = "multidict-6.4.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:7f3d3b3c34867579ea47cbd6c1f2ce23fbfd20a273b6f9e3177e256584f1eacc"}, - {file = "multidict-6.4.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:87a728af265e08f96b6318ebe3c0f68b9335131f461efab2fc64cc84a44aa6ed"}, - {file = "multidict-6.4.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9f193eeda1857f8e8d3079a4abd258f42ef4a4bc87388452ed1e1c4d2b0c8740"}, - {file = "multidict-6.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:be06e73c06415199200e9a2324a11252a3d62030319919cde5e6950ffeccf72e"}, - {file = "multidict-6.4.4-cp312-cp312-win32.whl", hash = "sha256:622f26ea6a7e19b7c48dd9228071f571b2fbbd57a8cd71c061e848f281550e6b"}, - {file = "multidict-6.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:5e2bcda30d5009996ff439e02a9f2b5c3d64a20151d34898c000a6281faa3781"}, - {file = "multidict-6.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:82ffabefc8d84c2742ad19c37f02cde5ec2a1ee172d19944d380f920a340e4b9"}, - {file = "multidict-6.4.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:6a2f58a66fe2c22615ad26156354005391e26a2f3721c3621504cd87c1ea87bf"}, - {file = "multidict-6.4.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5883d6ee0fd9d8a48e9174df47540b7545909841ac82354c7ae4cbe9952603bd"}, - {file = "multidict-6.4.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9abcf56a9511653fa1d052bfc55fbe53dbee8f34e68bd6a5a038731b0ca42d15"}, - {file = "multidict-6.4.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6ed5ae5605d4ad5a049fad2a28bb7193400700ce2f4ae484ab702d1e3749c3f9"}, - {file = "multidict-6.4.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bbfcb60396f9bcfa63e017a180c3105b8c123a63e9d1428a36544e7d37ca9e20"}, - {file = "multidict-6.4.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b0f1987787f5f1e2076b59692352ab29a955b09ccc433c1f6b8e8e18666f608b"}, - {file = "multidict-6.4.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1d0121ccce8c812047d8d43d691a1ad7641f72c4f730474878a5aeae1b8ead8c"}, - {file = "multidict-6.4.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:83ec4967114295b8afd120a8eec579920c882831a3e4c3331d591a8e5bfbbc0f"}, - {file = "multidict-6.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:995f985e2e268deaf17867801b859a282e0448633f1310e3704b30616d269d69"}, - {file = "multidict-6.4.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:d832c608f94b9f92a0ec8b7e949be7792a642b6e535fcf32f3e28fab69eeb046"}, - {file = "multidict-6.4.4-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d21c1212171cf7da703c5b0b7a0e85be23b720818aef502ad187d627316d5645"}, - {file = "multidict-6.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:cbebaa076aaecad3d4bb4c008ecc73b09274c952cf6a1b78ccfd689e51f5a5b0"}, - {file = "multidict-6.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:c93a6fb06cc8e5d3628b2b5fda215a5db01e8f08fc15fadd65662d9b857acbe4"}, - {file = "multidict-6.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8cd8f81f1310182362fb0c7898145ea9c9b08a71081c5963b40ee3e3cac589b1"}, - {file = "multidict-6.4.4-cp313-cp313-win32.whl", hash = "sha256:3e9f1cd61a0ab857154205fb0b1f3d3ace88d27ebd1409ab7af5096e409614cd"}, - {file = "multidict-6.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:8ffb40b74400e4455785c2fa37eba434269149ec525fc8329858c862e4b35373"}, - {file = "multidict-6.4.4-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:6a602151dbf177be2450ef38966f4be3467d41a86c6a845070d12e17c858a156"}, - {file = "multidict-6.4.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0d2b9712211b860d123815a80b859075d86a4d54787e247d7fbee9db6832cf1c"}, - {file = "multidict-6.4.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:d2fa86af59f8fc1972e121ade052145f6da22758f6996a197d69bb52f8204e7e"}, - {file = "multidict-6.4.4-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50855d03e9e4d66eab6947ba688ffb714616f985838077bc4b490e769e48da51"}, - {file = "multidict-6.4.4-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:5bce06b83be23225be1905dcdb6b789064fae92499fbc458f59a8c0e68718601"}, - {file = "multidict-6.4.4-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:66ed0731f8e5dfd8369a883b6e564aca085fb9289aacabd9decd70568b9a30de"}, - {file = "multidict-6.4.4-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:329ae97fc2f56f44d91bc47fe0972b1f52d21c4b7a2ac97040da02577e2daca2"}, - {file = "multidict-6.4.4-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c27e5dcf520923d6474d98b96749e6805f7677e93aaaf62656005b8643f907ab"}, - {file = "multidict-6.4.4-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:058cc59b9e9b143cc56715e59e22941a5d868c322242278d28123a5d09cdf6b0"}, - {file = "multidict-6.4.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:69133376bc9a03f8c47343d33f91f74a99c339e8b58cea90433d8e24bb298031"}, - {file = "multidict-6.4.4-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:d6b15c55721b1b115c5ba178c77104123745b1417527ad9641a4c5e2047450f0"}, - {file = "multidict-6.4.4-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:a887b77f51d3d41e6e1a63cf3bc7ddf24de5939d9ff69441387dfefa58ac2e26"}, - {file = "multidict-6.4.4-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:632a3bf8f1787f7ef7d3c2f68a7bde5be2f702906f8b5842ad6da9d974d0aab3"}, - {file = "multidict-6.4.4-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:a145c550900deb7540973c5cdb183b0d24bed6b80bf7bddf33ed8f569082535e"}, - {file = "multidict-6.4.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:cc5d83c6619ca5c9672cb78b39ed8542f1975a803dee2cda114ff73cbb076edd"}, - {file = "multidict-6.4.4-cp313-cp313t-win32.whl", hash = "sha256:3312f63261b9df49be9d57aaa6abf53a6ad96d93b24f9cc16cf979956355ce6e"}, - {file = "multidict-6.4.4-cp313-cp313t-win_amd64.whl", hash = "sha256:ba852168d814b2c73333073e1c7116d9395bea69575a01b0b3c89d2d5a87c8fb"}, - {file = "multidict-6.4.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:603f39bd1cf85705c6c1ba59644b480dfe495e6ee2b877908de93322705ad7cf"}, - {file = "multidict-6.4.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fc60f91c02e11dfbe3ff4e1219c085695c339af72d1641800fe6075b91850c8f"}, - {file = "multidict-6.4.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:496bcf01c76a70a31c3d746fd39383aad8d685ce6331e4c709e9af4ced5fa221"}, - {file = "multidict-6.4.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4219390fb5bf8e548e77b428bb36a21d9382960db5321b74d9d9987148074d6b"}, - {file = "multidict-6.4.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3ef4e9096ff86dfdcbd4a78253090ba13b1d183daa11b973e842465d94ae1772"}, - {file = "multidict-6.4.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:49a29d7133b1fc214e818bbe025a77cc6025ed9a4f407d2850373ddde07fd04a"}, - {file = "multidict-6.4.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e32053d6d3a8b0dfe49fde05b496731a0e6099a4df92154641c00aa76786aef5"}, - {file = "multidict-6.4.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cc403092a49509e8ef2d2fd636a8ecefc4698cc57bbe894606b14579bc2a955"}, - {file = "multidict-6.4.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5363f9b2a7f3910e5c87d8b1855c478c05a2dc559ac57308117424dfaad6805c"}, - {file = "multidict-6.4.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2e543a40e4946cf70a88a3be87837a3ae0aebd9058ba49e91cacb0b2cd631e2b"}, - {file = "multidict-6.4.4-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:60d849912350da557fe7de20aa8cf394aada6980d0052cc829eeda4a0db1c1db"}, - {file = "multidict-6.4.4-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:19d08b4f22eae45bb018b9f06e2838c1e4b853c67628ef8ae126d99de0da6395"}, - {file = "multidict-6.4.4-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:d693307856d1ef08041e8b6ff01d5b4618715007d288490ce2c7e29013c12b9a"}, - {file = "multidict-6.4.4-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:fad6daaed41021934917f4fb03ca2db8d8a4d79bf89b17ebe77228eb6710c003"}, - {file = "multidict-6.4.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c10d17371bff801af0daf8b073c30b6cf14215784dc08cd5c43ab5b7b8029bbc"}, - {file = "multidict-6.4.4-cp39-cp39-win32.whl", hash = "sha256:7e23f2f841fcb3ebd4724a40032d32e0892fbba4143e43d2a9e7695c5e50e6bd"}, - {file = "multidict-6.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:4d7b50b673ffb4ff4366e7ab43cf1f0aef4bd3608735c5fbdf0bdb6f690da411"}, - {file = "multidict-6.4.4-py3-none-any.whl", hash = "sha256:bd4557071b561a8b3b6075c3ce93cf9bfb6182cb241805c3d66ced3b75eff4ac"}, - {file = "multidict-6.4.4.tar.gz", hash = "sha256:69ee9e6ba214b5245031b76233dd95408a0fd57fdb019ddcc1ead4790932a8e8"}, + {file = "multidict-6.7.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:9f474ad5acda359c8758c8accc22032c6abe6dc87a8be2440d097785e27a9349"}, + {file = "multidict-6.7.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4b7a9db5a870f780220e931d0002bbfd88fb53aceb6293251e2c839415c1b20e"}, + {file = "multidict-6.7.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:03ca744319864e92721195fa28c7a3b2bc7b686246b35e4078c1e4d0eb5466d3"}, + {file = "multidict-6.7.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f0e77e3c0008bc9316e662624535b88d360c3a5d3f81e15cf12c139a75250046"}, + {file = "multidict-6.7.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:08325c9e5367aa379a3496aa9a022fe8837ff22e00b94db256d3a1378c76ab32"}, + {file = "multidict-6.7.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e2862408c99f84aa571ab462d25236ef9cb12a602ea959ba9c9009a54902fc73"}, + {file = "multidict-6.7.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4d72a9a2d885f5c208b0cb91ff2ed43636bb7e345ec839ff64708e04f69a13cc"}, + {file = "multidict-6.7.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:478cc36476687bac1514d651cbbaa94b86b0732fb6855c60c673794c7dd2da62"}, + {file = "multidict-6.7.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6843b28b0364dc605f21481c90fadb5f60d9123b442eb8a726bb74feef588a84"}, + {file = "multidict-6.7.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:23bfeee5316266e5ee2d625df2d2c602b829435fc3a235c2ba2131495706e4a0"}, + {file = "multidict-6.7.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:680878b9f3d45c31e1f730eef731f9b0bc1da456155688c6745ee84eb818e90e"}, + {file = "multidict-6.7.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:eb866162ef2f45063acc7a53a88ef6fe8bf121d45c30ea3c9cd87ce7e191a8d4"}, + {file = "multidict-6.7.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:df0e3bf7993bdbeca5ac25aa859cf40d39019e015c9c91809ba7093967f7a648"}, + {file = "multidict-6.7.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:661709cdcd919a2ece2234f9bae7174e5220c80b034585d7d8a755632d3e2111"}, + {file = "multidict-6.7.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:096f52730c3fb8ed419db2d44391932b63891b2c5ed14850a7e215c0ba9ade36"}, + {file = "multidict-6.7.0-cp310-cp310-win32.whl", hash = "sha256:afa8a2978ec65d2336305550535c9c4ff50ee527914328c8677b3973ade52b85"}, + {file = "multidict-6.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:b15b3afff74f707b9275d5ba6a91ae8f6429c3ffb29bbfd216b0b375a56f13d7"}, + {file = "multidict-6.7.0-cp310-cp310-win_arm64.whl", hash = "sha256:4b73189894398d59131a66ff157837b1fafea9974be486d036bb3d32331fdbf0"}, + {file = "multidict-6.7.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4d409aa42a94c0b3fa617708ef5276dfe81012ba6753a0370fcc9d0195d0a1fc"}, + {file = "multidict-6.7.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:14c9e076eede3b54c636f8ce1c9c252b5f057c62131211f0ceeec273810c9721"}, + {file = "multidict-6.7.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4c09703000a9d0fa3c3404b27041e574cc7f4df4c6563873246d0e11812a94b6"}, + {file = "multidict-6.7.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:a265acbb7bb33a3a2d626afbe756371dce0279e7b17f4f4eda406459c2b5ff1c"}, + {file = "multidict-6.7.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:51cb455de290ae462593e5b1cb1118c5c22ea7f0d3620d9940bf695cea5a4bd7"}, + {file = "multidict-6.7.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:db99677b4457c7a5c5a949353e125ba72d62b35f74e26da141530fbb012218a7"}, + {file = "multidict-6.7.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f470f68adc395e0183b92a2f4689264d1ea4b40504a24d9882c27375e6662bb9"}, + {file = "multidict-6.7.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0db4956f82723cc1c270de9c6e799b4c341d327762ec78ef82bb962f79cc07d8"}, + {file = "multidict-6.7.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3e56d780c238f9e1ae66a22d2adf8d16f485381878250db8d496623cd38b22bd"}, + {file = "multidict-6.7.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9d14baca2ee12c1a64740d4531356ba50b82543017f3ad6de0deb943c5979abb"}, + {file = "multidict-6.7.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:295a92a76188917c7f99cda95858c822f9e4aae5824246bba9b6b44004ddd0a6"}, + {file = "multidict-6.7.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:39f1719f57adbb767ef592a50ae5ebb794220d1188f9ca93de471336401c34d2"}, + {file = "multidict-6.7.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:0a13fb8e748dfc94749f622de065dd5c1def7e0d2216dba72b1d8069a389c6ff"}, + {file = "multidict-6.7.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e3aa16de190d29a0ea1b48253c57d99a68492c8dd8948638073ab9e74dc9410b"}, + {file = "multidict-6.7.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a048ce45dcdaaf1defb76b2e684f997fb5abf74437b6cb7b22ddad934a964e34"}, + {file = "multidict-6.7.0-cp311-cp311-win32.whl", hash = "sha256:a90af66facec4cebe4181b9e62a68be65e45ac9b52b67de9eec118701856e7ff"}, + {file = "multidict-6.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:95b5ffa4349df2887518bb839409bcf22caa72d82beec453216802f475b23c81"}, + {file = "multidict-6.7.0-cp311-cp311-win_arm64.whl", hash = "sha256:329aa225b085b6f004a4955271a7ba9f1087e39dcb7e65f6284a988264a63912"}, + {file = "multidict-6.7.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8a3862568a36d26e650a19bb5cbbba14b71789032aebc0423f8cc5f150730184"}, + {file = "multidict-6.7.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:960c60b5849b9b4f9dcc9bea6e3626143c252c74113df2c1540aebce70209b45"}, + {file = "multidict-6.7.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2049be98fb57a31b4ccf870bf377af2504d4ae35646a19037ec271e4c07998aa"}, + {file = "multidict-6.7.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0934f3843a1860dd465d38895c17fce1f1cb37295149ab05cd1b9a03afacb2a7"}, + {file = "multidict-6.7.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b3e34f3a1b8131ba06f1a73adab24f30934d148afcd5f5de9a73565a4404384e"}, + {file = "multidict-6.7.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:efbb54e98446892590dc2458c19c10344ee9a883a79b5cec4bc34d6656e8d546"}, + {file = "multidict-6.7.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a35c5fc61d4f51eb045061e7967cfe3123d622cd500e8868e7c0c592a09fedc4"}, + {file = "multidict-6.7.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29fe6740ebccba4175af1b9b87bf553e9c15cd5868ee967e010efcf94e4fd0f1"}, + {file = "multidict-6.7.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:123e2a72e20537add2f33a79e605f6191fba2afda4cbb876e35c1a7074298a7d"}, + {file = "multidict-6.7.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b284e319754366c1aee2267a2036248b24eeb17ecd5dc16022095e747f2f4304"}, + {file = "multidict-6.7.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:803d685de7be4303b5a657b76e2f6d1240e7e0a8aa2968ad5811fa2285553a12"}, + {file = "multidict-6.7.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c04a328260dfd5db8c39538f999f02779012268f54614902d0afc775d44e0a62"}, + {file = "multidict-6.7.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8a19cdb57cd3df4cd865849d93ee14920fb97224300c88501f16ecfa2604b4e0"}, + {file = "multidict-6.7.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9b2fd74c52accced7e75de26023b7dccee62511a600e62311b918ec5c168fc2a"}, + {file = "multidict-6.7.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3e8bfdd0e487acf992407a140d2589fe598238eaeffa3da8448d63a63cd363f8"}, + {file = "multidict-6.7.0-cp312-cp312-win32.whl", hash = "sha256:dd32a49400a2c3d52088e120ee00c1e3576cbff7e10b98467962c74fdb762ed4"}, + {file = "multidict-6.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:92abb658ef2d7ef22ac9f8bb88e8b6c3e571671534e029359b6d9e845923eb1b"}, + {file = "multidict-6.7.0-cp312-cp312-win_arm64.whl", hash = "sha256:490dab541a6a642ce1a9d61a4781656b346a55c13038f0b1244653828e3a83ec"}, + {file = "multidict-6.7.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:bee7c0588aa0076ce77c0ea5d19a68d76ad81fcd9fe8501003b9a24f9d4000f6"}, + {file = "multidict-6.7.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7ef6b61cad77091056ce0e7ce69814ef72afacb150b7ac6a3e9470def2198159"}, + {file = "multidict-6.7.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9c0359b1ec12b1d6849c59f9d319610b7f20ef990a6d454ab151aa0e3b9f78ca"}, + {file = "multidict-6.7.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cd240939f71c64bd658f186330603aac1a9a81bf6273f523fca63673cb7378a8"}, + {file = "multidict-6.7.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a60a4d75718a5efa473ebd5ab685786ba0c67b8381f781d1be14da49f1a2dc60"}, + {file = "multidict-6.7.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:53a42d364f323275126aff81fb67c5ca1b7a04fda0546245730a55c8c5f24bc4"}, + {file = "multidict-6.7.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3b29b980d0ddbecb736735ee5bef69bb2ddca56eff603c86f3f29a1128299b4f"}, + {file = "multidict-6.7.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f8a93b1c0ed2d04b97a5e9336fd2d33371b9a6e29ab7dd6503d63407c20ffbaf"}, + {file = "multidict-6.7.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9ff96e8815eecacc6645da76c413eb3b3d34cfca256c70b16b286a687d013c32"}, + {file = "multidict-6.7.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7516c579652f6a6be0e266aec0acd0db80829ca305c3d771ed898538804c2036"}, + {file = "multidict-6.7.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:040f393368e63fb0f3330e70c26bfd336656bed925e5cbe17c9da839a6ab13ec"}, + {file = "multidict-6.7.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b3bc26a951007b1057a1c543af845f1c7e3e71cc240ed1ace7bf4484aa99196e"}, + {file = "multidict-6.7.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:7b022717c748dd1992a83e219587aabe45980d88969f01b316e78683e6285f64"}, + {file = "multidict-6.7.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:9600082733859f00d79dee64effc7aef1beb26adb297416a4ad2116fd61374bd"}, + {file = "multidict-6.7.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:94218fcec4d72bc61df51c198d098ce2b378e0ccbac41ddbed5ef44092913288"}, + {file = "multidict-6.7.0-cp313-cp313-win32.whl", hash = "sha256:a37bd74c3fa9d00be2d7b8eca074dc56bd8077ddd2917a839bd989612671ed17"}, + {file = "multidict-6.7.0-cp313-cp313-win_amd64.whl", hash = "sha256:30d193c6cc6d559db42b6bcec8a5d395d34d60c9877a0b71ecd7c204fcf15390"}, + {file = "multidict-6.7.0-cp313-cp313-win_arm64.whl", hash = "sha256:ea3334cabe4d41b7ccd01e4d349828678794edbc2d3ae97fc162a3312095092e"}, + {file = "multidict-6.7.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:ad9ce259f50abd98a1ca0aa6e490b58c316a0fce0617f609723e40804add2c00"}, + {file = "multidict-6.7.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:07f5594ac6d084cbb5de2df218d78baf55ef150b91f0ff8a21cc7a2e3a5a58eb"}, + {file = "multidict-6.7.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:0591b48acf279821a579282444814a2d8d0af624ae0bc600aa4d1b920b6e924b"}, + {file = "multidict-6.7.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:749a72584761531d2b9467cfbdfd29487ee21124c304c4b6cb760d8777b27f9c"}, + {file = "multidict-6.7.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b4c3d199f953acd5b446bf7c0de1fe25d94e09e79086f8dc2f48a11a129cdf1"}, + {file = "multidict-6.7.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:9fb0211dfc3b51efea2f349ec92c114d7754dd62c01f81c3e32b765b70c45c9b"}, + {file = "multidict-6.7.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a027ec240fe73a8d6281872690b988eed307cd7d91b23998ff35ff577ca688b5"}, + {file = "multidict-6.7.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1d964afecdf3a8288789df2f5751dc0a8261138c3768d9af117ed384e538fad"}, + {file = "multidict-6.7.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:caf53b15b1b7df9fbd0709aa01409000a2b4dd03a5f6f5cc548183c7c8f8b63c"}, + {file = "multidict-6.7.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:654030da3197d927f05a536a66186070e98765aa5142794c9904555d3a9d8fb5"}, + {file = "multidict-6.7.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:2090d3718829d1e484706a2f525e50c892237b2bf9b17a79b059cb98cddc2f10"}, + {file = "multidict-6.7.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:2d2cfeec3f6f45651b3d408c4acec0ebf3daa9bc8a112a084206f5db5d05b754"}, + {file = "multidict-6.7.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:4ef089f985b8c194d341eb2c24ae6e7408c9a0e2e5658699c92f497437d88c3c"}, + {file = "multidict-6.7.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e93a0617cd16998784bf4414c7e40f17a35d2350e5c6f0bd900d3a8e02bd3762"}, + {file = "multidict-6.7.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f0feece2ef8ebc42ed9e2e8c78fc4aa3cf455733b507c09ef7406364c94376c6"}, + {file = "multidict-6.7.0-cp313-cp313t-win32.whl", hash = "sha256:19a1d55338ec1be74ef62440ca9e04a2f001a04d0cc49a4983dc320ff0f3212d"}, + {file = "multidict-6.7.0-cp313-cp313t-win_amd64.whl", hash = "sha256:3da4fb467498df97e986af166b12d01f05d2e04f978a9c1c680ea1988e0bc4b6"}, + {file = "multidict-6.7.0-cp313-cp313t-win_arm64.whl", hash = "sha256:b4121773c49a0776461f4a904cdf6264c88e42218aaa8407e803ca8025872792"}, + {file = "multidict-6.7.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3bab1e4aff7adaa34410f93b1f8e57c4b36b9af0426a76003f441ee1d3c7e842"}, + {file = "multidict-6.7.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:b8512bac933afc3e45fb2b18da8e59b78d4f408399a960339598374d4ae3b56b"}, + {file = "multidict-6.7.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:79dcf9e477bc65414ebfea98ffd013cb39552b5ecd62908752e0e413d6d06e38"}, + {file = "multidict-6.7.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:31bae522710064b5cbeddaf2e9f32b1abab70ac6ac91d42572502299e9953128"}, + {file = "multidict-6.7.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a0df7ff02397bb63e2fd22af2c87dfa39e8c7f12947bc524dbdc528282c7e34"}, + {file = "multidict-6.7.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7a0222514e8e4c514660e182d5156a415c13ef0aabbd71682fc714e327b95e99"}, + {file = "multidict-6.7.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2397ab4daaf2698eb51a76721e98db21ce4f52339e535725de03ea962b5a3202"}, + {file = "multidict-6.7.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8891681594162635948a636c9fe0ff21746aeb3dd5463f6e25d9bea3a8a39ca1"}, + {file = "multidict-6.7.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:18706cc31dbf402a7945916dd5cddf160251b6dab8a2c5f3d6d5a55949f676b3"}, + {file = "multidict-6.7.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f844a1bbf1d207dd311a56f383f7eda2d0e134921d45751842d8235e7778965d"}, + {file = "multidict-6.7.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:d4393e3581e84e5645506923816b9cc81f5609a778c7e7534054091acc64d1c6"}, + {file = "multidict-6.7.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:fbd18dc82d7bf274b37aa48d664534330af744e03bccf696d6f4c6042e7d19e7"}, + {file = "multidict-6.7.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:b6234e14f9314731ec45c42fc4554b88133ad53a09092cc48a88e771c125dadb"}, + {file = "multidict-6.7.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:08d4379f9744d8f78d98c8673c06e202ffa88296f009c71bbafe8a6bf847d01f"}, + {file = "multidict-6.7.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:9fe04da3f79387f450fd0061d4dd2e45a72749d31bf634aecc9e27f24fdc4b3f"}, + {file = "multidict-6.7.0-cp314-cp314-win32.whl", hash = "sha256:fbafe31d191dfa7c4c51f7a6149c9fb7e914dcf9ffead27dcfd9f1ae382b3885"}, + {file = "multidict-6.7.0-cp314-cp314-win_amd64.whl", hash = "sha256:2f67396ec0310764b9222a1728ced1ab638f61aadc6226f17a71dd9324f9a99c"}, + {file = "multidict-6.7.0-cp314-cp314-win_arm64.whl", hash = "sha256:ba672b26069957ee369cfa7fc180dde1fc6f176eaf1e6beaf61fbebbd3d9c000"}, + {file = "multidict-6.7.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:c1dcc7524066fa918c6a27d61444d4ee7900ec635779058571f70d042d86ed63"}, + {file = "multidict-6.7.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:27e0b36c2d388dc7b6ced3406671b401e84ad7eb0656b8f3a2f46ed0ce483718"}, + {file = "multidict-6.7.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2a7baa46a22e77f0988e3b23d4ede5513ebec1929e34ee9495be535662c0dfe2"}, + {file = "multidict-6.7.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7bf77f54997a9166a2f5675d1201520586439424c2511723a7312bdb4bcc034e"}, + {file = "multidict-6.7.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e011555abada53f1578d63389610ac8a5400fc70ce71156b0aa30d326f1a5064"}, + {file = "multidict-6.7.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:28b37063541b897fd6a318007373930a75ca6d6ac7c940dbe14731ffdd8d498e"}, + {file = "multidict-6.7.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:05047ada7a2fde2631a0ed706f1fd68b169a681dfe5e4cf0f8e4cb6618bbc2cd"}, + {file = "multidict-6.7.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:716133f7d1d946a4e1b91b1756b23c088881e70ff180c24e864c26192ad7534a"}, + {file = "multidict-6.7.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d1bed1b467ef657f2a0ae62844a607909ef1c6889562de5e1d505f74457d0b96"}, + {file = "multidict-6.7.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:ca43bdfa5d37bd6aee89d85e1d0831fb86e25541be7e9d376ead1b28974f8e5e"}, + {file = "multidict-6.7.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:44b546bd3eb645fd26fb949e43c02a25a2e632e2ca21a35e2e132c8105dc8599"}, + {file = "multidict-6.7.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:a6ef16328011d3f468e7ebc326f24c1445f001ca1dec335b2f8e66bed3006394"}, + {file = "multidict-6.7.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:5aa873cbc8e593d361ae65c68f85faadd755c3295ea2c12040ee146802f23b38"}, + {file = "multidict-6.7.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:3d7b6ccce016e29df4b7ca819659f516f0bc7a4b3efa3bb2012ba06431b044f9"}, + {file = "multidict-6.7.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:171b73bd4ee683d307599b66793ac80981b06f069b62eea1c9e29c9241aa66b0"}, + {file = "multidict-6.7.0-cp314-cp314t-win32.whl", hash = "sha256:b2d7f80c4e1fd010b07cb26820aae86b7e73b681ee4889684fb8d2d4537aab13"}, + {file = "multidict-6.7.0-cp314-cp314t-win_amd64.whl", hash = "sha256:09929cab6fcb68122776d575e03c6cc64ee0b8fca48d17e135474b042ce515cd"}, + {file = "multidict-6.7.0-cp314-cp314t-win_arm64.whl", hash = "sha256:cc41db090ed742f32bd2d2c721861725e6109681eddf835d0a82bd3a5c382827"}, + {file = "multidict-6.7.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:363eb68a0a59bd2303216d2346e6c441ba10d36d1f9969fcb6f1ba700de7bb5c"}, + {file = "multidict-6.7.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d874eb056410ca05fed180b6642e680373688efafc7f077b2a2f61811e873a40"}, + {file = "multidict-6.7.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8b55d5497b51afdfde55925e04a022f1de14d4f4f25cdfd4f5d9b0aa96166851"}, + {file = "multidict-6.7.0-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f8e5c0031b90ca9ce555e2e8fd5c3b02a25f14989cbc310701823832c99eb687"}, + {file = "multidict-6.7.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9cf41880c991716f3c7cec48e2f19ae4045fc9db5fc9cff27347ada24d710bb5"}, + {file = "multidict-6.7.0-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8cfc12a8630a29d601f48d47787bd7eb730e475e83edb5d6c5084317463373eb"}, + {file = "multidict-6.7.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3996b50c3237c4aec17459217c1e7bbdead9a22a0fcd3c365564fbd16439dde6"}, + {file = "multidict-6.7.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7f5170993a0dd3ab871c74f45c0a21a4e2c37a2f2b01b5f722a2ad9c6650469e"}, + {file = "multidict-6.7.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ec81878ddf0e98817def1e77d4f50dae5ef5b0e4fe796fae3bd674304172416e"}, + {file = "multidict-6.7.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:9281bf5b34f59afbc6b1e477a372e9526b66ca446f4bf62592839c195a718b32"}, + {file = "multidict-6.7.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:68af405971779d8b37198726f2b6fe3955db846fee42db7a4286fc542203934c"}, + {file = "multidict-6.7.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3ba3ef510467abb0667421a286dc906e30eb08569365f5cdb131d7aff7c2dd84"}, + {file = "multidict-6.7.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:b61189b29081a20c7e4e0b49b44d5d44bb0dc92be3c6d06a11cc043f81bf9329"}, + {file = "multidict-6.7.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:fb287618b9c7aa3bf8d825f02d9201b2f13078a5ed3b293c8f4d953917d84d5e"}, + {file = "multidict-6.7.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:521f33e377ff64b96c4c556b81c55d0cfffb96a11c194fd0c3f1e56f3d8dd5a4"}, + {file = "multidict-6.7.0-cp39-cp39-win32.whl", hash = "sha256:ce8fdc2dca699f8dbf055a61d73eaa10482569ad20ee3c36ef9641f69afa8c91"}, + {file = "multidict-6.7.0-cp39-cp39-win_amd64.whl", hash = "sha256:7e73299c99939f089dd9b2120a04a516b95cdf8c1cd2b18c53ebf0de80b1f18f"}, + {file = "multidict-6.7.0-cp39-cp39-win_arm64.whl", hash = "sha256:6bdce131e14b04fd34a809b6380dbfd826065c3e2fe8a50dbae659fa0c390546"}, + {file = "multidict-6.7.0-py3-none-any.whl", hash = "sha256:394fc5c42a333c9ffc3e421a4c85e08580d990e08b99f6bf35b4132114c5dcb3"}, + {file = "multidict-6.7.0.tar.gz", hash = "sha256:c6e99d9a65ca282e578dfea819cfa9c0a62b2499d8677392e09feaf305e9e6f5"}, +] + +[[package]] +name = "nemesis-dpapi" +version = "0.1.0" +description = "" +optional = false +python-versions = ">=3.12" +groups = ["main"] +files = [] +develop = true + +[package.dependencies] +asyncpg = ">=0.29.0,<=0.30.0" +cryptography = ">=42.0.0,<43.0.0" +dapr = "1.16.0" +dpapick3 = ">=0.7.1,<0.8.0" +impacket = ">=0.12.0,<0.13.0" +pycryptodome = ">=3.23.0,<4.0.0" +pydantic = ">=2.0.0,<3.0.0" + +[package.source] +type = "directory" +url = "../nemesis_dpapi" + +[[package]] +name = "packaging" +version = "25.0" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.8" +groups = ["main", "dev"] +files = [ + {file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"}, + {file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"}, ] [[package]] @@ -1261,6 +1525,22 @@ files = [ {file = "pefile-2024.8.26.tar.gz", hash = "sha256:3ff6c5d8b43e8c37bb6e6dd5085658d658a7a0bdcd20b6a07b1fcfc1c4e9d632"}, ] +[[package]] +name = "pluggy" +version = "1.6.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.9" +groups = ["main", "dev"] +files = [ + {file = "pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746"}, + {file = "pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["coverage", "pytest", "pytest-benchmark"] + [[package]] name = "plyara" version = "2.2.8" @@ -1278,155 +1558,253 @@ tests = ["coverage", "pycodestyle", "pydocstyle", "pyflakes"] [[package]] name = "propcache" -version = "0.3.2" +version = "0.4.1" description = "Accelerated property cache" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "propcache-0.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:22d9962a358aedbb7a2e36187ff273adeaab9743373a272976d2e348d08c7770"}, - {file = "propcache-0.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0d0fda578d1dc3f77b6b5a5dce3b9ad69a8250a891760a548df850a5e8da87f3"}, - {file = "propcache-0.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3def3da3ac3ce41562d85db655d18ebac740cb3fa4367f11a52b3da9d03a5cc3"}, - {file = "propcache-0.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9bec58347a5a6cebf239daba9bda37dffec5b8d2ce004d9fe4edef3d2815137e"}, - {file = "propcache-0.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:55ffda449a507e9fbd4aca1a7d9aa6753b07d6166140e5a18d2ac9bc49eac220"}, - {file = "propcache-0.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:64a67fb39229a8a8491dd42f864e5e263155e729c2e7ff723d6e25f596b1e8cb"}, - {file = "propcache-0.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9da1cf97b92b51253d5b68cf5a2b9e0dafca095e36b7f2da335e27dc6172a614"}, - {file = "propcache-0.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5f559e127134b07425134b4065be45b166183fdcb433cb6c24c8e4149056ad50"}, - {file = "propcache-0.3.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:aff2e4e06435d61f11a428360a932138d0ec288b0a31dd9bd78d200bd4a2b339"}, - {file = "propcache-0.3.2-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:4927842833830942a5d0a56e6f4839bc484785b8e1ce8d287359794818633ba0"}, - {file = "propcache-0.3.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:6107ddd08b02654a30fb8ad7a132021759d750a82578b94cd55ee2772b6ebea2"}, - {file = "propcache-0.3.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:70bd8b9cd6b519e12859c99f3fc9a93f375ebd22a50296c3a295028bea73b9e7"}, - {file = "propcache-0.3.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2183111651d710d3097338dd1893fcf09c9f54e27ff1a8795495a16a469cc90b"}, - {file = "propcache-0.3.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:fb075ad271405dcad8e2a7ffc9a750a3bf70e533bd86e89f0603e607b93aa64c"}, - {file = "propcache-0.3.2-cp310-cp310-win32.whl", hash = "sha256:404d70768080d3d3bdb41d0771037da19d8340d50b08e104ca0e7f9ce55fce70"}, - {file = "propcache-0.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:7435d766f978b4ede777002e6b3b6641dd229cd1da8d3d3106a45770365f9ad9"}, - {file = "propcache-0.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0b8d2f607bd8f80ddc04088bc2a037fdd17884a6fcadc47a96e334d72f3717be"}, - {file = "propcache-0.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:06766d8f34733416e2e34f46fea488ad5d60726bb9481d3cddf89a6fa2d9603f"}, - {file = "propcache-0.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a2dc1f4a1df4fecf4e6f68013575ff4af84ef6f478fe5344317a65d38a8e6dc9"}, - {file = "propcache-0.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be29c4f4810c5789cf10ddf6af80b041c724e629fa51e308a7a0fb19ed1ef7bf"}, - {file = "propcache-0.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59d61f6970ecbd8ff2e9360304d5c8876a6abd4530cb752c06586849ac8a9dc9"}, - {file = "propcache-0.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:62180e0b8dbb6b004baec00a7983e4cc52f5ada9cd11f48c3528d8cfa7b96a66"}, - {file = "propcache-0.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c144ca294a204c470f18cf4c9d78887810d04a3e2fbb30eea903575a779159df"}, - {file = "propcache-0.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c5c2a784234c28854878d68978265617aa6dc0780e53d44b4d67f3651a17a9a2"}, - {file = "propcache-0.3.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5745bc7acdafa978ca1642891b82c19238eadc78ba2aaa293c6863b304e552d7"}, - {file = "propcache-0.3.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:c0075bf773d66fa8c9d41f66cc132ecc75e5bb9dd7cce3cfd14adc5ca184cb95"}, - {file = "propcache-0.3.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5f57aa0847730daceff0497f417c9de353c575d8da3579162cc74ac294c5369e"}, - {file = "propcache-0.3.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:eef914c014bf72d18efb55619447e0aecd5fb7c2e3fa7441e2e5d6099bddff7e"}, - {file = "propcache-0.3.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:2a4092e8549031e82facf3decdbc0883755d5bbcc62d3aea9d9e185549936dcf"}, - {file = "propcache-0.3.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:85871b050f174bc0bfb437efbdb68aaf860611953ed12418e4361bc9c392749e"}, - {file = "propcache-0.3.2-cp311-cp311-win32.whl", hash = "sha256:36c8d9b673ec57900c3554264e630d45980fd302458e4ac801802a7fd2ef7897"}, - {file = "propcache-0.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:e53af8cb6a781b02d2ea079b5b853ba9430fcbe18a8e3ce647d5982a3ff69f39"}, - {file = "propcache-0.3.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8de106b6c84506b31c27168582cd3cb3000a6412c16df14a8628e5871ff83c10"}, - {file = "propcache-0.3.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:28710b0d3975117239c76600ea351934ac7b5ff56e60953474342608dbbb6154"}, - {file = "propcache-0.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce26862344bdf836650ed2487c3d724b00fbfec4233a1013f597b78c1cb73615"}, - {file = "propcache-0.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bca54bd347a253af2cf4544bbec232ab982f4868de0dd684246b67a51bc6b1db"}, - {file = "propcache-0.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:55780d5e9a2ddc59711d727226bb1ba83a22dd32f64ee15594b9392b1f544eb1"}, - {file = "propcache-0.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:035e631be25d6975ed87ab23153db6a73426a48db688070d925aa27e996fe93c"}, - {file = "propcache-0.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee6f22b6eaa39297c751d0e80c0d3a454f112f5c6481214fcf4c092074cecd67"}, - {file = "propcache-0.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7ca3aee1aa955438c4dba34fc20a9f390e4c79967257d830f137bd5a8a32ed3b"}, - {file = "propcache-0.3.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7a4f30862869fa2b68380d677cc1c5fcf1e0f2b9ea0cf665812895c75d0ca3b8"}, - {file = "propcache-0.3.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:b77ec3c257d7816d9f3700013639db7491a434644c906a2578a11daf13176251"}, - {file = "propcache-0.3.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:cab90ac9d3f14b2d5050928483d3d3b8fb6b4018893fc75710e6aa361ecb2474"}, - {file = "propcache-0.3.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:0b504d29f3c47cf6b9e936c1852246c83d450e8e063d50562115a6be6d3a2535"}, - {file = "propcache-0.3.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:ce2ac2675a6aa41ddb2a0c9cbff53780a617ac3d43e620f8fd77ba1c84dcfc06"}, - {file = "propcache-0.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:62b4239611205294cc433845b914131b2a1f03500ff3c1ed093ed216b82621e1"}, - {file = "propcache-0.3.2-cp312-cp312-win32.whl", hash = "sha256:df4a81b9b53449ebc90cc4deefb052c1dd934ba85012aa912c7ea7b7e38b60c1"}, - {file = "propcache-0.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:7046e79b989d7fe457bb755844019e10f693752d169076138abf17f31380800c"}, - {file = "propcache-0.3.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ca592ed634a73ca002967458187109265e980422116c0a107cf93d81f95af945"}, - {file = "propcache-0.3.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9ecb0aad4020e275652ba3975740f241bd12a61f1a784df044cf7477a02bc252"}, - {file = "propcache-0.3.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7f08f1cc28bd2eade7a8a3d2954ccc673bb02062e3e7da09bc75d843386b342f"}, - {file = "propcache-0.3.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1a342c834734edb4be5ecb1e9fb48cb64b1e2320fccbd8c54bf8da8f2a84c33"}, - {file = "propcache-0.3.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8a544caaae1ac73f1fecfae70ded3e93728831affebd017d53449e3ac052ac1e"}, - {file = "propcache-0.3.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:310d11aa44635298397db47a3ebce7db99a4cc4b9bbdfcf6c98a60c8d5261cf1"}, - {file = "propcache-0.3.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c1396592321ac83157ac03a2023aa6cc4a3cc3cfdecb71090054c09e5a7cce3"}, - {file = "propcache-0.3.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8cabf5b5902272565e78197edb682017d21cf3b550ba0460ee473753f28d23c1"}, - {file = "propcache-0.3.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0a2f2235ac46a7aa25bdeb03a9e7060f6ecbd213b1f9101c43b3090ffb971ef6"}, - {file = "propcache-0.3.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:92b69e12e34869a6970fd2f3da91669899994b47c98f5d430b781c26f1d9f387"}, - {file = "propcache-0.3.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:54e02207c79968ebbdffc169591009f4474dde3b4679e16634d34c9363ff56b4"}, - {file = "propcache-0.3.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4adfb44cb588001f68c5466579d3f1157ca07f7504fc91ec87862e2b8e556b88"}, - {file = "propcache-0.3.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:fd3e6019dc1261cd0291ee8919dd91fbab7b169bb76aeef6c716833a3f65d206"}, - {file = "propcache-0.3.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4c181cad81158d71c41a2bce88edce078458e2dd5ffee7eddd6b05da85079f43"}, - {file = "propcache-0.3.2-cp313-cp313-win32.whl", hash = "sha256:8a08154613f2249519e549de2330cf8e2071c2887309a7b07fb56098f5170a02"}, - {file = "propcache-0.3.2-cp313-cp313-win_amd64.whl", hash = "sha256:e41671f1594fc4ab0a6dec1351864713cb3a279910ae8b58f884a88a0a632c05"}, - {file = "propcache-0.3.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:9a3cf035bbaf035f109987d9d55dc90e4b0e36e04bbbb95af3055ef17194057b"}, - {file = "propcache-0.3.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:156c03d07dc1323d8dacaa221fbe028c5c70d16709cdd63502778e6c3ccca1b0"}, - {file = "propcache-0.3.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:74413c0ba02ba86f55cf60d18daab219f7e531620c15f1e23d95563f505efe7e"}, - {file = "propcache-0.3.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f066b437bb3fa39c58ff97ab2ca351db465157d68ed0440abecb21715eb24b28"}, - {file = "propcache-0.3.2-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f1304b085c83067914721e7e9d9917d41ad87696bf70f0bc7dee450e9c71ad0a"}, - {file = "propcache-0.3.2-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ab50cef01b372763a13333b4e54021bdcb291fc9a8e2ccb9c2df98be51bcde6c"}, - {file = "propcache-0.3.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fad3b2a085ec259ad2c2842666b2a0a49dea8463579c606426128925af1ed725"}, - {file = "propcache-0.3.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:261fa020c1c14deafd54c76b014956e2f86991af198c51139faf41c4d5e83892"}, - {file = "propcache-0.3.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:46d7f8aa79c927e5f987ee3a80205c987717d3659f035c85cf0c3680526bdb44"}, - {file = "propcache-0.3.2-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:6d8f3f0eebf73e3c0ff0e7853f68be638b4043c65a70517bb575eff54edd8dbe"}, - {file = "propcache-0.3.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:03c89c1b14a5452cf15403e291c0ccd7751d5b9736ecb2c5bab977ad6c5bcd81"}, - {file = "propcache-0.3.2-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:0cc17efde71e12bbaad086d679ce575268d70bc123a5a71ea7ad76f70ba30bba"}, - {file = "propcache-0.3.2-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:acdf05d00696bc0447e278bb53cb04ca72354e562cf88ea6f9107df8e7fd9770"}, - {file = "propcache-0.3.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4445542398bd0b5d32df908031cb1b30d43ac848e20470a878b770ec2dcc6330"}, - {file = "propcache-0.3.2-cp313-cp313t-win32.whl", hash = "sha256:f86e5d7cd03afb3a1db8e9f9f6eff15794e79e791350ac48a8c924e6f439f394"}, - {file = "propcache-0.3.2-cp313-cp313t-win_amd64.whl", hash = "sha256:9704bedf6e7cbe3c65eca4379a9b53ee6a83749f047808cbb5044d40d7d72198"}, - {file = "propcache-0.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a7fad897f14d92086d6b03fdd2eb844777b0c4d7ec5e3bac0fbae2ab0602bbe5"}, - {file = "propcache-0.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1f43837d4ca000243fd7fd6301947d7cb93360d03cd08369969450cc6b2ce3b4"}, - {file = "propcache-0.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:261df2e9474a5949c46e962065d88eb9b96ce0f2bd30e9d3136bcde84befd8f2"}, - {file = "propcache-0.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e514326b79e51f0a177daab1052bc164d9d9e54133797a3a58d24c9c87a3fe6d"}, - {file = "propcache-0.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d4a996adb6904f85894570301939afeee65f072b4fd265ed7e569e8d9058e4ec"}, - {file = "propcache-0.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:76cace5d6b2a54e55b137669b30f31aa15977eeed390c7cbfb1dafa8dfe9a701"}, - {file = "propcache-0.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31248e44b81d59d6addbb182c4720f90b44e1efdc19f58112a3c3a1615fb47ef"}, - {file = "propcache-0.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abb7fa19dbf88d3857363e0493b999b8011eea856b846305d8c0512dfdf8fbb1"}, - {file = "propcache-0.3.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:d81ac3ae39d38588ad0549e321e6f773a4e7cc68e7751524a22885d5bbadf886"}, - {file = "propcache-0.3.2-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:cc2782eb0f7a16462285b6f8394bbbd0e1ee5f928034e941ffc444012224171b"}, - {file = "propcache-0.3.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:db429c19a6c7e8a1c320e6a13c99799450f411b02251fb1b75e6217cf4a14fcb"}, - {file = "propcache-0.3.2-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:21d8759141a9e00a681d35a1f160892a36fb6caa715ba0b832f7747da48fb6ea"}, - {file = "propcache-0.3.2-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:2ca6d378f09adb13837614ad2754fa8afaee330254f404299611bce41a8438cb"}, - {file = "propcache-0.3.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:34a624af06c048946709f4278b4176470073deda88d91342665d95f7c6270fbe"}, - {file = "propcache-0.3.2-cp39-cp39-win32.whl", hash = "sha256:4ba3fef1c30f306b1c274ce0b8baaa2c3cdd91f645c48f06394068f37d3837a1"}, - {file = "propcache-0.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:7a2368eed65fc69a7a7a40b27f22e85e7627b74216f0846b04ba5c116e191ec9"}, - {file = "propcache-0.3.2-py3-none-any.whl", hash = "sha256:98f1ec44fb675f5052cccc8e609c46ed23a35a1cfd18545ad4e29002d858a43f"}, - {file = "propcache-0.3.2.tar.gz", hash = "sha256:20d7d62e4e7ef05f221e0db2856b979540686342e7dd9973b815599c7057e168"}, + {file = "propcache-0.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c2d1fa3201efaf55d730400d945b5b3ab6e672e100ba0f9a409d950ab25d7db"}, + {file = "propcache-0.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1eb2994229cc8ce7fe9b3db88f5465f5fd8651672840b2e426b88cdb1a30aac8"}, + {file = "propcache-0.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:66c1f011f45a3b33d7bcb22daed4b29c0c9e2224758b6be00686731e1b46f925"}, + {file = "propcache-0.4.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9a52009f2adffe195d0b605c25ec929d26b36ef986ba85244891dee3b294df21"}, + {file = "propcache-0.4.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5d4e2366a9c7b837555cf02fb9be2e3167d333aff716332ef1b7c3a142ec40c5"}, + {file = "propcache-0.4.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:9d2b6caef873b4f09e26ea7e33d65f42b944837563a47a94719cc3544319a0db"}, + {file = "propcache-0.4.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2b16ec437a8c8a965ecf95739448dd938b5c7f56e67ea009f4300d8df05f32b7"}, + {file = "propcache-0.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:296f4c8ed03ca7476813fe666c9ea97869a8d7aec972618671b33a38a5182ef4"}, + {file = "propcache-0.4.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:1f0978529a418ebd1f49dad413a2b68af33f85d5c5ca5c6ca2a3bed375a7ac60"}, + {file = "propcache-0.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fd138803047fb4c062b1c1dd95462f5209456bfab55c734458f15d11da288f8f"}, + {file = "propcache-0.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8c9b3cbe4584636d72ff556d9036e0c9317fa27b3ac1f0f558e7e84d1c9c5900"}, + {file = "propcache-0.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f93243fdc5657247533273ac4f86ae106cc6445a0efacb9a1bfe982fcfefd90c"}, + {file = "propcache-0.4.1-cp310-cp310-win32.whl", hash = "sha256:a0ee98db9c5f80785b266eb805016e36058ac72c51a064040f2bc43b61101cdb"}, + {file = "propcache-0.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:1cdb7988c4e5ac7f6d175a28a9aa0c94cb6f2ebe52756a3c0cda98d2809a9e37"}, + {file = "propcache-0.4.1-cp310-cp310-win_arm64.whl", hash = "sha256:d82ad62b19645419fe79dd63b3f9253e15b30e955c0170e5cebc350c1844e581"}, + {file = "propcache-0.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:60a8fda9644b7dfd5dece8c61d8a85e271cb958075bfc4e01083c148b61a7caf"}, + {file = "propcache-0.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c30b53e7e6bda1d547cabb47c825f3843a0a1a42b0496087bb58d8fedf9f41b5"}, + {file = "propcache-0.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6918ecbd897443087a3b7cd978d56546a812517dcaaca51b49526720571fa93e"}, + {file = "propcache-0.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3d902a36df4e5989763425a8ab9e98cd8ad5c52c823b34ee7ef307fd50582566"}, + {file = "propcache-0.4.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a9695397f85973bb40427dedddf70d8dc4a44b22f1650dd4af9eedf443d45165"}, + {file = "propcache-0.4.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2bb07ffd7eaad486576430c89f9b215f9e4be68c4866a96e97db9e97fead85dc"}, + {file = "propcache-0.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fd6f30fdcf9ae2a70abd34da54f18da086160e4d7d9251f81f3da0ff84fc5a48"}, + {file = "propcache-0.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fc38cba02d1acba4e2869eef1a57a43dfbd3d49a59bf90dda7444ec2be6a5570"}, + {file = "propcache-0.4.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:67fad6162281e80e882fb3ec355398cf72864a54069d060321f6cd0ade95fe85"}, + {file = "propcache-0.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f10207adf04d08bec185bae14d9606a1444715bc99180f9331c9c02093e1959e"}, + {file = "propcache-0.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e9b0d8d0845bbc4cfcdcbcdbf5086886bc8157aa963c31c777ceff7846c77757"}, + {file = "propcache-0.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:981333cb2f4c1896a12f4ab92a9cc8f09ea664e9b7dbdc4eff74627af3a11c0f"}, + {file = "propcache-0.4.1-cp311-cp311-win32.whl", hash = "sha256:f1d2f90aeec838a52f1c1a32fe9a619fefd5e411721a9117fbf82aea638fe8a1"}, + {file = "propcache-0.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:364426a62660f3f699949ac8c621aad6977be7126c5807ce48c0aeb8e7333ea6"}, + {file = "propcache-0.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:e53f3a38d3510c11953f3e6a33f205c6d1b001129f972805ca9b42fc308bc239"}, + {file = "propcache-0.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e153e9cd40cc8945138822807139367f256f89c6810c2634a4f6902b52d3b4e2"}, + {file = "propcache-0.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cd547953428f7abb73c5ad82cbb32109566204260d98e41e5dfdc682eb7f8403"}, + {file = "propcache-0.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f048da1b4f243fc44f205dfd320933a951b8d89e0afd4c7cacc762a8b9165207"}, + {file = "propcache-0.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ec17c65562a827bba85e3872ead335f95405ea1674860d96483a02f5c698fa72"}, + {file = "propcache-0.4.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:405aac25c6394ef275dee4c709be43745d36674b223ba4eb7144bf4d691b7367"}, + {file = "propcache-0.4.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0013cb6f8dde4b2a2f66903b8ba740bdfe378c943c4377a200551ceb27f379e4"}, + {file = "propcache-0.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:15932ab57837c3368b024473a525e25d316d8353016e7cc0e5ba9eb343fbb1cf"}, + {file = "propcache-0.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:031dce78b9dc099f4c29785d9cf5577a3faf9ebf74ecbd3c856a7b92768c3df3"}, + {file = "propcache-0.4.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ab08df6c9a035bee56e31af99be621526bd237bea9f32def431c656b29e41778"}, + {file = "propcache-0.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4d7af63f9f93fe593afbf104c21b3b15868efb2c21d07d8732c0c4287e66b6a6"}, + {file = "propcache-0.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:cfc27c945f422e8b5071b6e93169679e4eb5bf73bbcbf1ba3ae3a83d2f78ebd9"}, + {file = "propcache-0.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:35c3277624a080cc6ec6f847cbbbb5b49affa3598c4535a0a4682a697aaa5c75"}, + {file = "propcache-0.4.1-cp312-cp312-win32.whl", hash = "sha256:671538c2262dadb5ba6395e26c1731e1d52534bfe9ae56d0b5573ce539266aa8"}, + {file = "propcache-0.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:cb2d222e72399fcf5890d1d5cc1060857b9b236adff2792ff48ca2dfd46c81db"}, + {file = "propcache-0.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:204483131fb222bdaaeeea9f9e6c6ed0cac32731f75dfc1d4a567fc1926477c1"}, + {file = "propcache-0.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:43eedf29202c08550aac1d14e0ee619b0430aaef78f85864c1a892294fbc28cf"}, + {file = "propcache-0.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d62cdfcfd89ccb8de04e0eda998535c406bf5e060ffd56be6c586cbcc05b3311"}, + {file = "propcache-0.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cae65ad55793da34db5f54e4029b89d3b9b9490d8abe1b4c7ab5d4b8ec7ebf74"}, + {file = "propcache-0.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:333ddb9031d2704a301ee3e506dc46b1fe5f294ec198ed6435ad5b6a085facfe"}, + {file = "propcache-0.4.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:fd0858c20f078a32cf55f7e81473d96dcf3b93fd2ccdb3d40fdf54b8573df3af"}, + {file = "propcache-0.4.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:678ae89ebc632c5c204c794f8dab2837c5f159aeb59e6ed0539500400577298c"}, + {file = "propcache-0.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d472aeb4fbf9865e0c6d622d7f4d54a4e101a89715d8904282bb5f9a2f476c3f"}, + {file = "propcache-0.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4d3df5fa7e36b3225954fba85589da77a0fe6a53e3976de39caf04a0db4c36f1"}, + {file = "propcache-0.4.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:ee17f18d2498f2673e432faaa71698032b0127ebf23ae5974eeaf806c279df24"}, + {file = "propcache-0.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:580e97762b950f993ae618e167e7be9256b8353c2dcd8b99ec100eb50f5286aa"}, + {file = "propcache-0.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:501d20b891688eb8e7aa903021f0b72d5a55db40ffaab27edefd1027caaafa61"}, + {file = "propcache-0.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a0bd56e5b100aef69bd8562b74b46254e7c8812918d3baa700c8a8009b0af66"}, + {file = "propcache-0.4.1-cp313-cp313-win32.whl", hash = "sha256:bcc9aaa5d80322bc2fb24bb7accb4a30f81e90ab8d6ba187aec0744bc302ad81"}, + {file = "propcache-0.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:381914df18634f5494334d201e98245c0596067504b9372d8cf93f4bb23e025e"}, + {file = "propcache-0.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:8873eb4460fd55333ea49b7d189749ecf6e55bf85080f11b1c4530ed3034cba1"}, + {file = "propcache-0.4.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:92d1935ee1f8d7442da9c0c4fa7ac20d07e94064184811b685f5c4fada64553b"}, + {file = "propcache-0.4.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:473c61b39e1460d386479b9b2f337da492042447c9b685f28be4f74d3529e566"}, + {file = "propcache-0.4.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:c0ef0aaafc66fbd87842a3fe3902fd889825646bc21149eafe47be6072725835"}, + {file = "propcache-0.4.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f95393b4d66bfae908c3ca8d169d5f79cd65636ae15b5e7a4f6e67af675adb0e"}, + {file = "propcache-0.4.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c07fda85708bc48578467e85099645167a955ba093be0a2dcba962195676e859"}, + {file = "propcache-0.4.1-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:af223b406d6d000830c6f65f1e6431783fc3f713ba3e6cc8c024d5ee96170a4b"}, + {file = "propcache-0.4.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a78372c932c90ee474559c5ddfffd718238e8673c340dc21fe45c5b8b54559a0"}, + {file = "propcache-0.4.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:564d9f0d4d9509e1a870c920a89b2fec951b44bf5ba7d537a9e7c1ccec2c18af"}, + {file = "propcache-0.4.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:17612831fda0138059cc5546f4d12a2aacfb9e47068c06af35c400ba58ba7393"}, + {file = "propcache-0.4.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:41a89040cb10bd345b3c1a873b2bf36413d48da1def52f268a055f7398514874"}, + {file = "propcache-0.4.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e35b88984e7fa64aacecea39236cee32dd9bd8c55f57ba8a75cf2399553f9bd7"}, + {file = "propcache-0.4.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6f8b465489f927b0df505cbe26ffbeed4d6d8a2bbc61ce90eb074ff129ef0ab1"}, + {file = "propcache-0.4.1-cp313-cp313t-win32.whl", hash = "sha256:2ad890caa1d928c7c2965b48f3a3815c853180831d0e5503d35cf00c472f4717"}, + {file = "propcache-0.4.1-cp313-cp313t-win_amd64.whl", hash = "sha256:f7ee0e597f495cf415bcbd3da3caa3bd7e816b74d0d52b8145954c5e6fd3ff37"}, + {file = "propcache-0.4.1-cp313-cp313t-win_arm64.whl", hash = "sha256:929d7cbe1f01bb7baffb33dc14eb5691c95831450a26354cd210a8155170c93a"}, + {file = "propcache-0.4.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3f7124c9d820ba5548d431afb4632301acf965db49e666aa21c305cbe8c6de12"}, + {file = "propcache-0.4.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:c0d4b719b7da33599dfe3b22d3db1ef789210a0597bc650b7cee9c77c2be8c5c"}, + {file = "propcache-0.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9f302f4783709a78240ebc311b793f123328716a60911d667e0c036bc5dcbded"}, + {file = "propcache-0.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c80ee5802e3fb9ea37938e7eecc307fb984837091d5fd262bb37238b1ae97641"}, + {file = "propcache-0.4.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ed5a841e8bb29a55fb8159ed526b26adc5bdd7e8bd7bf793ce647cb08656cdf4"}, + {file = "propcache-0.4.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:55c72fd6ea2da4c318e74ffdf93c4fe4e926051133657459131a95c846d16d44"}, + {file = "propcache-0.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8326e144341460402713f91df60ade3c999d601e7eb5ff8f6f7862d54de0610d"}, + {file = "propcache-0.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:060b16ae65bc098da7f6d25bf359f1f31f688384858204fe5d652979e0015e5b"}, + {file = "propcache-0.4.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:89eb3fa9524f7bec9de6e83cf3faed9d79bffa560672c118a96a171a6f55831e"}, + {file = "propcache-0.4.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:dee69d7015dc235f526fe80a9c90d65eb0039103fe565776250881731f06349f"}, + {file = "propcache-0.4.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:5558992a00dfd54ccbc64a32726a3357ec93825a418a401f5cc67df0ac5d9e49"}, + {file = "propcache-0.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c9b822a577f560fbd9554812526831712c1436d2c046cedee4c3796d3543b144"}, + {file = "propcache-0.4.1-cp314-cp314-win32.whl", hash = "sha256:ab4c29b49d560fe48b696cdcb127dd36e0bc2472548f3bf56cc5cb3da2b2984f"}, + {file = "propcache-0.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:5a103c3eb905fcea0ab98be99c3a9a5ab2de60228aa5aceedc614c0281cf6153"}, + {file = "propcache-0.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:74c1fb26515153e482e00177a1ad654721bf9207da8a494a0c05e797ad27b992"}, + {file = "propcache-0.4.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:824e908bce90fb2743bd6b59db36eb4f45cd350a39637c9f73b1c1ea66f5b75f"}, + {file = "propcache-0.4.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:c2b5e7db5328427c57c8e8831abda175421b709672f6cfc3d630c3b7e2146393"}, + {file = "propcache-0.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6f6ff873ed40292cd4969ef5310179afd5db59fdf055897e282485043fc80ad0"}, + {file = "propcache-0.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:49a2dc67c154db2c1463013594c458881a069fcf98940e61a0569016a583020a"}, + {file = "propcache-0.4.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:005f08e6a0529984491e37d8dbc3dd86f84bd78a8ceb5fa9a021f4c48d4984be"}, + {file = "propcache-0.4.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5c3310452e0d31390da9035c348633b43d7e7feb2e37be252be6da45abd1abcc"}, + {file = "propcache-0.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c3c70630930447f9ef1caac7728c8ad1c56bc5015338b20fed0d08ea2480b3a"}, + {file = "propcache-0.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8e57061305815dfc910a3634dcf584f08168a8836e6999983569f51a8544cd89"}, + {file = "propcache-0.4.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:521a463429ef54143092c11a77e04056dd00636f72e8c45b70aaa3140d639726"}, + {file = "propcache-0.4.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:120c964da3fdc75e3731aa392527136d4ad35868cc556fd09bb6d09172d9a367"}, + {file = "propcache-0.4.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:d8f353eb14ee3441ee844ade4277d560cdd68288838673273b978e3d6d2c8f36"}, + {file = "propcache-0.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ab2943be7c652f09638800905ee1bab2c544e537edb57d527997a24c13dc1455"}, + {file = "propcache-0.4.1-cp314-cp314t-win32.whl", hash = "sha256:05674a162469f31358c30bcaa8883cb7829fa3110bf9c0991fe27d7896c42d85"}, + {file = "propcache-0.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:990f6b3e2a27d683cb7602ed6c86f15ee6b43b1194736f9baaeb93d0016633b1"}, + {file = "propcache-0.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:ecef2343af4cc68e05131e45024ba34f6095821988a9d0a02aa7c73fcc448aa9"}, + {file = "propcache-0.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3d233076ccf9e450c8b3bc6720af226b898ef5d051a2d145f7d765e6e9f9bcff"}, + {file = "propcache-0.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:357f5bb5c377a82e105e44bd3d52ba22b616f7b9773714bff93573988ef0a5fb"}, + {file = "propcache-0.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cbc3b6dfc728105b2a57c06791eb07a94229202ea75c59db644d7d496b698cac"}, + {file = "propcache-0.4.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:182b51b421f0501952d938dc0b0eb45246a5b5153c50d42b495ad5fb7517c888"}, + {file = "propcache-0.4.1-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4b536b39c5199b96fc6245eb5fb796c497381d3942f169e44e8e392b29c9ebcc"}, + {file = "propcache-0.4.1-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:db65d2af507bbfbdcedb254a11149f894169d90488dd3e7190f7cdcb2d6cd57a"}, + {file = "propcache-0.4.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fd2dbc472da1f772a4dae4fa24be938a6c544671a912e30529984dd80400cd88"}, + {file = "propcache-0.4.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:daede9cd44e0f8bdd9e6cc9a607fc81feb80fae7a5fc6cecaff0e0bb32e42d00"}, + {file = "propcache-0.4.1-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:71b749281b816793678ae7f3d0d84bd36e694953822eaad408d682efc5ca18e0"}, + {file = "propcache-0.4.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:0002004213ee1f36cfb3f9a42b5066100c44276b9b72b4e1504cddd3d692e86e"}, + {file = "propcache-0.4.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:fe49d0a85038f36ba9e3ffafa1103e61170b28e95b16622e11be0a0ea07c6781"}, + {file = "propcache-0.4.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:99d43339c83aaf4d32bda60928231848eee470c6bda8d02599cc4cebe872d183"}, + {file = "propcache-0.4.1-cp39-cp39-win32.whl", hash = "sha256:a129e76735bc792794d5177069691c3217898b9f5cee2b2661471e52ffe13f19"}, + {file = "propcache-0.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:948dab269721ae9a87fd16c514a0a2c2a1bdb23a9a61b969b0f9d9ee2968546f"}, + {file = "propcache-0.4.1-cp39-cp39-win_arm64.whl", hash = "sha256:5fd37c406dd6dc85aa743e214cef35dc54bbdd1419baac4f6ae5e5b1a2976938"}, + {file = "propcache-0.4.1-py3-none-any.whl", hash = "sha256:af2a6052aeb6cf17d3e46ee169099044fd8224cbaf75c76a2ef596e8163e2237"}, + {file = "propcache-0.4.1.tar.gz", hash = "sha256:f48107a8c637e80362555f37ecf49abe20370e557cc4ab374f04ec4423c97c3d"}, ] [[package]] name = "protobuf" -version = "6.31.1" +version = "6.32.1" description = "" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "protobuf-6.31.1-cp310-abi3-win32.whl", hash = "sha256:7fa17d5a29c2e04b7d90e5e32388b8bfd0e7107cd8e616feef7ed3fa6bdab5c9"}, - {file = "protobuf-6.31.1-cp310-abi3-win_amd64.whl", hash = "sha256:426f59d2964864a1a366254fa703b8632dcec0790d8862d30034d8245e1cd447"}, - {file = "protobuf-6.31.1-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:6f1227473dc43d44ed644425268eb7c2e488ae245d51c6866d19fe158e207402"}, - {file = "protobuf-6.31.1-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:a40fc12b84c154884d7d4c4ebd675d5b3b5283e155f324049ae396b95ddebc39"}, - {file = "protobuf-6.31.1-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:4ee898bf66f7a8b0bd21bce523814e6fbd8c6add948045ce958b73af7e8878c6"}, - {file = "protobuf-6.31.1-cp39-cp39-win32.whl", hash = "sha256:0414e3aa5a5f3ff423828e1e6a6e907d6c65c1d5b7e6e975793d5590bdeecc16"}, - {file = "protobuf-6.31.1-cp39-cp39-win_amd64.whl", hash = "sha256:8764cf4587791e7564051b35524b72844f845ad0bb011704c3736cce762d8fe9"}, - {file = "protobuf-6.31.1-py3-none-any.whl", hash = "sha256:720a6c7e6b77288b85063569baae8536671b39f15cc22037ec7045658d80489e"}, - {file = "protobuf-6.31.1.tar.gz", hash = "sha256:d8cac4c982f0b957a4dc73a80e2ea24fab08e679c0de9deb835f4a12d69aca9a"}, + {file = "protobuf-6.32.1-cp310-abi3-win32.whl", hash = "sha256:a8a32a84bc9f2aad712041b8b366190f71dde248926da517bde9e832e4412085"}, + {file = "protobuf-6.32.1-cp310-abi3-win_amd64.whl", hash = "sha256:b00a7d8c25fa471f16bc8153d0e53d6c9e827f0953f3c09aaa4331c718cae5e1"}, + {file = "protobuf-6.32.1-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:d8c7e6eb619ffdf105ee4ab76af5a68b60a9d0f66da3ea12d1640e6d8dab7281"}, + {file = "protobuf-6.32.1-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:2f5b80a49e1eb7b86d85fcd23fe92df154b9730a725c3b38c4e43b9d77018bf4"}, + {file = "protobuf-6.32.1-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:b1864818300c297265c83a4982fd3169f97122c299f56a56e2445c3698d34710"}, + {file = "protobuf-6.32.1-cp39-cp39-win32.whl", hash = "sha256:68ff170bac18c8178f130d1ccb94700cf72852298e016a2443bdb9502279e5f1"}, + {file = "protobuf-6.32.1-cp39-cp39-win_amd64.whl", hash = "sha256:d0975d0b2f3e6957111aa3935d08a0eb7e006b1505d825f862a1fffc8348e122"}, + {file = "protobuf-6.32.1-py3-none-any.whl", hash = "sha256:2601b779fc7d32a866c6b4404f9d42a3f67c5b9f3f15b4db3cccabe06b95c346"}, + {file = "protobuf-6.32.1.tar.gz", hash = "sha256:ee2469e4a021474ab9baafea6cd070e5bf27c7d29433504ddea1a4ee5850f68d"}, ] [[package]] name = "psycopg" -version = "3.2.9" +version = "3.2.10" description = "PostgreSQL database adapter for Python" optional = false python-versions = ">=3.8" groups = ["main"] files = [ - {file = "psycopg-3.2.9-py3-none-any.whl", hash = "sha256:01a8dadccdaac2123c916208c96e06631641c0566b22005493f09663c7a8d3b6"}, - {file = "psycopg-3.2.9.tar.gz", hash = "sha256:2fbb46fcd17bc81f993f28c47f1ebea38d66ae97cc2dbc3cad73b37cefbff700"}, + {file = "psycopg-3.2.10-py3-none-any.whl", hash = "sha256:ab5caf09a9ec42e314a21f5216dbcceac528e0e05142e42eea83a3b28b320ac3"}, + {file = "psycopg-3.2.10.tar.gz", hash = "sha256:0bce99269d16ed18401683a8569b2c5abd94f72f8364856d56c0389bcd50972a"}, ] [package.dependencies] +psycopg-binary = {version = "3.2.10", optional = true, markers = "implementation_name != \"pypy\" and extra == \"binary\""} typing-extensions = {version = ">=4.6", markers = "python_version < \"3.13\""} tzdata = {version = "*", markers = "sys_platform == \"win32\""} [package.extras] -binary = ["psycopg-binary (==3.2.9)"] -c = ["psycopg-c (==3.2.9)"] +binary = ["psycopg-binary (==3.2.10)"] +c = ["psycopg-c (==3.2.10)"] dev = ["ast-comments (>=1.1.2)", "black (>=24.1.0)", "codespell (>=2.2)", "dnspython (>=2.1)", "flake8 (>=4.0)", "isort-psycopg", "isort[colors] (>=6.0)", "mypy (>=1.14)", "pre-commit (>=4.0.1)", "types-setuptools (>=57.4)", "types-shapely (>=2.0)", "wheel (>=0.37)"] docs = ["Sphinx (>=5.0)", "furo (==2022.6.21)", "sphinx-autobuild (>=2021.3.14)", "sphinx-autodoc-typehints (>=1.12)"] pool = ["psycopg-pool"] test = ["anyio (>=4.0)", "mypy (>=1.14)", "pproxy (>=2.7)", "pytest (>=6.2.5)", "pytest-cov (>=3.0)", "pytest-randomly (>=3.5)"] +[[package]] +name = "psycopg-binary" +version = "3.2.10" +description = "PostgreSQL database adapter for Python -- C optimisation distribution" +optional = false +python-versions = ">=3.8" +groups = ["main"] +markers = "implementation_name != \"pypy\"" +files = [ + {file = "psycopg_binary-3.2.10-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:037dc92fc7d3f2adae7680e17216934c15b919d6528b908ac2eb52aecc0addcf"}, + {file = "psycopg_binary-3.2.10-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:84f7e8c5e5031db342ae697c2e8fb48cd708ba56990573b33e53ce626445371d"}, + {file = "psycopg_binary-3.2.10-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:a5a81104d88780018005fe17c37fa55b4afbb6dd3c205963cc56c025d5f1cc32"}, + {file = "psycopg_binary-3.2.10-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:0c23e88e048bbc33f32f5a35981707c9418723d469552dd5ac4e956366e58492"}, + {file = "psycopg_binary-3.2.10-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9c9f2728488ac5848acdbf14bb4fde50f8ba783cbf3c19e9abd506741389fa7f"}, + {file = "psycopg_binary-3.2.10-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ab1c6d761c4ee581016823dcc02f29b16ad69177fcbba88a9074c924fc31813e"}, + {file = "psycopg_binary-3.2.10-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:a024b3ee539a475cbc59df877c8ecdd6f8552a1b522b69196935bc26dc6152fb"}, + {file = "psycopg_binary-3.2.10-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:50130c0d1a2a01ec3d41631df86b6c1646c76718be000600a399dc1aad80b813"}, + {file = "psycopg_binary-3.2.10-cp310-cp310-win_amd64.whl", hash = "sha256:7fa1626225a162924d2da0ff4ef77869f7a8501d320355d2732be5bf2dda6138"}, + {file = "psycopg_binary-3.2.10-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:db0eb06a19e4c64a08db0db80875ede44939af6a2afc281762c338fad5d6e547"}, + {file = "psycopg_binary-3.2.10-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d922fdd49ed17c558b6b2f9ae2054c3d0cced2a34e079ce5a41c86904d0203f7"}, + {file = "psycopg_binary-3.2.10-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d557a94cd6d2e775b3af6cc0bd0ff0d9d641820b5cc3060ccf1f5ca2bf971217"}, + {file = "psycopg_binary-3.2.10-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:29b6bb87959515bc8b6abef10d8d23a9a681f03e48e9f0c8adb4b9fb7fa73f11"}, + {file = "psycopg_binary-3.2.10-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1b29285474e3339d0840e1b5079fdb0481914108f92ec62de0c87ae333c60b24"}, + {file = "psycopg_binary-3.2.10-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:62590dd113d10cd9c08251cb80b32e2e8aaf01ece04a700322e776b1d216959f"}, + {file = "psycopg_binary-3.2.10-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:764a5b9b40ad371c55dfdf95374d89e44a82fd62272d4fceebea0adb8930e2fb"}, + {file = "psycopg_binary-3.2.10-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bd3676a04970cf825d2c771b0c147f91182c5a3653e0dbe958e12383668d0f79"}, + {file = "psycopg_binary-3.2.10-cp311-cp311-win_amd64.whl", hash = "sha256:646048f46192c8d23786cc6ef19f35b7488d4110396391e407eca695fdfe9dcd"}, + {file = "psycopg_binary-3.2.10-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:1dee2f4d2adc9adacbfecf8254bd82f6ac95cff707e1b9b99aa721cd1ef16b47"}, + {file = "psycopg_binary-3.2.10-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8b45e65383da9c4a42a56f817973e521e893f4faae897fe9f1a971f9fe799742"}, + {file = "psycopg_binary-3.2.10-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:484d2b1659afe0f8f1cef5ea960bb640e96fa864faf917086f9f833f5c7a8034"}, + {file = "psycopg_binary-3.2.10-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:3bb4046973264ebc8cb7e20a83882d68577c1f26a6f8ad4fe52e4468cd9a8eee"}, + {file = "psycopg_binary-3.2.10-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:14bcbcac0cab465d88b2581e43ec01af4b01c9833e663f1352e05cb41be19e44"}, + {file = "psycopg_binary-3.2.10-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:70bb7f665587dfd79e69f48b34efe226149454d7aab138ed22d5431d703de2f6"}, + {file = "psycopg_binary-3.2.10-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:d2fe9eaa367f6171ab1a21a7dcb335eb2398be7f8bb7e04a20e2260aedc6f782"}, + {file = "psycopg_binary-3.2.10-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:299834cce3eec0c48aae5a5207fc8f0c558fd65f2ceab1a36693329847da956b"}, + {file = "psycopg_binary-3.2.10-cp312-cp312-win_amd64.whl", hash = "sha256:e037aac8dc894d147ef33056fc826ee5072977107a3fdf06122224353a057598"}, + {file = "psycopg_binary-3.2.10-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:55b14f2402be027fe1568bc6c4d75ac34628ff5442a70f74137dadf99f738e3b"}, + {file = "psycopg_binary-3.2.10-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:43d803fb4e108a67c78ba58f3e6855437ca25d56504cae7ebbfbd8fce9b59247"}, + {file = "psycopg_binary-3.2.10-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:470594d303928ab72a1ffd179c9c7bde9d00f76711d6b0c28f8a46ddf56d9807"}, + {file = "psycopg_binary-3.2.10-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:a1d4e4d309049e3cb61269652a3ca56cb598da30ecd7eb8cea561e0d18bc1a43"}, + {file = "psycopg_binary-3.2.10-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a92ff1c2cd79b3966d6a87e26ceb222ecd5581b5ae4b58961f126af806a861ed"}, + {file = "psycopg_binary-3.2.10-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac0365398947879c9827b319217096be727da16c94422e0eb3cf98c930643162"}, + {file = "psycopg_binary-3.2.10-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:42ee399c2613b470a87084ed79b06d9d277f19b0457c10e03a4aef7059097abc"}, + {file = "psycopg_binary-3.2.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2028073fc12cd70ba003309d1439c0c4afab4a7eee7653b8c91213064fffe12b"}, + {file = "psycopg_binary-3.2.10-cp313-cp313-win_amd64.whl", hash = "sha256:8390db6d2010ffcaf7f2b42339a2da620a7125d37029c1f9b72dfb04a8e7be6f"}, + {file = "psycopg_binary-3.2.10-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:b34c278a58aa79562afe7f45e0455b1f4cad5974fc3d5674cc5f1f9f57e97fc5"}, + {file = "psycopg_binary-3.2.10-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:810f65b9ef1fe9dddb5c05937884ea9563aaf4e1a2c3d138205231ed5f439511"}, + {file = "psycopg_binary-3.2.10-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8923487c3898c65e1450847e15d734bb2e6adbd2e79d2d1dd5ad829a1306bdc0"}, + {file = "psycopg_binary-3.2.10-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:7950ff79df7a453ac8a7d7a74694055b6c15905b0a2b6e3c99eb59c51a3f9bf7"}, + {file = "psycopg_binary-3.2.10-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0c2b95e83fda70ed2b0b4fadd8538572e4a4d987b721823981862d1ab56cc760"}, + {file = "psycopg_binary-3.2.10-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:20384985fbc650c09a547a13c6d7f91bb42020d38ceafd2b68b7fc4a48a1f160"}, + {file = "psycopg_binary-3.2.10-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:1f6982609b8ff8fcd67299b67cd5787da1876f3bb28fedd547262cfa8ddedf94"}, + {file = "psycopg_binary-3.2.10-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:bf30dcf6aaaa8d4779a20d2158bdf81cc8e84ce8eee595d748a7671c70c7b890"}, + {file = "psycopg_binary-3.2.10-cp314-cp314-win_amd64.whl", hash = "sha256:d5c6a66a76022af41970bf19f51bc6bf87bd10165783dd1d40484bfd87d6b382"}, + {file = "psycopg_binary-3.2.10-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:901729188b3fd5625970650ca1167786847dee0b92930c2858724d1a5e25dee1"}, + {file = "psycopg_binary-3.2.10-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d7d05174276bb403b8a57e01b857d96b0ac2a6879c5ce06a5cac2d1115763081"}, + {file = "psycopg_binary-3.2.10-cp38-cp38-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:37b42b2f5f58df1f07a5df1b0c2bcc9bd3b9c105e2e988923bfa47aa4ae967da"}, + {file = "psycopg_binary-3.2.10-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6fe450a98a0788b721b1b8302f0ba9be6eca82faf74bf7a86d794cd6484c7e27"}, + {file = "psycopg_binary-3.2.10-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:a28f24a7b68456bd31209b027a5b04304d37eb1d622ef847bf8c47933218a738"}, + {file = "psycopg_binary-3.2.10-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:5369202e0e764193eac311b5a337d8cd58b1e23b822ddb7a559ed9f683d97623"}, + {file = "psycopg_binary-3.2.10-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:8f4ae059c6c9e491cdc3f39f9fc4f09373ef281c6cc381499269dcff21abafc9"}, + {file = "psycopg_binary-3.2.10-cp38-cp38-win_amd64.whl", hash = "sha256:3e115930af2f38f4bbb5f1b61b598ceb802f091c1592c0fe0571c796b714b89a"}, + {file = "psycopg_binary-3.2.10-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0738320a8d405f98743227ff70ed8fac9670870289435f4861dc640cef4a61d3"}, + {file = "psycopg_binary-3.2.10-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:89440355d1b163b11dc661ae64a5667578aab1b80bbf71ced90693d88e9863e1"}, + {file = "psycopg_binary-3.2.10-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3234605839e7d7584bd0a20716395eba34d368a5099dafe7896c943facac98fc"}, + {file = "psycopg_binary-3.2.10-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:725843fd444075cc6c9989f5b25ca83ac68d8d70b58e1f476fbb4096975e43cc"}, + {file = "psycopg_binary-3.2.10-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:447afc326cbc95ed67c0cd27606c0f81fa933b830061e096dbd37e08501cb3de"}, + {file = "psycopg_binary-3.2.10-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5334a61a00ccb722f0b28789e265c7a273cfd10d5a1ed6bf062686fbb71e7032"}, + {file = "psycopg_binary-3.2.10-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:183a59cbdcd7e156669577fd73a9e917b1ee664e620f1e31ae138d24c7714693"}, + {file = "psycopg_binary-3.2.10-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:8fa2efaf5e2f8c289a185c91c80a624a8f97aa17fbedcbc68f373d089b332afd"}, + {file = "psycopg_binary-3.2.10-cp39-cp39-win_amd64.whl", hash = "sha256:6220d6efd6e2df7b67d70ed60d653106cd3b70c5cb8cbe4e9f0a142a5db14015"}, +] + [[package]] name = "pyasn1" version = "0.6.1" @@ -1456,14 +1834,15 @@ pyasn1 = ">=0.6.1,<0.7.0" [[package]] name = "pycparser" -version = "2.22" +version = "2.23" description = "C parser in Python" optional = false python-versions = ">=3.8" groups = ["main"] +markers = "implementation_name != \"PyPy\"" files = [ - {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, - {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, + {file = "pycparser-2.23-py3-none-any.whl", hash = "sha256:e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934"}, + {file = "pycparser-2.23.tar.gz", hash = "sha256:78816d4f24add8f10a06d6f05b4d424ad9e96cfebf68a4ddc99c65c0720d00c2"}, ] [[package]] @@ -1570,21 +1949,21 @@ files = [ [[package]] name = "pydantic" -version = "2.11.5" +version = "2.12.0" description = "Data validation using Python type hints" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "pydantic-2.11.5-py3-none-any.whl", hash = "sha256:f9c26ba06f9747749ca1e5c94d6a85cb84254577553c8785576fd38fa64dc0f7"}, - {file = "pydantic-2.11.5.tar.gz", hash = "sha256:7f853db3d0ce78ce8bbb148c401c2cdd6431b3473c0cdff2755c7690952a7b7a"}, + {file = "pydantic-2.12.0-py3-none-any.whl", hash = "sha256:f6a1da352d42790537e95e83a8bdfb91c7efbae63ffd0b86fa823899e807116f"}, + {file = "pydantic-2.12.0.tar.gz", hash = "sha256:c1a077e6270dbfb37bfd8b498b3981e2bb18f68103720e51fa6c306a5a9af563"}, ] [package.dependencies] annotated-types = ">=0.6.0" -pydantic-core = "2.33.2" -typing-extensions = ">=4.12.2" -typing-inspection = ">=0.4.0" +pydantic-core = "2.41.1" +typing-extensions = ">=4.14.1" +typing-inspection = ">=0.4.2" [package.extras] email = ["email-validator (>=2.0.0)"] @@ -1592,115 +1971,144 @@ timezone = ["tzdata"] [[package]] name = "pydantic-core" -version = "2.33.2" +version = "2.41.1" description = "Core functionality for Pydantic validation and serialization" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "pydantic_core-2.33.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2b3d326aaef0c0399d9afffeb6367d5e26ddc24d351dbc9c636840ac355dc5d8"}, - {file = "pydantic_core-2.33.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e5b2671f05ba48b94cb90ce55d8bdcaaedb8ba00cc5359f6810fc918713983d"}, - {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0069c9acc3f3981b9ff4cdfaf088e98d83440a4c7ea1bc07460af3d4dc22e72d"}, - {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d53b22f2032c42eaaf025f7c40c2e3b94568ae077a606f006d206a463bc69572"}, - {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0405262705a123b7ce9f0b92f123334d67b70fd1f20a9372b907ce1080c7ba02"}, - {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4b25d91e288e2c4e0662b8038a28c6a07eaac3e196cfc4ff69de4ea3db992a1b"}, - {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bdfe4b3789761f3bcb4b1ddf33355a71079858958e3a552f16d5af19768fef2"}, - {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:efec8db3266b76ef9607c2c4c419bdb06bf335ae433b80816089ea7585816f6a"}, - {file = "pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:031c57d67ca86902726e0fae2214ce6770bbe2f710dc33063187a68744a5ecac"}, - {file = "pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:f8de619080e944347f5f20de29a975c2d815d9ddd8be9b9b7268e2e3ef68605a"}, - {file = "pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:73662edf539e72a9440129f231ed3757faab89630d291b784ca99237fb94db2b"}, - {file = "pydantic_core-2.33.2-cp310-cp310-win32.whl", hash = "sha256:0a39979dcbb70998b0e505fb1556a1d550a0781463ce84ebf915ba293ccb7e22"}, - {file = "pydantic_core-2.33.2-cp310-cp310-win_amd64.whl", hash = "sha256:b0379a2b24882fef529ec3b4987cb5d003b9cda32256024e6fe1586ac45fc640"}, - {file = "pydantic_core-2.33.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4c5b0a576fb381edd6d27f0a85915c6daf2f8138dc5c267a57c08a62900758c7"}, - {file = "pydantic_core-2.33.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e799c050df38a639db758c617ec771fd8fb7a5f8eaaa4b27b101f266b216a246"}, - {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc46a01bf8d62f227d5ecee74178ffc448ff4e5197c756331f71efcc66dc980f"}, - {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a144d4f717285c6d9234a66778059f33a89096dfb9b39117663fd8413d582dcc"}, - {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73cf6373c21bc80b2e0dc88444f41ae60b2f070ed02095754eb5a01df12256de"}, - {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3dc625f4aa79713512d1976fe9f0bc99f706a9dee21dfd1810b4bbbf228d0e8a"}, - {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b21b5549499972441da4758d662aeea93f1923f953e9cbaff14b8b9565aef"}, - {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bdc25f3681f7b78572699569514036afe3c243bc3059d3942624e936ec93450e"}, - {file = "pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fe5b32187cbc0c862ee201ad66c30cf218e5ed468ec8dc1cf49dec66e160cc4d"}, - {file = "pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:bc7aee6f634a6f4a95676fcb5d6559a2c2a390330098dba5e5a5f28a2e4ada30"}, - {file = "pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:235f45e5dbcccf6bd99f9f472858849f73d11120d76ea8707115415f8e5ebebf"}, - {file = "pydantic_core-2.33.2-cp311-cp311-win32.whl", hash = "sha256:6368900c2d3ef09b69cb0b913f9f8263b03786e5b2a387706c5afb66800efd51"}, - {file = "pydantic_core-2.33.2-cp311-cp311-win_amd64.whl", hash = "sha256:1e063337ef9e9820c77acc768546325ebe04ee38b08703244c1309cccc4f1bab"}, - {file = "pydantic_core-2.33.2-cp311-cp311-win_arm64.whl", hash = "sha256:6b99022f1d19bc32a4c2a0d544fc9a76e3be90f0b3f4af413f87d38749300e65"}, - {file = "pydantic_core-2.33.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a7ec89dc587667f22b6a0b6579c249fca9026ce7c333fc142ba42411fa243cdc"}, - {file = "pydantic_core-2.33.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3c6db6e52c6d70aa0d00d45cdb9b40f0433b96380071ea80b09277dba021ddf7"}, - {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e61206137cbc65e6d5256e1166f88331d3b6238e082d9f74613b9b765fb9025"}, - {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb8c529b2819c37140eb51b914153063d27ed88e3bdc31b71198a198e921e011"}, - {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c52b02ad8b4e2cf14ca7b3d918f3eb0ee91e63b3167c32591e57c4317e134f8f"}, - {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:96081f1605125ba0855dfda83f6f3df5ec90c61195421ba72223de35ccfb2f88"}, - {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f57a69461af2a5fa6e6bbd7a5f60d3b7e6cebb687f55106933188e79ad155c1"}, - {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:572c7e6c8bb4774d2ac88929e3d1f12bc45714ae5ee6d9a788a9fb35e60bb04b"}, - {file = "pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:db4b41f9bd95fbe5acd76d89920336ba96f03e149097365afe1cb092fceb89a1"}, - {file = "pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:fa854f5cf7e33842a892e5c73f45327760bc7bc516339fda888c75ae60edaeb6"}, - {file = "pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5f483cfb75ff703095c59e365360cb73e00185e01aaea067cd19acffd2ab20ea"}, - {file = "pydantic_core-2.33.2-cp312-cp312-win32.whl", hash = "sha256:9cb1da0f5a471435a7bc7e439b8a728e8b61e59784b2af70d7c169f8dd8ae290"}, - {file = "pydantic_core-2.33.2-cp312-cp312-win_amd64.whl", hash = "sha256:f941635f2a3d96b2973e867144fde513665c87f13fe0e193c158ac51bfaaa7b2"}, - {file = "pydantic_core-2.33.2-cp312-cp312-win_arm64.whl", hash = "sha256:cca3868ddfaccfbc4bfb1d608e2ccaaebe0ae628e1416aeb9c4d88c001bb45ab"}, - {file = "pydantic_core-2.33.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1082dd3e2d7109ad8b7da48e1d4710c8d06c253cbc4a27c1cff4fbcaa97a9e3f"}, - {file = "pydantic_core-2.33.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f517ca031dfc037a9c07e748cefd8d96235088b83b4f4ba8939105d20fa1dcd6"}, - {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a9f2c9dd19656823cb8250b0724ee9c60a82f3cdf68a080979d13092a3b0fef"}, - {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b0a451c263b01acebe51895bfb0e1cc842a5c666efe06cdf13846c7418caa9a"}, - {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ea40a64d23faa25e62a70ad163571c0b342b8bf66d5fa612ac0dec4f069d916"}, - {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb2d542b4d66f9470e8065c5469ec676978d625a8b7a363f07d9a501a9cb36a"}, - {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdac5d6ffa1b5a83bca06ffe7583f5576555e6c8b3a91fbd25ea7780f825f7d"}, - {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04a1a413977ab517154eebb2d326da71638271477d6ad87a769102f7c2488c56"}, - {file = "pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c8e7af2f4e0194c22b5b37205bfb293d166a7344a5b0d0eaccebc376546d77d5"}, - {file = "pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:5c92edd15cd58b3c2d34873597a1e20f13094f59cf88068adb18947df5455b4e"}, - {file = "pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:65132b7b4a1c0beded5e057324b7e16e10910c106d43675d9bd87d4f38dde162"}, - {file = "pydantic_core-2.33.2-cp313-cp313-win32.whl", hash = "sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849"}, - {file = "pydantic_core-2.33.2-cp313-cp313-win_amd64.whl", hash = "sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9"}, - {file = "pydantic_core-2.33.2-cp313-cp313-win_arm64.whl", hash = "sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9"}, - {file = "pydantic_core-2.33.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac"}, - {file = "pydantic_core-2.33.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5"}, - {file = "pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9"}, - {file = "pydantic_core-2.33.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a2b911a5b90e0374d03813674bf0a5fbbb7741570dcd4b4e85a2e48d17def29d"}, - {file = "pydantic_core-2.33.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6fa6dfc3e4d1f734a34710f391ae822e0a8eb8559a85c6979e14e65ee6ba2954"}, - {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c54c939ee22dc8e2d545da79fc5381f1c020d6d3141d3bd747eab59164dc89fb"}, - {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:53a57d2ed685940a504248187d5685e49eb5eef0f696853647bf37c418c538f7"}, - {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09fb9dd6571aacd023fe6aaca316bd01cf60ab27240d7eb39ebd66a3a15293b4"}, - {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0e6116757f7959a712db11f3e9c0a99ade00a5bbedae83cb801985aa154f071b"}, - {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d55ab81c57b8ff8548c3e4947f119551253f4e3787a7bbc0b6b3ca47498a9d3"}, - {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c20c462aa4434b33a2661701b861604913f912254e441ab8d78d30485736115a"}, - {file = "pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:44857c3227d3fb5e753d5fe4a3420d6376fa594b07b621e220cd93703fe21782"}, - {file = "pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:eb9b459ca4df0e5c87deb59d37377461a538852765293f9e6ee834f0435a93b9"}, - {file = "pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9fcd347d2cc5c23b06de6d3b7b8275be558a0c90549495c699e379a80bf8379e"}, - {file = "pydantic_core-2.33.2-cp39-cp39-win32.whl", hash = "sha256:83aa99b1285bc8f038941ddf598501a86f1536789740991d7d8756e34f1e74d9"}, - {file = "pydantic_core-2.33.2-cp39-cp39-win_amd64.whl", hash = "sha256:f481959862f57f29601ccced557cc2e817bce7533ab8e01a797a48b49c9692b3"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5c4aa4e82353f65e548c476b37e64189783aa5384903bfea4f41580f255fddfa"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d946c8bf0d5c24bf4fe333af284c59a19358aa3ec18cb3dc4370080da1e8ad29"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87b31b6846e361ef83fedb187bb5b4372d0da3f7e28d85415efa92d6125d6e6d"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa9d91b338f2df0508606f7009fde642391425189bba6d8c653afd80fd6bb64e"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2058a32994f1fde4ca0480ab9d1e75a0e8c87c22b53a3ae66554f9af78f2fe8c"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:0e03262ab796d986f978f79c943fc5f620381be7287148b8010b4097f79a39ec"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:1a8695a8d00c73e50bff9dfda4d540b7dee29ff9b8053e38380426a85ef10052"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:fa754d1850735a0b0e03bcffd9d4b4343eb417e47196e4485d9cca326073a42c"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a11c8d26a50bfab49002947d3d237abe4d9e4b5bdc8846a63537b6488e197808"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:dd14041875d09cc0f9308e37a6f8b65f5585cf2598a53aa0123df8b129d481f8"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d87c561733f66531dced0da6e864f44ebf89a8fba55f31407b00c2f7f9449593"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f82865531efd18d6e07a04a17331af02cb7a651583c418df8266f17a63c6612"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bfb5112df54209d820d7bf9317c7a6c9025ea52e49f46b6a2060104bba37de7"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:64632ff9d614e5eecfb495796ad51b0ed98c453e447a76bcbeeb69615079fc7e"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:f889f7a40498cc077332c7ab6b4608d296d852182211787d4f3ee377aaae66e8"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:de4b83bb311557e439b9e186f733f6c645b9417c84e2eb8203f3f820a4b988bf"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:82f68293f055f51b51ea42fafc74b6aad03e70e191799430b90c13d643059ebb"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:329467cecfb529c925cf2bbd4d60d2c509bc2fb52a20c1045bf09bb70971a9c1"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:87acbfcf8e90ca885206e98359d7dca4bcbb35abdc0ff66672a293e1d7a19101"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:7f92c15cd1e97d4b12acd1cc9004fa092578acfa57b67ad5e43a197175d01a64"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3f26877a748dc4251cfcfda9dfb5f13fcb034f5308388066bcfe9031b63ae7d"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dac89aea9af8cd672fa7b510e7b8c33b0bba9a43186680550ccf23020f32d535"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:970919794d126ba8645f3837ab6046fb4e72bbc057b3709144066204c19a455d"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:3eb3fe62804e8f859c49ed20a8451342de53ed764150cb14ca71357c765dc2a6"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:3abcd9392a36025e3bd55f9bd38d908bd17962cc49bc6da8e7e96285336e2bca"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:3a1c81334778f9e3af2f8aeb7a960736e5cab1dfebfb26aabca09afd2906c039"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2807668ba86cb38c6817ad9bc66215ab8584d1d304030ce4f0887336f28a5e27"}, - {file = "pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc"}, + {file = "pydantic_core-2.41.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:e63036298322e9aea1c8b7c0a6c1204d615dbf6ec0668ce5b83ff27f07404a61"}, + {file = "pydantic_core-2.41.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:241299ca91fc77ef64f11ed909d2d9220a01834e8e6f8de61275c4dd16b7c936"}, + {file = "pydantic_core-2.41.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ab7e594a2a5c24ab8013a7dc8cfe5f2260e80e490685814122081705c2cf2b0"}, + {file = "pydantic_core-2.41.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b054ef1a78519cb934b58e9c90c09e93b837c935dcd907b891f2b265b129eb6e"}, + {file = "pydantic_core-2.41.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f2ab7d10d0ab2ed6da54c757233eb0f48ebfb4f86e9b88ccecb3f92bbd61a538"}, + {file = "pydantic_core-2.41.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2757606b7948bb853a27e4040820306eaa0ccb9e8f9f8a0fa40cb674e170f350"}, + {file = "pydantic_core-2.41.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cec0e75eb61f606bad0a32f2be87507087514e26e8c73db6cbdb8371ccd27917"}, + {file = "pydantic_core-2.41.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0234236514f44a5bf552105cfe2543a12f48203397d9d0f866affa569345a5b5"}, + {file = "pydantic_core-2.41.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:1b974e41adfbb4ebb0f65fc4ca951347b17463d60893ba7d5f7b9bb087c83897"}, + {file = "pydantic_core-2.41.1-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:248dafb3204136113c383e91a4d815269f51562b6659b756cf3df14eefc7d0bb"}, + {file = "pydantic_core-2.41.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:678f9d76a91d6bcedd7568bbf6beb77ae8447f85d1aeebaab7e2f0829cfc3a13"}, + {file = "pydantic_core-2.41.1-cp310-cp310-win32.whl", hash = "sha256:dff5bee1d21ee58277900692a641925d2dddfde65182c972569b1a276d2ac8fb"}, + {file = "pydantic_core-2.41.1-cp310-cp310-win_amd64.whl", hash = "sha256:5042da12e5d97d215f91567110fdfa2e2595a25f17c19b9ff024f31c34f9b53e"}, + {file = "pydantic_core-2.41.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4f276a6134fe1fc1daa692642a3eaa2b7b858599c49a7610816388f5e37566a1"}, + {file = "pydantic_core-2.41.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:07588570a805296ece009c59d9a679dc08fab72fb337365afb4f3a14cfbfc176"}, + {file = "pydantic_core-2.41.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28527e4b53400cd60ffbd9812ccb2b5135d042129716d71afd7e45bf42b855c0"}, + {file = "pydantic_core-2.41.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:46a1c935c9228bad738c8a41de06478770927baedf581d172494ab36a6b96575"}, + {file = "pydantic_core-2.41.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:447ddf56e2b7d28d200d3e9eafa936fe40485744b5a824b67039937580b3cb20"}, + {file = "pydantic_core-2.41.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:63892ead40c1160ac860b5debcc95c95c5a0035e543a8b5a4eac70dd22e995f4"}, + {file = "pydantic_core-2.41.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f4a9543ca355e6df8fbe9c83e9faab707701e9103ae857ecb40f1c0cf8b0e94d"}, + {file = "pydantic_core-2.41.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f2611bdb694116c31e551ed82e20e39a90bea9b7ad9e54aaf2d045ad621aa7a1"}, + {file = "pydantic_core-2.41.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fecc130893a9b5f7bfe230be1bb8c61fe66a19db8ab704f808cb25a82aad0bc9"}, + {file = "pydantic_core-2.41.1-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:1e2df5f8344c99b6ea5219f00fdc8950b8e6f2c422fbc1cc122ec8641fac85a1"}, + {file = "pydantic_core-2.41.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:35291331e9d8ed94c257bab6be1cb3a380b5eee570a2784bffc055e18040a2ea"}, + {file = "pydantic_core-2.41.1-cp311-cp311-win32.whl", hash = "sha256:2876a095292668d753f1a868c4a57c4ac9f6acbd8edda8debe4218d5848cf42f"}, + {file = "pydantic_core-2.41.1-cp311-cp311-win_amd64.whl", hash = "sha256:b92d6c628e9a338846a28dfe3fcdc1a3279388624597898b105e078cdfc59298"}, + {file = "pydantic_core-2.41.1-cp311-cp311-win_arm64.whl", hash = "sha256:7d82ae99409eb69d507a89835488fb657faa03ff9968a9379567b0d2e2e56bc5"}, + {file = "pydantic_core-2.41.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:db2f82c0ccbce8f021ad304ce35cbe02aa2f95f215cac388eed542b03b4d5eb4"}, + {file = "pydantic_core-2.41.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:47694a31c710ced9205d5f1e7e8af3ca57cbb8a503d98cb9e33e27c97a501601"}, + {file = "pydantic_core-2.41.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:93e9decce94daf47baf9e9d392f5f2557e783085f7c5e522011545d9d6858e00"}, + {file = "pydantic_core-2.41.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ab0adafdf2b89c8b84f847780a119437a0931eca469f7b44d356f2b426dd9741"}, + {file = "pydantic_core-2.41.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5da98cc81873f39fd56882e1569c4677940fbc12bce6213fad1ead784192d7c8"}, + {file = "pydantic_core-2.41.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:209910e88afb01fd0fd403947b809ba8dba0e08a095e1f703294fda0a8fdca51"}, + {file = "pydantic_core-2.41.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:365109d1165d78d98e33c5bfd815a9b5d7d070f578caefaabcc5771825b4ecb5"}, + {file = "pydantic_core-2.41.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:706abf21e60a2857acdb09502bc853ee5bce732955e7b723b10311114f033115"}, + {file = "pydantic_core-2.41.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:bf0bd5417acf7f6a7ec3b53f2109f587be176cb35f9cf016da87e6017437a72d"}, + {file = "pydantic_core-2.41.1-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:2e71b1c6ceb9c78424ae9f63a07292fb769fb890a4e7efca5554c47f33a60ea5"}, + {file = "pydantic_core-2.41.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:80745b9770b4a38c25015b517451c817799bfb9d6499b0d13d8227ec941cb513"}, + {file = "pydantic_core-2.41.1-cp312-cp312-win32.whl", hash = "sha256:83b64d70520e7890453f1aa21d66fda44e7b35f1cfea95adf7b4289a51e2b479"}, + {file = "pydantic_core-2.41.1-cp312-cp312-win_amd64.whl", hash = "sha256:377defd66ee2003748ee93c52bcef2d14fde48fe28a0b156f88c3dbf9bc49a50"}, + {file = "pydantic_core-2.41.1-cp312-cp312-win_arm64.whl", hash = "sha256:c95caff279d49c1d6cdfe2996e6c2ad712571d3b9caaa209a404426c326c4bde"}, + {file = "pydantic_core-2.41.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:70e790fce5f05204ef4403159857bfcd587779da78627b0babb3654f75361ebf"}, + {file = "pydantic_core-2.41.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9cebf1ca35f10930612d60bd0f78adfacee824c30a880e3534ba02c207cceceb"}, + {file = "pydantic_core-2.41.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:170406a37a5bc82c22c3274616bf6f17cc7df9c4a0a0a50449e559cb755db669"}, + {file = "pydantic_core-2.41.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:12d4257fc9187a0ccd41b8b327d6a4e57281ab75e11dda66a9148ef2e1fb712f"}, + {file = "pydantic_core-2.41.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a75a33b4db105dd1c8d57839e17ee12db8d5ad18209e792fa325dbb4baeb00f4"}, + {file = "pydantic_core-2.41.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:08a589f850803a74e0fcb16a72081cafb0d72a3cdda500106942b07e76b7bf62"}, + {file = "pydantic_core-2.41.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a97939d6ea44763c456bd8a617ceada2c9b96bb5b8ab3dfa0d0827df7619014"}, + {file = "pydantic_core-2.41.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d2ae423c65c556f09569524b80ffd11babff61f33055ef9773d7c9fabc11ed8d"}, + {file = "pydantic_core-2.41.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:4dc703015fbf8764d6a8001c327a87f1823b7328d40b47ce6000c65918ad2b4f"}, + {file = "pydantic_core-2.41.1-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:968e4ffdfd35698a5fe659e5e44c508b53664870a8e61c8f9d24d3d145d30257"}, + {file = "pydantic_core-2.41.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:fff2b76c8e172d34771cd4d4f0ade08072385310f214f823b5a6ad4006890d32"}, + {file = "pydantic_core-2.41.1-cp313-cp313-win32.whl", hash = "sha256:a38a5263185407ceb599f2f035faf4589d57e73c7146d64f10577f6449e8171d"}, + {file = "pydantic_core-2.41.1-cp313-cp313-win_amd64.whl", hash = "sha256:b42ae7fd6760782c975897e1fdc810f483b021b32245b0105d40f6e7a3803e4b"}, + {file = "pydantic_core-2.41.1-cp313-cp313-win_arm64.whl", hash = "sha256:ad4111acc63b7384e205c27a2f15e23ac0ee21a9d77ad6f2e9cb516ec90965fb"}, + {file = "pydantic_core-2.41.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:440d0df7415b50084a4ba9d870480c16c5f67c0d1d4d5119e3f70925533a0edc"}, + {file = "pydantic_core-2.41.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:71eaa38d342099405dae6484216dcf1e8e4b0bebd9b44a4e08c9b43db6a2ab67"}, + {file = "pydantic_core-2.41.1-cp313-cp313t-win_amd64.whl", hash = "sha256:555ecf7e50f1161d3f693bc49f23c82cf6cdeafc71fa37a06120772a09a38795"}, + {file = "pydantic_core-2.41.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:05226894a26f6f27e1deb735d7308f74ef5fa3a6de3e0135bb66cdcaee88f64b"}, + {file = "pydantic_core-2.41.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:85ff7911c6c3e2fd8d3779c50925f6406d770ea58ea6dde9c230d35b52b16b4a"}, + {file = "pydantic_core-2.41.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47f1f642a205687d59b52dc1a9a607f45e588f5a2e9eeae05edd80c7a8c47674"}, + {file = "pydantic_core-2.41.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:df11c24e138876ace5ec6043e5cae925e34cf38af1a1b3d63589e8f7b5f5cdc4"}, + {file = "pydantic_core-2.41.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7f0bf7f5c8f7bf345c527e8a0d72d6b26eda99c1227b0c34e7e59e181260de31"}, + {file = "pydantic_core-2.41.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:82b887a711d341c2c47352375d73b029418f55b20bd7815446d175a70effa706"}, + {file = "pydantic_core-2.41.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b5f1d5d6bbba484bdf220c72d8ecd0be460f4bd4c5e534a541bb2cd57589fb8b"}, + {file = "pydantic_core-2.41.1-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2bf1917385ebe0f968dc5c6ab1375886d56992b93ddfe6bf52bff575d03662be"}, + {file = "pydantic_core-2.41.1-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:4f94f3ab188f44b9a73f7295663f3ecb8f2e2dd03a69c8f2ead50d37785ecb04"}, + {file = "pydantic_core-2.41.1-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:3925446673641d37c30bd84a9d597e49f72eacee8b43322c8999fa17d5ae5bc4"}, + {file = "pydantic_core-2.41.1-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:49bd51cc27adb980c7b97357ae036ce9b3c4d0bb406e84fbe16fb2d368b602a8"}, + {file = "pydantic_core-2.41.1-cp314-cp314-win32.whl", hash = "sha256:a31ca0cd0e4d12ea0df0077df2d487fc3eb9d7f96bbb13c3c5b88dcc21d05159"}, + {file = "pydantic_core-2.41.1-cp314-cp314-win_amd64.whl", hash = "sha256:1b5c4374a152e10a22175d7790e644fbd8ff58418890e07e2073ff9d4414efae"}, + {file = "pydantic_core-2.41.1-cp314-cp314-win_arm64.whl", hash = "sha256:4fee76d757639b493eb600fba668f1e17475af34c17dd61db7a47e824d464ca9"}, + {file = "pydantic_core-2.41.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f9b9c968cfe5cd576fdd7361f47f27adeb120517e637d1b189eea1c3ece573f4"}, + {file = "pydantic_core-2.41.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1ebc7ab67b856384aba09ed74e3e977dded40e693de18a4f197c67d0d4e6d8e"}, + {file = "pydantic_core-2.41.1-cp314-cp314t-win_amd64.whl", hash = "sha256:8ae0dc57b62a762985bc7fbf636be3412394acc0ddb4ade07fe104230f1b9762"}, + {file = "pydantic_core-2.41.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:10ce489cf09a4956a1549af839b983edc59b0f60e1b068c21b10154e58f54f80"}, + {file = "pydantic_core-2.41.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ff548c908caffd9455fd1342366bcf8a1ec8a3fca42f35c7fc60883d6a901074"}, + {file = "pydantic_core-2.41.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d43bf082025082bda13be89a5f876cc2386b7727c7b322be2d2b706a45cea8e"}, + {file = "pydantic_core-2.41.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:666aee751faf1c6864b2db795775dd67b61fdcf646abefa309ed1da039a97209"}, + {file = "pydantic_core-2.41.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b83aaeff0d7bde852c32e856f3ee410842ebc08bc55c510771d87dcd1c01e1ed"}, + {file = "pydantic_core-2.41.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:055c7931b0329cb8acde20cdde6d9c2cbc2a02a0a8e54a792cddd91e2ea92c65"}, + {file = "pydantic_core-2.41.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:530bbb1347e3e5ca13a91ac087c4971d7da09630ef8febd27a20a10800c2d06d"}, + {file = "pydantic_core-2.41.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:65a0ea16cfea7bfa9e43604c8bd726e63a3788b61c384c37664b55209fcb1d74"}, + {file = "pydantic_core-2.41.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:8fa93fadff794c6d15c345c560513b160197342275c6d104cc879f932b978afc"}, + {file = "pydantic_core-2.41.1-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:c8a1af9ac51969a494c6a82b563abae6859dc082d3b999e8fa7ba5ee1b05e8e8"}, + {file = "pydantic_core-2.41.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:30edab28829703f876897c9471a857e43d847b8799c3c9e2fbce644724b50aa4"}, + {file = "pydantic_core-2.41.1-cp39-cp39-win32.whl", hash = "sha256:84d0ff869f98be2e93efdf1ae31e5a15f0926d22af8677d51676e373abbfe57a"}, + {file = "pydantic_core-2.41.1-cp39-cp39-win_amd64.whl", hash = "sha256:b5674314987cdde5a5511b029fa5fb1556b3d147a367e01dd583b19cfa8e35df"}, + {file = "pydantic_core-2.41.1-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:68f2251559b8efa99041bb63571ec7cdd2d715ba74cc82b3bc9eff824ebc8bf0"}, + {file = "pydantic_core-2.41.1-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:c7bc140c596097cb53b30546ca257dbe3f19282283190b1b5142928e5d5d3a20"}, + {file = "pydantic_core-2.41.1-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2896510fce8f4725ec518f8b9d7f015a00db249d2fd40788f442af303480063d"}, + {file = "pydantic_core-2.41.1-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ced20e62cfa0f496ba68fa5d6c7ee71114ea67e2a5da3114d6450d7f4683572a"}, + {file = "pydantic_core-2.41.1-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:b04fa9ed049461a7398138c604b00550bc89e3e1151d84b81ad6dc93e39c4c06"}, + {file = "pydantic_core-2.41.1-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:b3b7d9cfbfdc43c80a16638c6dc2768e3956e73031fca64e8e1a3ae744d1faeb"}, + {file = "pydantic_core-2.41.1-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eec83fc6abef04c7f9bec616e2d76ee9a6a4ae2a359b10c21d0f680e24a247ca"}, + {file = "pydantic_core-2.41.1-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6771a2d9f83c4038dfad5970a3eef215940682b2175e32bcc817bdc639019b28"}, + {file = "pydantic_core-2.41.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:fabcbdb12de6eada8d6e9a759097adb3c15440fafc675b3e94ae5c9cb8d678a0"}, + {file = "pydantic_core-2.41.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:80e97ccfaf0aaf67d55de5085b0ed0d994f57747d9d03f2de5cc9847ca737b08"}, + {file = "pydantic_core-2.41.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34df1fe8fea5d332484a763702e8b6a54048a9d4fe6ccf41e34a128238e01f52"}, + {file = "pydantic_core-2.41.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:421b5595f845842fc093f7250e24ee395f54ca62d494fdde96f43ecf9228ae01"}, + {file = "pydantic_core-2.41.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:dce8b22663c134583aaad24827863306a933f576c79da450be3984924e2031d1"}, + {file = "pydantic_core-2.41.1-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:300a9c162fea9906cc5c103893ca2602afd84f0ec90d3be36f4cc360125d22e1"}, + {file = "pydantic_core-2.41.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e019167628f6e6161ae7ab9fb70f6d076a0bf0d55aa9b20833f86a320c70dd65"}, + {file = "pydantic_core-2.41.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:13ab9cc2de6f9d4ab645a050ae5aee61a2424ac4d3a16ba23d4c2027705e0301"}, + {file = "pydantic_core-2.41.1-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:af2385d3f98243fb733862f806c5bb9122e5fba05b373e3af40e3c82d711cef1"}, + {file = "pydantic_core-2.41.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:6550617a0c2115be56f90c31a5370261d8ce9dbf051c3ed53b51172dd34da696"}, + {file = "pydantic_core-2.41.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc17b6ecf4983d298686014c92ebc955a9f9baf9f57dad4065e7906e7bee6222"}, + {file = "pydantic_core-2.41.1-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:42ae9352cf211f08b04ea110563d6b1e415878eea5b4c70f6bdb17dca3b932d2"}, + {file = "pydantic_core-2.41.1-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:e82947de92068b0a21681a13dd2102387197092fbe7defcfb8453e0913866506"}, + {file = "pydantic_core-2.41.1-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:e244c37d5471c9acdcd282890c6c4c83747b77238bfa19429b8473586c907656"}, + {file = "pydantic_core-2.41.1-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:1e798b4b304a995110d41ec93653e57975620ccb2842ba9420037985e7d7284e"}, + {file = "pydantic_core-2.41.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:f1fc716c0eb1663c59699b024428ad5ec2bcc6b928527b8fe28de6cb89f47efb"}, + {file = "pydantic_core-2.41.1.tar.gz", hash = "sha256:1ad375859a6d8c356b7704ec0f547a58e82ee80bb41baa811ad710e124bc8f2f"}, ] [package.dependencies] -typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" +typing-extensions = ">=4.14.1" + +[[package]] +name = "pygments" +version = "2.19.2" +description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=3.8" +groups = ["main", "dev"] +files = [ + {file = "pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b"}, + {file = "pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887"}, +] + +[package.extras] +windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pyopenssl" @@ -1737,6 +2145,48 @@ files = [ [package.extras] dev = ["build", "flake8", "mypy", "pytest", "twine"] +[[package]] +name = "pytest" +version = "8.4.2" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.9" +groups = ["main", "dev"] +files = [ + {file = "pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79"}, + {file = "pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01"}, +] + +[package.dependencies] +colorama = {version = ">=0.4", markers = "sys_platform == \"win32\""} +iniconfig = ">=1" +packaging = ">=20" +pluggy = ">=1.5,<2" +pygments = ">=2.7.2" + +[package.extras] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "pytest-asyncio" +version = "1.2.0" +description = "Pytest support for asyncio" +optional = false +python-versions = ">=3.9" +groups = ["main", "dev"] +files = [ + {file = "pytest_asyncio-1.2.0-py3-none-any.whl", hash = "sha256:8e17ae5e46d8e7efe51ab6494dd2010f4ca8dae51652aa3c8d55acf50bfb2e99"}, + {file = "pytest_asyncio-1.2.0.tar.gz", hash = "sha256:c609a64a2a8768462d0c99811ddb8bd2583c33fd33cf7f21af1c142e824ffb57"}, +] + +[package.dependencies] +pytest = ">=8.2,<9" +typing-extensions = {version = ">=4.12", markers = "python_version < \"3.13\""} + +[package.extras] +docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1)"] +testing = ["coverage (>=6.2)", "hypothesis (>=5.7.1)"] + [[package]] name = "python-dateutil" version = "2.9.0.post0" @@ -1752,67 +2202,104 @@ files = [ [package.dependencies] six = ">=1.5" +[[package]] +name = "python-registry" +version = "1.3.1" +description = "Read access to Windows Registry files." +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "python-registry-1.3.1.tar.gz", hash = "sha256:99185f67d5601be3e7843e55902d5769aea1740869b0882f34ff1bd4b43b1eb2"}, + {file = "python_registry-1.3.1-py2-none-any.whl", hash = "sha256:59d3b00c04bca0c4e1a12be0404da6ccf76b87537ee3a3ad2d8fc1bccf6f63ca"}, + {file = "python_registry-1.3.1-py3-none-any.whl", hash = "sha256:b5b8ae07c271dce12dacd24e16af8aa8d56167ebdb360112a4f152b6d04a4ca9"}, +] + +[package.dependencies] +enum-compat = "*" +unicodecsv = "*" + [[package]] name = "pyyaml" -version = "6.0.2" +version = "6.0.3" description = "YAML parser and emitter for Python" optional = false python-versions = ">=3.8" groups = ["main"] files = [ - {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, - {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, - {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237"}, - {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b"}, - {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed"}, - {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180"}, - {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68"}, - {file = "PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99"}, - {file = "PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e"}, - {file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"}, - {file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"}, - {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c"}, - {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317"}, - {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85"}, - {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"}, - {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e"}, - {file = "PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5"}, - {file = "PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44"}, - {file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"}, - {file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"}, - {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5"}, - {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425"}, - {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476"}, - {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48"}, - {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b"}, - {file = "PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4"}, - {file = "PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8"}, - {file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"}, - {file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"}, - {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"}, - {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"}, - {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"}, - {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"}, - {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"}, - {file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"}, - {file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"}, - {file = "PyYAML-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a"}, - {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5"}, - {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d"}, - {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083"}, - {file = "PyYAML-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706"}, - {file = "PyYAML-6.0.2-cp38-cp38-win32.whl", hash = "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a"}, - {file = "PyYAML-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff"}, - {file = "PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d"}, - {file = "PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f"}, - {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290"}, - {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12"}, - {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19"}, - {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e"}, - {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725"}, - {file = "PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631"}, - {file = "PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8"}, - {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"}, + {file = "PyYAML-6.0.3-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:c2514fceb77bc5e7a2f7adfaa1feb2fb311607c9cb518dbc378688ec73d8292f"}, + {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c57bb8c96f6d1808c030b1687b9b5fb476abaa47f0db9c0101f5e9f394e97f4"}, + {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:efd7b85f94a6f21e4932043973a7ba2613b059c4a000551892ac9f1d11f5baf3"}, + {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22ba7cfcad58ef3ecddc7ed1db3409af68d023b7f940da23c6c2a1890976eda6"}, + {file = "PyYAML-6.0.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:6344df0d5755a2c9a276d4473ae6b90647e216ab4757f8426893b5dd2ac3f369"}, + {file = "PyYAML-6.0.3-cp38-cp38-win32.whl", hash = "sha256:3ff07ec89bae51176c0549bc4c63aa6202991da2d9a6129d7aef7f1407d3f295"}, + {file = "PyYAML-6.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:5cf4e27da7e3fbed4d6c3d8e797387aaad68102272f8f9752883bc32d61cb87b"}, + {file = "pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b"}, + {file = "pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956"}, + {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8"}, + {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198"}, + {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b"}, + {file = "pyyaml-6.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0"}, + {file = "pyyaml-6.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69"}, + {file = "pyyaml-6.0.3-cp310-cp310-win32.whl", hash = "sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e"}, + {file = "pyyaml-6.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c"}, + {file = "pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e"}, + {file = "pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824"}, + {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c"}, + {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00"}, + {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d"}, + {file = "pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a"}, + {file = "pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4"}, + {file = "pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b"}, + {file = "pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf"}, + {file = "pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196"}, + {file = "pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0"}, + {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28"}, + {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c"}, + {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc"}, + {file = "pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e"}, + {file = "pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea"}, + {file = "pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5"}, + {file = "pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b"}, + {file = "pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd"}, + {file = "pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8"}, + {file = "pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1"}, + {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c"}, + {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5"}, + {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6"}, + {file = "pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6"}, + {file = "pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be"}, + {file = "pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26"}, + {file = "pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c"}, + {file = "pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb"}, + {file = "pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac"}, + {file = "pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310"}, + {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7"}, + {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788"}, + {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5"}, + {file = "pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764"}, + {file = "pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35"}, + {file = "pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac"}, + {file = "pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3"}, + {file = "pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3"}, + {file = "pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba"}, + {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c"}, + {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702"}, + {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c"}, + {file = "pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065"}, + {file = "pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65"}, + {file = "pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9"}, + {file = "pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b"}, + {file = "pyyaml-6.0.3-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:b865addae83924361678b652338317d1bd7e79b1f4596f96b96c77a5a34b34da"}, + {file = "pyyaml-6.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c3355370a2c156cffb25e876646f149d5d68f5e0a3ce86a5084dd0b64a994917"}, + {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3c5677e12444c15717b902a5798264fa7909e41153cdf9ef7ad571b704a63dd9"}, + {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5ed875a24292240029e4483f9d4a4b8a1ae08843b9c54f43fcc11e404532a8a5"}, + {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0150219816b6a1fa26fb4699fb7daa9caf09eb1999f3b70fb6e786805e80375a"}, + {file = "pyyaml-6.0.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:fa160448684b4e94d80416c0fa4aac48967a969efe22931448d853ada8baf926"}, + {file = "pyyaml-6.0.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:27c0abcb4a5dac13684a37f76e701e054692a9b2d3064b70f5e4eb54810553d7"}, + {file = "pyyaml-6.0.3-cp39-cp39-win32.whl", hash = "sha256:1ebe39cb5fc479422b83de611d14e2c0d3bb2a18bbcb01f229ab3cfbd8fee7a0"}, + {file = "pyyaml-6.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:2e71d11abed7344e42a8849600193d15b6def118602c4c176f748e4583246007"}, + {file = "pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f"}, ] [[package]] @@ -1920,26 +2407,27 @@ files = [ [[package]] name = "typing-extensions" -version = "4.14.0" +version = "4.15.0" description = "Backported and Experimental Type Hints for Python 3.9+" optional = false python-versions = ">=3.9" -groups = ["main"] +groups = ["main", "dev"] files = [ - {file = "typing_extensions-4.14.0-py3-none-any.whl", hash = "sha256:a1514509136dd0b477638fc68d6a91497af5076466ad0fa6c338e44e359944af"}, - {file = "typing_extensions-4.14.0.tar.gz", hash = "sha256:8676b788e32f02ab42d9e7c61324048ae4c6d844a399eebace3d4979d75ceef4"}, + {file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"}, + {file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"}, ] +markers = {dev = "python_version < \"3.13\""} [[package]] name = "typing-inspection" -version = "0.4.1" +version = "0.4.2" description = "Runtime typing introspection tools" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "typing_inspection-0.4.1-py3-none-any.whl", hash = "sha256:389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51"}, - {file = "typing_inspection-0.4.1.tar.gz", hash = "sha256:6ae134cc0203c33377d43188d4064e9b357dba58cff3185f22924610e70a9d28"}, + {file = "typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7"}, + {file = "typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464"}, ] [package.dependencies] @@ -1958,16 +2446,27 @@ files = [ {file = "tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9"}, ] +[[package]] +name = "unicodecsv" +version = "0.14.1" +description = "Python2's stdlib csv module is nice, but it doesn't support unicode. This module is a drop-in replacement which *does*." +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "unicodecsv-0.14.1.tar.gz", hash = "sha256:018c08037d48649a0412063ff4eda26eaa81eff1546dbffa51fa5293276ff7fc"}, +] + [[package]] name = "urllib3" -version = "2.4.0" +version = "2.5.0" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "urllib3-2.4.0-py3-none-any.whl", hash = "sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813"}, - {file = "urllib3-2.4.0.tar.gz", hash = "sha256:414bc6535b787febd7567804cc015fee39daab8ad86268f1310a9250697de466"}, + {file = "urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc"}, + {file = "urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760"}, ] [package.extras] @@ -1996,139 +2495,162 @@ watchdog = ["watchdog (>=2.3)"] [[package]] name = "yara-x" -version = "1.0.1" +version = "1.8.1" description = "Python bindings for YARA-X" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "yara_x-1.0.1-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:ebc95204932a9d17da7794dd2058462a8d2d0e63c6ec2a42338835d236b185a3"}, - {file = "yara_x-1.0.1-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:bfa419ac1202bcffb2c8c76feb2206a623d841b68e781bc92b0907a7d9ba951f"}, - {file = "yara_x-1.0.1-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e737715a68612c8c028200001e87fe82a167053b0a87e81d886434997838aabf"}, - {file = "yara_x-1.0.1-cp38-abi3-win_amd64.whl", hash = "sha256:c9e4ae8a8e223f543b9f1c58f20a73e7d6067d93219726cc26fb63298eaf73d9"}, - {file = "yara_x-1.0.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:9157737907cc99f11d058700a6b37895dcc67d2a723c23126eadd51f52a2603b"}, - {file = "yara_x-1.0.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:8ab1c36c787912b6f61ec4ff686004fea9ce3ef6e98db9d6666eec3c952ba2f6"}, - {file = "yara_x-1.0.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:655f9de34b5a948dbb0063724af5276451c1a6cf91c03e92e0c0d4acf54c042b"}, - {file = "yara_x-1.0.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:fd870ac612e4cb08a528fa756a54ad27072e6319eae916ebc9f80c53e0053502"}, - {file = "yara_x-1.0.1-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:1076b4e426cf884c7615bc705930bd1d9c109c5ddb6b68f4d6302af0646b6094"}, - {file = "yara_x-1.0.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:38b080c493c69ff4d61f3dfcb59c0dc17987f68cc2731d35e887566fe898a10b"}, - {file = "yara_x-1.0.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f10206dc27246f80c6225df39eda261e90c8cec9e426c68dc76ed8c3d3126fc"}, - {file = "yara_x-1.0.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:adf8fa723eaaf0f15c7532b5913e430f9b0dab12cbf5650e45daec2ed0af1d9b"}, - {file = "yara_x-1.0.1.tar.gz", hash = "sha256:0563f1569672c8f125f21b15ab1975cddd12f3e1a462be0e1766911a98e8f88d"}, + {file = "yara_x-1.8.1-cp38-abi3-macosx_14_0_arm64.whl", hash = "sha256:dbb1fd289f24a05c113b8f0713d4750331cb9c39722d212fea108b8aa69c8594"}, + {file = "yara_x-1.8.1-cp38-abi3-macosx_14_0_x86_64.whl", hash = "sha256:b41f4c4b9326905d584b38cf84d52037eb6864e70154844db4f59672107a5a1a"}, + {file = "yara_x-1.8.1-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:bcee1686b937fd8d75df3faae354e45c64256561b384bdf97ff79be32770c7a8"}, + {file = "yara_x-1.8.1-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:c6936ac7a316ce86e78e570bca73724319c2daa33db147974adbe3c8e502f27a"}, + {file = "yara_x-1.8.1-cp38-abi3-win_amd64.whl", hash = "sha256:25c3e4554ba3428968f1749004efaaf47a111f2ac7db51d08915f66140c7d53d"}, + {file = "yara_x-1.8.1-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:3565c776476c71a15cd997463b52578c38b4c20aae538157baf070b83339d7b4"}, + {file = "yara_x-1.8.1-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:b0fb608e107e92fb240cac8229160637c6219fe06811e113aca0cc7a85190e80"}, + {file = "yara_x-1.8.1-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:4d3ceaf7571b8d6ce6a2e644e3083c4f5656e4726885d98d12b75bbbb23262f7"}, + {file = "yara_x-1.8.1-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:7f2e42b980e0b4388e633d11c3ac4a7d70d10e3099e008ba3dadf8c6f9a33fb9"}, + {file = "yara_x-1.8.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:6a31f5f7e17b0231e9b0ef00ca0c91fb6c2a7b21c1973a42c0a459288ed7fd71"}, ] [[package]] name = "yarl" -version = "1.20.1" +version = "1.22.0" description = "Yet another URL library" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "yarl-1.20.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6032e6da6abd41e4acda34d75a816012717000fa6839f37124a47fcefc49bec4"}, - {file = "yarl-1.20.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2c7b34d804b8cf9b214f05015c4fee2ebe7ed05cf581e7192c06555c71f4446a"}, - {file = "yarl-1.20.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0c869f2651cc77465f6cd01d938d91a11d9ea5d798738c1dc077f3de0b5e5fed"}, - {file = "yarl-1.20.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62915e6688eb4d180d93840cda4110995ad50c459bf931b8b3775b37c264af1e"}, - {file = "yarl-1.20.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:41ebd28167bc6af8abb97fec1a399f412eec5fd61a3ccbe2305a18b84fb4ca73"}, - {file = "yarl-1.20.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:21242b4288a6d56f04ea193adde174b7e347ac46ce6bc84989ff7c1b1ecea84e"}, - {file = "yarl-1.20.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bea21cdae6c7eb02ba02a475f37463abfe0a01f5d7200121b03e605d6a0439f8"}, - {file = "yarl-1.20.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f8a891e4a22a89f5dde7862994485e19db246b70bb288d3ce73a34422e55b23"}, - {file = "yarl-1.20.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dd803820d44c8853a109a34e3660e5a61beae12970da479cf44aa2954019bf70"}, - {file = "yarl-1.20.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b982fa7f74c80d5c0c7b5b38f908971e513380a10fecea528091405f519b9ebb"}, - {file = "yarl-1.20.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:33f29ecfe0330c570d997bcf1afd304377f2e48f61447f37e846a6058a4d33b2"}, - {file = "yarl-1.20.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:835ab2cfc74d5eb4a6a528c57f05688099da41cf4957cf08cad38647e4a83b30"}, - {file = "yarl-1.20.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:46b5e0ccf1943a9a6e766b2c2b8c732c55b34e28be57d8daa2b3c1d1d4009309"}, - {file = "yarl-1.20.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:df47c55f7d74127d1b11251fe6397d84afdde0d53b90bedb46a23c0e534f9d24"}, - {file = "yarl-1.20.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:76d12524d05841276b0e22573f28d5fbcb67589836772ae9244d90dd7d66aa13"}, - {file = "yarl-1.20.1-cp310-cp310-win32.whl", hash = "sha256:6c4fbf6b02d70e512d7ade4b1f998f237137f1417ab07ec06358ea04f69134f8"}, - {file = "yarl-1.20.1-cp310-cp310-win_amd64.whl", hash = "sha256:aef6c4d69554d44b7f9d923245f8ad9a707d971e6209d51279196d8e8fe1ae16"}, - {file = "yarl-1.20.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:47ee6188fea634bdfaeb2cc420f5b3b17332e6225ce88149a17c413c77ff269e"}, - {file = "yarl-1.20.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d0f6500f69e8402d513e5eedb77a4e1818691e8f45e6b687147963514d84b44b"}, - {file = "yarl-1.20.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7a8900a42fcdaad568de58887c7b2f602962356908eedb7628eaf6021a6e435b"}, - {file = "yarl-1.20.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bad6d131fda8ef508b36be3ece16d0902e80b88ea7200f030a0f6c11d9e508d4"}, - {file = "yarl-1.20.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:df018d92fe22aaebb679a7f89fe0c0f368ec497e3dda6cb81a567610f04501f1"}, - {file = "yarl-1.20.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8f969afbb0a9b63c18d0feecf0db09d164b7a44a053e78a7d05f5df163e43833"}, - {file = "yarl-1.20.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:812303eb4aa98e302886ccda58d6b099e3576b1b9276161469c25803a8db277d"}, - {file = "yarl-1.20.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98c4a7d166635147924aa0bf9bfe8d8abad6fffa6102de9c99ea04a1376f91e8"}, - {file = "yarl-1.20.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:12e768f966538e81e6e7550f9086a6236b16e26cd964cf4df35349970f3551cf"}, - {file = "yarl-1.20.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fe41919b9d899661c5c28a8b4b0acf704510b88f27f0934ac7a7bebdd8938d5e"}, - {file = "yarl-1.20.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:8601bc010d1d7780592f3fc1bdc6c72e2b6466ea34569778422943e1a1f3c389"}, - {file = "yarl-1.20.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:daadbdc1f2a9033a2399c42646fbd46da7992e868a5fe9513860122d7fe7a73f"}, - {file = "yarl-1.20.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:03aa1e041727cb438ca762628109ef1333498b122e4c76dd858d186a37cec845"}, - {file = "yarl-1.20.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:642980ef5e0fa1de5fa96d905c7e00cb2c47cb468bfcac5a18c58e27dbf8d8d1"}, - {file = "yarl-1.20.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:86971e2795584fe8c002356d3b97ef6c61862720eeff03db2a7c86b678d85b3e"}, - {file = "yarl-1.20.1-cp311-cp311-win32.whl", hash = "sha256:597f40615b8d25812f14562699e287f0dcc035d25eb74da72cae043bb884d773"}, - {file = "yarl-1.20.1-cp311-cp311-win_amd64.whl", hash = "sha256:26ef53a9e726e61e9cd1cda6b478f17e350fb5800b4bd1cd9fe81c4d91cfeb2e"}, - {file = "yarl-1.20.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:bdcc4cd244e58593a4379fe60fdee5ac0331f8eb70320a24d591a3be197b94a9"}, - {file = "yarl-1.20.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b29a2c385a5f5b9c7d9347e5812b6f7ab267193c62d282a540b4fc528c8a9d2a"}, - {file = "yarl-1.20.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1112ae8154186dfe2de4732197f59c05a83dc814849a5ced892b708033f40dc2"}, - {file = "yarl-1.20.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:90bbd29c4fe234233f7fa2b9b121fb63c321830e5d05b45153a2ca68f7d310ee"}, - {file = "yarl-1.20.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:680e19c7ce3710ac4cd964e90dad99bf9b5029372ba0c7cbfcd55e54d90ea819"}, - {file = "yarl-1.20.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4a979218c1fdb4246a05efc2cc23859d47c89af463a90b99b7c56094daf25a16"}, - {file = "yarl-1.20.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:255b468adf57b4a7b65d8aad5b5138dce6a0752c139965711bdcb81bc370e1b6"}, - {file = "yarl-1.20.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a97d67108e79cfe22e2b430d80d7571ae57d19f17cda8bb967057ca8a7bf5bfd"}, - {file = "yarl-1.20.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8570d998db4ddbfb9a590b185a0a33dbf8aafb831d07a5257b4ec9948df9cb0a"}, - {file = "yarl-1.20.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:97c75596019baae7c71ccf1d8cc4738bc08134060d0adfcbe5642f778d1dca38"}, - {file = "yarl-1.20.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:1c48912653e63aef91ff988c5432832692ac5a1d8f0fb8a33091520b5bbe19ef"}, - {file = "yarl-1.20.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4c3ae28f3ae1563c50f3d37f064ddb1511ecc1d5584e88c6b7c63cf7702a6d5f"}, - {file = "yarl-1.20.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c5e9642f27036283550f5f57dc6156c51084b458570b9d0d96100c8bebb186a8"}, - {file = "yarl-1.20.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:2c26b0c49220d5799f7b22c6838409ee9bc58ee5c95361a4d7831f03cc225b5a"}, - {file = "yarl-1.20.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:564ab3d517e3d01c408c67f2e5247aad4019dcf1969982aba3974b4093279004"}, - {file = "yarl-1.20.1-cp312-cp312-win32.whl", hash = "sha256:daea0d313868da1cf2fac6b2d3a25c6e3a9e879483244be38c8e6a41f1d876a5"}, - {file = "yarl-1.20.1-cp312-cp312-win_amd64.whl", hash = "sha256:48ea7d7f9be0487339828a4de0360d7ce0efc06524a48e1810f945c45b813698"}, - {file = "yarl-1.20.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:0b5ff0fbb7c9f1b1b5ab53330acbfc5247893069e7716840c8e7d5bb7355038a"}, - {file = "yarl-1.20.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:14f326acd845c2b2e2eb38fb1346c94f7f3b01a4f5c788f8144f9b630bfff9a3"}, - {file = "yarl-1.20.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f60e4ad5db23f0b96e49c018596707c3ae89f5d0bd97f0ad3684bcbad899f1e7"}, - {file = "yarl-1.20.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:49bdd1b8e00ce57e68ba51916e4bb04461746e794e7c4d4bbc42ba2f18297691"}, - {file = "yarl-1.20.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:66252d780b45189975abfed839616e8fd2dbacbdc262105ad7742c6ae58f3e31"}, - {file = "yarl-1.20.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59174e7332f5d153d8f7452a102b103e2e74035ad085f404df2e40e663a22b28"}, - {file = "yarl-1.20.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e3968ec7d92a0c0f9ac34d5ecfd03869ec0cab0697c91a45db3fbbd95fe1b653"}, - {file = "yarl-1.20.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1a4fbb50e14396ba3d375f68bfe02215d8e7bc3ec49da8341fe3157f59d2ff5"}, - {file = "yarl-1.20.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:11a62c839c3a8eac2410e951301309426f368388ff2f33799052787035793b02"}, - {file = "yarl-1.20.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:041eaa14f73ff5a8986b4388ac6bb43a77f2ea09bf1913df7a35d4646db69e53"}, - {file = "yarl-1.20.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:377fae2fef158e8fd9d60b4c8751387b8d1fb121d3d0b8e9b0be07d1b41e83dc"}, - {file = "yarl-1.20.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:1c92f4390e407513f619d49319023664643d3339bd5e5a56a3bebe01bc67ec04"}, - {file = "yarl-1.20.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d25ddcf954df1754ab0f86bb696af765c5bfaba39b74095f27eececa049ef9a4"}, - {file = "yarl-1.20.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:909313577e9619dcff8c31a0ea2aa0a2a828341d92673015456b3ae492e7317b"}, - {file = "yarl-1.20.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:793fd0580cb9664548c6b83c63b43c477212c0260891ddf86809e1c06c8b08f1"}, - {file = "yarl-1.20.1-cp313-cp313-win32.whl", hash = "sha256:468f6e40285de5a5b3c44981ca3a319a4b208ccc07d526b20b12aeedcfa654b7"}, - {file = "yarl-1.20.1-cp313-cp313-win_amd64.whl", hash = "sha256:495b4ef2fea40596bfc0affe3837411d6aa3371abcf31aac0ccc4bdd64d4ef5c"}, - {file = "yarl-1.20.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:f60233b98423aab21d249a30eb27c389c14929f47be8430efa7dbd91493a729d"}, - {file = "yarl-1.20.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:6f3eff4cc3f03d650d8755c6eefc844edde99d641d0dcf4da3ab27141a5f8ddf"}, - {file = "yarl-1.20.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:69ff8439d8ba832d6bed88af2c2b3445977eba9a4588b787b32945871c2444e3"}, - {file = "yarl-1.20.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cf34efa60eb81dd2645a2e13e00bb98b76c35ab5061a3989c7a70f78c85006d"}, - {file = "yarl-1.20.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8e0fe9364ad0fddab2688ce72cb7a8e61ea42eff3c7caeeb83874a5d479c896c"}, - {file = "yarl-1.20.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8f64fbf81878ba914562c672024089e3401974a39767747691c65080a67b18c1"}, - {file = "yarl-1.20.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f6342d643bf9a1de97e512e45e4b9560a043347e779a173250824f8b254bd5ce"}, - {file = "yarl-1.20.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56dac5f452ed25eef0f6e3c6a066c6ab68971d96a9fb441791cad0efba6140d3"}, - {file = "yarl-1.20.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7d7f497126d65e2cad8dc5f97d34c27b19199b6414a40cb36b52f41b79014be"}, - {file = "yarl-1.20.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:67e708dfb8e78d8a19169818eeb5c7a80717562de9051bf2413aca8e3696bf16"}, - {file = "yarl-1.20.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:595c07bc79af2494365cc96ddeb772f76272364ef7c80fb892ef9d0649586513"}, - {file = "yarl-1.20.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:7bdd2f80f4a7df852ab9ab49484a4dee8030023aa536df41f2d922fd57bf023f"}, - {file = "yarl-1.20.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:c03bfebc4ae8d862f853a9757199677ab74ec25424d0ebd68a0027e9c639a390"}, - {file = "yarl-1.20.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:344d1103e9c1523f32a5ed704d576172d2cabed3122ea90b1d4e11fe17c66458"}, - {file = "yarl-1.20.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:88cab98aa4e13e1ade8c141daeedd300a4603b7132819c484841bb7af3edce9e"}, - {file = "yarl-1.20.1-cp313-cp313t-win32.whl", hash = "sha256:b121ff6a7cbd4abc28985b6028235491941b9fe8fe226e6fdc539c977ea1739d"}, - {file = "yarl-1.20.1-cp313-cp313t-win_amd64.whl", hash = "sha256:541d050a355bbbc27e55d906bc91cb6fe42f96c01413dd0f4ed5a5240513874f"}, - {file = "yarl-1.20.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e42ba79e2efb6845ebab49c7bf20306c4edf74a0b20fc6b2ccdd1a219d12fad3"}, - {file = "yarl-1.20.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:41493b9b7c312ac448b7f0a42a089dffe1d6e6e981a2d76205801a023ed26a2b"}, - {file = "yarl-1.20.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f5a5928ff5eb13408c62a968ac90d43f8322fd56d87008b8f9dabf3c0f6ee983"}, - {file = "yarl-1.20.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:30c41ad5d717b3961b2dd785593b67d386b73feca30522048d37298fee981805"}, - {file = "yarl-1.20.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:59febc3969b0781682b469d4aca1a5cab7505a4f7b85acf6db01fa500fa3f6ba"}, - {file = "yarl-1.20.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d2b6fb3622b7e5bf7a6e5b679a69326b4279e805ed1699d749739a61d242449e"}, - {file = "yarl-1.20.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:749d73611db8d26a6281086f859ea7ec08f9c4c56cec864e52028c8b328db723"}, - {file = "yarl-1.20.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9427925776096e664c39e131447aa20ec738bdd77c049c48ea5200db2237e000"}, - {file = "yarl-1.20.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff70f32aa316393eaf8222d518ce9118148eddb8a53073c2403863b41033eed5"}, - {file = "yarl-1.20.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:c7ddf7a09f38667aea38801da8b8d6bfe81df767d9dfc8c88eb45827b195cd1c"}, - {file = "yarl-1.20.1-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:57edc88517d7fc62b174fcfb2e939fbc486a68315d648d7e74d07fac42cec240"}, - {file = "yarl-1.20.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:dab096ce479d5894d62c26ff4f699ec9072269d514b4edd630a393223f45a0ee"}, - {file = "yarl-1.20.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:14a85f3bd2d7bb255be7183e5d7d6e70add151a98edf56a770d6140f5d5f4010"}, - {file = "yarl-1.20.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:2c89b5c792685dd9cd3fa9761c1b9f46fc240c2a3265483acc1565769996a3f8"}, - {file = "yarl-1.20.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:69e9b141de5511021942a6866990aea6d111c9042235de90e08f94cf972ca03d"}, - {file = "yarl-1.20.1-cp39-cp39-win32.whl", hash = "sha256:b5f307337819cdfdbb40193cad84978a029f847b0a357fbe49f712063cfc4f06"}, - {file = "yarl-1.20.1-cp39-cp39-win_amd64.whl", hash = "sha256:eae7bfe2069f9c1c5b05fc7fe5d612e5bbc089a39309904ee8b829e322dcad00"}, - {file = "yarl-1.20.1-py3-none-any.whl", hash = "sha256:83b8eb083fe4683c6115795d9fc1cfaf2cbbefb19b3a1cb68f6527460f483a77"}, - {file = "yarl-1.20.1.tar.gz", hash = "sha256:d017a4997ee50c91fd5466cef416231bb82177b93b029906cefc542ce14c35ac"}, + {file = "yarl-1.22.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c7bd6683587567e5a49ee6e336e0612bec8329be1b7d4c8af5687dcdeb67ee1e"}, + {file = "yarl-1.22.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5cdac20da754f3a723cceea5b3448e1a2074866406adeb4ef35b469d089adb8f"}, + {file = "yarl-1.22.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:07a524d84df0c10f41e3ee918846e1974aba4ec017f990dc735aad487a0bdfdf"}, + {file = "yarl-1.22.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e1b329cb8146d7b736677a2440e422eadd775d1806a81db2d4cded80a48efc1a"}, + {file = "yarl-1.22.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:75976c6945d85dbb9ee6308cd7ff7b1fb9409380c82d6119bd778d8fcfe2931c"}, + {file = "yarl-1.22.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:80ddf7a5f8c86cb3eb4bc9028b07bbbf1f08a96c5c0bc1244be5e8fefcb94147"}, + {file = "yarl-1.22.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d332fc2e3c94dad927f2112395772a4e4fedbcf8f80efc21ed7cdfae4d574fdb"}, + {file = "yarl-1.22.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0cf71bf877efeac18b38d3930594c0948c82b64547c1cf420ba48722fe5509f6"}, + {file = "yarl-1.22.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:663e1cadaddae26be034a6ab6072449a8426ddb03d500f43daf952b74553bba0"}, + {file = "yarl-1.22.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:6dcbb0829c671f305be48a7227918cfcd11276c2d637a8033a99a02b67bf9eda"}, + {file = "yarl-1.22.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f0d97c18dfd9a9af4490631905a3f131a8e4c9e80a39353919e2cfed8f00aedc"}, + {file = "yarl-1.22.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:437840083abe022c978470b942ff832c3940b2ad3734d424b7eaffcd07f76737"}, + {file = "yarl-1.22.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a899cbd98dce6f5d8de1aad31cb712ec0a530abc0a86bd6edaa47c1090138467"}, + {file = "yarl-1.22.0-cp310-cp310-win32.whl", hash = "sha256:595697f68bd1f0c1c159fcb97b661fc9c3f5db46498043555d04805430e79bea"}, + {file = "yarl-1.22.0-cp310-cp310-win_amd64.whl", hash = "sha256:cb95a9b1adaa48e41815a55ae740cfda005758104049a640a398120bf02515ca"}, + {file = "yarl-1.22.0-cp310-cp310-win_arm64.whl", hash = "sha256:b85b982afde6df99ecc996990d4ad7ccbdbb70e2a4ba4de0aecde5922ba98a0b"}, + {file = "yarl-1.22.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1ab72135b1f2db3fed3997d7e7dc1b80573c67138023852b6efb336a5eae6511"}, + {file = "yarl-1.22.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:669930400e375570189492dc8d8341301578e8493aec04aebc20d4717f899dd6"}, + {file = "yarl-1.22.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:792a2af6d58177ef7c19cbf0097aba92ca1b9cb3ffdd9c7470e156c8f9b5e028"}, + {file = "yarl-1.22.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3ea66b1c11c9150f1372f69afb6b8116f2dd7286f38e14ea71a44eee9ec51b9d"}, + {file = "yarl-1.22.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3e2daa88dc91870215961e96a039ec73e4937da13cf77ce17f9cad0c18df3503"}, + {file = "yarl-1.22.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ba440ae430c00eee41509353628600212112cd5018d5def7e9b05ea7ac34eb65"}, + {file = "yarl-1.22.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e6438cc8f23a9c1478633d216b16104a586b9761db62bfacb6425bac0a36679e"}, + {file = "yarl-1.22.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c52a6e78aef5cf47a98ef8e934755abf53953379b7d53e68b15ff4420e6683d"}, + {file = "yarl-1.22.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3b06bcadaac49c70f4c88af4ffcfbe3dc155aab3163e75777818092478bcbbe7"}, + {file = "yarl-1.22.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:6944b2dc72c4d7f7052683487e3677456050ff77fcf5e6204e98caf785ad1967"}, + {file = "yarl-1.22.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:d5372ca1df0f91a86b047d1277c2aaf1edb32d78bbcefffc81b40ffd18f027ed"}, + {file = "yarl-1.22.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:51af598701f5299012b8416486b40fceef8c26fc87dc6d7d1f6fc30609ea0aa6"}, + {file = "yarl-1.22.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b266bd01fedeffeeac01a79ae181719ff848a5a13ce10075adbefc8f1daee70e"}, + {file = "yarl-1.22.0-cp311-cp311-win32.whl", hash = "sha256:a9b1ba5610a4e20f655258d5a1fdc7ebe3d837bb0e45b581398b99eb98b1f5ca"}, + {file = "yarl-1.22.0-cp311-cp311-win_amd64.whl", hash = "sha256:078278b9b0b11568937d9509b589ee83ef98ed6d561dfe2020e24a9fd08eaa2b"}, + {file = "yarl-1.22.0-cp311-cp311-win_arm64.whl", hash = "sha256:b6a6f620cfe13ccec221fa312139135166e47ae169f8253f72a0abc0dae94376"}, + {file = "yarl-1.22.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e340382d1afa5d32b892b3ff062436d592ec3d692aeea3bef3a5cfe11bbf8c6f"}, + {file = "yarl-1.22.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f1e09112a2c31ffe8d80be1b0988fa6a18c5d5cad92a9ffbb1c04c91bfe52ad2"}, + {file = "yarl-1.22.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:939fe60db294c786f6b7c2d2e121576628468f65453d86b0fe36cb52f987bd74"}, + {file = "yarl-1.22.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e1651bf8e0398574646744c1885a41198eba53dc8a9312b954073f845c90a8df"}, + {file = "yarl-1.22.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b8a0588521a26bf92a57a1705b77b8b59044cdceccac7151bd8d229e66b8dedb"}, + {file = "yarl-1.22.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:42188e6a615c1a75bcaa6e150c3fe8f3e8680471a6b10150c5f7e83f47cc34d2"}, + {file = "yarl-1.22.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f6d2cb59377d99718913ad9a151030d6f83ef420a2b8f521d94609ecc106ee82"}, + {file = "yarl-1.22.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50678a3b71c751d58d7908edc96d332af328839eea883bb554a43f539101277a"}, + {file = "yarl-1.22.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1e8fbaa7cec507aa24ea27a01456e8dd4b6fab829059b69844bd348f2d467124"}, + {file = "yarl-1.22.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:433885ab5431bc3d3d4f2f9bd15bfa1614c522b0f1405d62c4f926ccd69d04fa"}, + {file = "yarl-1.22.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:b790b39c7e9a4192dc2e201a282109ed2985a1ddbd5ac08dc56d0e121400a8f7"}, + {file = "yarl-1.22.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:31f0b53913220599446872d757257be5898019c85e7971599065bc55065dc99d"}, + {file = "yarl-1.22.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a49370e8f711daec68d09b821a34e1167792ee2d24d405cbc2387be4f158b520"}, + {file = "yarl-1.22.0-cp312-cp312-win32.whl", hash = "sha256:70dfd4f241c04bd9239d53b17f11e6ab672b9f1420364af63e8531198e3f5fe8"}, + {file = "yarl-1.22.0-cp312-cp312-win_amd64.whl", hash = "sha256:8884d8b332a5e9b88e23f60bb166890009429391864c685e17bd73a9eda9105c"}, + {file = "yarl-1.22.0-cp312-cp312-win_arm64.whl", hash = "sha256:ea70f61a47f3cc93bdf8b2f368ed359ef02a01ca6393916bc8ff877427181e74"}, + {file = "yarl-1.22.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8dee9c25c74997f6a750cd317b8ca63545169c098faee42c84aa5e506c819b53"}, + {file = "yarl-1.22.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:01e73b85a5434f89fc4fe27dcda2aff08ddf35e4d47bbbea3bdcd25321af538a"}, + {file = "yarl-1.22.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:22965c2af250d20c873cdbee8ff958fb809940aeb2e74ba5f20aaf6b7ac8c70c"}, + {file = "yarl-1.22.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b4f15793aa49793ec8d1c708ab7f9eded1aa72edc5174cae703651555ed1b601"}, + {file = "yarl-1.22.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5542339dcf2747135c5c85f68680353d5cb9ffd741c0f2e8d832d054d41f35a"}, + {file = "yarl-1.22.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5c401e05ad47a75869c3ab3e35137f8468b846770587e70d71e11de797d113df"}, + {file = "yarl-1.22.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:243dda95d901c733f5b59214d28b0120893d91777cb8aa043e6ef059d3cddfe2"}, + {file = "yarl-1.22.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bec03d0d388060058f5d291a813f21c011041938a441c593374da6077fe21b1b"}, + {file = "yarl-1.22.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b0748275abb8c1e1e09301ee3cf90c8a99678a4e92e4373705f2a2570d581273"}, + {file = "yarl-1.22.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:47fdb18187e2a4e18fda2c25c05d8251a9e4a521edaed757fef033e7d8498d9a"}, + {file = "yarl-1.22.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c7044802eec4524fde550afc28edda0dd5784c4c45f0be151a2d3ba017daca7d"}, + {file = "yarl-1.22.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:139718f35149ff544caba20fce6e8a2f71f1e39b92c700d8438a0b1d2a631a02"}, + {file = "yarl-1.22.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e1b51bebd221006d3d2f95fbe124b22b247136647ae5dcc8c7acafba66e5ee67"}, + {file = "yarl-1.22.0-cp313-cp313-win32.whl", hash = "sha256:d3e32536234a95f513bd374e93d717cf6b2231a791758de6c509e3653f234c95"}, + {file = "yarl-1.22.0-cp313-cp313-win_amd64.whl", hash = "sha256:47743b82b76d89a1d20b83e60d5c20314cbd5ba2befc9cda8f28300c4a08ed4d"}, + {file = "yarl-1.22.0-cp313-cp313-win_arm64.whl", hash = "sha256:5d0fcda9608875f7d052eff120c7a5da474a6796fe4d83e152e0e4d42f6d1a9b"}, + {file = "yarl-1.22.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:719ae08b6972befcba4310e49edb1161a88cdd331e3a694b84466bd938a6ab10"}, + {file = "yarl-1.22.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:47d8a5c446df1c4db9d21b49619ffdba90e77c89ec6e283f453856c74b50b9e3"}, + {file = "yarl-1.22.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:cfebc0ac8333520d2d0423cbbe43ae43c8838862ddb898f5ca68565e395516e9"}, + {file = "yarl-1.22.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4398557cbf484207df000309235979c79c4356518fd5c99158c7d38203c4da4f"}, + {file = "yarl-1.22.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2ca6fd72a8cd803be290d42f2dec5cdcd5299eeb93c2d929bf060ad9efaf5de0"}, + {file = "yarl-1.22.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ca1f59c4e1ab6e72f0a23c13fca5430f889634166be85dbf1013683e49e3278e"}, + {file = "yarl-1.22.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6c5010a52015e7c70f86eb967db0f37f3c8bd503a695a49f8d45700144667708"}, + {file = "yarl-1.22.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d7672ecf7557476642c88497c2f8d8542f8e36596e928e9bcba0e42e1e7d71f"}, + {file = "yarl-1.22.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:3b7c88eeef021579d600e50363e0b6ee4f7f6f728cd3486b9d0f3ee7b946398d"}, + {file = "yarl-1.22.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:f4afb5c34f2c6fecdcc182dfcfc6af6cccf1aa923eed4d6a12e9d96904e1a0d8"}, + {file = "yarl-1.22.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:59c189e3e99a59cf8d83cbb31d4db02d66cda5a1a4374e8a012b51255341abf5"}, + {file = "yarl-1.22.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:5a3bf7f62a289fa90f1990422dc8dff5a458469ea71d1624585ec3a4c8d6960f"}, + {file = "yarl-1.22.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:de6b9a04c606978fdfe72666fa216ffcf2d1a9f6a381058d4378f8d7b1e5de62"}, + {file = "yarl-1.22.0-cp313-cp313t-win32.whl", hash = "sha256:1834bb90991cc2999f10f97f5f01317f99b143284766d197e43cd5b45eb18d03"}, + {file = "yarl-1.22.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ff86011bd159a9d2dfc89c34cfd8aff12875980e3bd6a39ff097887520e60249"}, + {file = "yarl-1.22.0-cp313-cp313t-win_arm64.whl", hash = "sha256:7861058d0582b847bc4e3a4a4c46828a410bca738673f35a29ba3ca5db0b473b"}, + {file = "yarl-1.22.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:34b36c2c57124530884d89d50ed2c1478697ad7473efd59cfd479945c95650e4"}, + {file = "yarl-1.22.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:0dd9a702591ca2e543631c2a017e4a547e38a5c0f29eece37d9097e04a7ac683"}, + {file = "yarl-1.22.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:594fcab1032e2d2cc3321bb2e51271e7cd2b516c7d9aee780ece81b07ff8244b"}, + {file = "yarl-1.22.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f3d7a87a78d46a2e3d5b72587ac14b4c16952dd0887dbb051451eceac774411e"}, + {file = "yarl-1.22.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:852863707010316c973162e703bddabec35e8757e67fcb8ad58829de1ebc8590"}, + {file = "yarl-1.22.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:131a085a53bfe839a477c0845acf21efc77457ba2bcf5899618136d64f3303a2"}, + {file = "yarl-1.22.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:078a8aefd263f4d4f923a9677b942b445a2be970ca24548a8102689a3a8ab8da"}, + {file = "yarl-1.22.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bca03b91c323036913993ff5c738d0842fc9c60c4648e5c8d98331526df89784"}, + {file = "yarl-1.22.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:68986a61557d37bb90d3051a45b91fa3d5c516d177dfc6dd6f2f436a07ff2b6b"}, + {file = "yarl-1.22.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:4792b262d585ff0dff6bcb787f8492e40698443ec982a3568c2096433660c694"}, + {file = "yarl-1.22.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:ebd4549b108d732dba1d4ace67614b9545b21ece30937a63a65dd34efa19732d"}, + {file = "yarl-1.22.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f87ac53513d22240c7d59203f25cc3beac1e574c6cd681bbfd321987b69f95fd"}, + {file = "yarl-1.22.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:22b029f2881599e2f1b06f8f1db2ee63bd309e2293ba2d566e008ba12778b8da"}, + {file = "yarl-1.22.0-cp314-cp314-win32.whl", hash = "sha256:6a635ea45ba4ea8238463b4f7d0e721bad669f80878b7bfd1f89266e2ae63da2"}, + {file = "yarl-1.22.0-cp314-cp314-win_amd64.whl", hash = "sha256:0d6e6885777af0f110b0e5d7e5dda8b704efed3894da26220b7f3d887b839a79"}, + {file = "yarl-1.22.0-cp314-cp314-win_arm64.whl", hash = "sha256:8218f4e98d3c10d683584cb40f0424f4b9fd6e95610232dd75e13743b070ee33"}, + {file = "yarl-1.22.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:45c2842ff0e0d1b35a6bf1cd6c690939dacb617a70827f715232b2e0494d55d1"}, + {file = "yarl-1.22.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:d947071e6ebcf2e2bee8fce76e10faca8f7a14808ca36a910263acaacef08eca"}, + {file = "yarl-1.22.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:334b8721303e61b00019474cc103bdac3d7b1f65e91f0bfedeec2d56dfe74b53"}, + {file = "yarl-1.22.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e7ce67c34138a058fd092f67d07a72b8e31ff0c9236e751957465a24b28910c"}, + {file = "yarl-1.22.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d77e1b2c6d04711478cb1c4ab90db07f1609ccf06a287d5607fcd90dc9863acf"}, + {file = "yarl-1.22.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4647674b6150d2cae088fc07de2738a84b8bcedebef29802cf0b0a82ab6face"}, + {file = "yarl-1.22.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:efb07073be061c8f79d03d04139a80ba33cbd390ca8f0297aae9cce6411e4c6b"}, + {file = "yarl-1.22.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e51ac5435758ba97ad69617e13233da53908beccc6cfcd6c34bbed8dcbede486"}, + {file = "yarl-1.22.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:33e32a0dd0c8205efa8e83d04fc9f19313772b78522d1bdc7d9aed706bfd6138"}, + {file = "yarl-1.22.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:bf4a21e58b9cde0e401e683ebd00f6ed30a06d14e93f7c8fd059f8b6e8f87b6a"}, + {file = "yarl-1.22.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:e4b582bab49ac33c8deb97e058cd67c2c50dac0dd134874106d9c774fd272529"}, + {file = "yarl-1.22.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:0b5bcc1a9c4839e7e30b7b30dd47fe5e7e44fb7054ec29b5bb8d526aa1041093"}, + {file = "yarl-1.22.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:c0232bce2170103ec23c454e54a57008a9a72b5d1c3105dc2496750da8cfa47c"}, + {file = "yarl-1.22.0-cp314-cp314t-win32.whl", hash = "sha256:8009b3173bcd637be650922ac455946197d858b3630b6d8787aa9e5c4564533e"}, + {file = "yarl-1.22.0-cp314-cp314t-win_amd64.whl", hash = "sha256:9fb17ea16e972c63d25d4a97f016d235c78dd2344820eb35bc034bc32012ee27"}, + {file = "yarl-1.22.0-cp314-cp314t-win_arm64.whl", hash = "sha256:9f6d73c1436b934e3f01df1e1b21ff765cd1d28c77dfb9ace207f746d4610ee1"}, + {file = "yarl-1.22.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3aa27acb6de7a23785d81557577491f6c38a5209a254d1191519d07d8fe51748"}, + {file = "yarl-1.22.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:af74f05666a5e531289cb1cc9c883d1de2088b8e5b4de48004e5ca8a830ac859"}, + {file = "yarl-1.22.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:62441e55958977b8167b2709c164c91a6363e25da322d87ae6dd9c6019ceecf9"}, + {file = "yarl-1.22.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b580e71cac3f8113d3135888770903eaf2f507e9421e5697d6ee6d8cd1c7f054"}, + {file = "yarl-1.22.0-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e81fda2fb4a07eda1a2252b216aa0df23ebcd4d584894e9612e80999a78fd95b"}, + {file = "yarl-1.22.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:99b6fc1d55782461b78221e95fc357b47ad98b041e8e20f47c1411d0aacddc60"}, + {file = "yarl-1.22.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:088e4e08f033db4be2ccd1f34cf29fe994772fb54cfe004bbf54db320af56890"}, + {file = "yarl-1.22.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e4e1f6f0b4da23e61188676e3ed027ef0baa833a2e633c29ff8530800edccba"}, + {file = "yarl-1.22.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:84fc3ec96fce86ce5aa305eb4aa9358279d1aa644b71fab7b8ed33fe3ba1a7ca"}, + {file = "yarl-1.22.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:5dbeefd6ca588b33576a01b0ad58aa934bc1b41ef89dee505bf2932b22ddffba"}, + {file = "yarl-1.22.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:14291620375b1060613f4aab9ebf21850058b6b1b438f386cc814813d901c60b"}, + {file = "yarl-1.22.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:a4fcfc8eb2c34148c118dfa02e6427ca278bfd0f3df7c5f99e33d2c0e81eae3e"}, + {file = "yarl-1.22.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:029866bde8d7b0878b9c160e72305bbf0a7342bcd20b9999381704ae03308dc8"}, + {file = "yarl-1.22.0-cp39-cp39-win32.whl", hash = "sha256:4dcc74149ccc8bba31ce1944acee24813e93cfdee2acda3c172df844948ddf7b"}, + {file = "yarl-1.22.0-cp39-cp39-win_amd64.whl", hash = "sha256:10619d9fdee46d20edc49d3479e2f8269d0779f1b031e6f7c2aa1c76be04b7ed"}, + {file = "yarl-1.22.0-cp39-cp39-win_arm64.whl", hash = "sha256:dd7afd3f8b0bfb4e0d9fc3c31bfe8a4ec7debe124cfd90619305def3c8ca8cd2"}, + {file = "yarl-1.22.0-py3-none-any.whl", hash = "sha256:1380560bdba02b6b6c90de54133c81c9f2a453dee9912fe58c1dcced1edb7cff"}, + {file = "yarl-1.22.0.tar.gz", hash = "sha256:bebf8557577d4401ba8bd9ff33906f1376c877aa78d1fe216ad01b4d6745af71"}, ] [package.dependencies] @@ -2139,4 +2661,4 @@ propcache = ">=0.2.1" [metadata] lock-version = "2.1" python-versions = ">=3.12,<4.0" -content-hash = "6552d98358cb657ece08997fab507a11e8f34f896497043040915d5278251739" +content-hash = "e8abe96590b5a664df7c757fd442be6f40542d7361e116779887b6ec1885316d" diff --git a/libs/file_enrichment_modules/pyproject.toml b/libs/file_enrichment_modules/pyproject.toml index 8ef2d75..ab7203a 100644 --- a/libs/file_enrichment_modules/pyproject.toml +++ b/libs/file_enrichment_modules/pyproject.toml @@ -16,82 +16,18 @@ plyara = "^2.2.7" asyncpg = "^0.30.0" psycopg = "^3.2.4" common = { path = "../common", develop = true } +nemesis_dpapi = { path = "../nemesis_dpapi", develop = true } +file_linking = { path = "../file_linking", develop = true } +chromium = { path = "../chromium", develop = true } lnkparse3 = "^1.5.0" +dapr = "1.16.0" [tool.poetry.group.dev.dependencies] ruff = "^0.9.2" +pytest = "^8.4.2" +pytest-asyncio = "^1.2.0" [build-system] requires = ["poetry-core>=2.0.0,<3.0.0"] build-backend = "poetry.core.masonry.api" -[tool.ruff] -select = [ - "E", # pycodestyle errors - "W", # pycodestyle warnings - "F", # pyflakes - "I", # isort - "C", # flake8-comprehensions - "B", # flake8-bugbear - "UP", # pyupgrade - "NPY", # numpydoc - "A", # flake8-annotations - "TCH001", # Move application-only imports into TYPE_CHECKING block - "TCH002", # Move third-party imports into TYPE_CHECKING block - "TCH003", # Move standard library imports into TYPE_CHECKING block -] -ignore = [ - "E501", # line too long, handled by black - "B008", # do not perform function calls in argument defaults - "C901", # too complex - "W191", # indentation contains tabs - "F722", # syntax error in forward annotation - "UP007", # X | Y syntax while we're still supporting 3.9 - "UP038", # isinstance() X | Y instance ^ - "B905", # zip() without strict (isn't supported in 3.9) -] - -exclude = [ - ".bzr", - ".direnv", - ".eggs", - ".git", - ".git-rewrite", - ".hg", - ".mypy_cache", - ".nox", - ".pants.d", - ".pytype", - ".ruff_cache", - ".svn", - ".tox", - ".venv", - "__pypackages__", - "_build", - "buck-out", - "build", - "dist", - "node_modules", - "venv", - "office2john.py", -] - -line-length = 120 -indent-width = 4 -target-version = "py312" - -[tool.ruff.lint] -fixable = ["ALL"] -unfixable = ["B"] - -dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" -extend-select = [ - "I", # isort, added per instructions in the VS code extension (https://marketplace.visualstudio.com/items?itemName=charliermarsh.ruff) -] - -[tool.ruff.format] -quote-style = "double" -indent-style = "space" -skip-magic-trailing-comma = false -line-ending = "auto" - diff --git a/libs/file_enrichment_modules/tests/test_example.py b/libs/file_enrichment_modules/tests/test_example.py new file mode 100644 index 0000000..b497045 --- /dev/null +++ b/libs/file_enrichment_modules/tests/test_example.py @@ -0,0 +1,3 @@ +def test_example(): + """Simple example test to verify pytest is working.""" + assert True diff --git a/libs/file_linking/.vscode/settings.json b/libs/file_linking/.vscode/settings.json new file mode 100644 index 0000000..372f1cf --- /dev/null +++ b/libs/file_linking/.vscode/settings.json @@ -0,0 +1,54 @@ +{ + "[javascript]": { + "editor.formatOnSave": false + }, + "[html]": { + "editor.formatOnSave": false + }, + "[python]": { + "editor.codeActionsOnSave": { + "source.fixAll.ruff": "explicit", + "source.organizeImports.ruff": "explicit" + }, + "editor.defaultFormatter": "charliermarsh.ruff" + }, + "autoDocstring.docstringFormat": "google", + "files.exclude": { + "**/.DS_Store": true, + "**/.git": true, + "**/.hg": true, + "**/.mypy_cache": true, + "**/.pytest_cache": true, + "**/.svn": true, + "**/.venv": true, + "**/__pycache__": true, + "**/Thumbs.db": true + }, + "files.trimTrailingWhitespace": true, + "files.watcherExclude": { + "**/__pycache__/**": true, + "**/.git/objects/**": true, + "**/.git/subtree-cache/**": true, + "**/.hg/store/**": true, + "**/.ipynb_checkpoints/**": true, + "**/.mypy_cache/**": true, + "**/.pytest_cache/**": true, + "**/.venv/**": true, + "**/*.egg-info/**": true, + "**/build/**": true, + "**/dist/**": true, + "**/node_modules/*/**": true + }, + "python.analysis.diagnosticSeverityOverrides": { + "reportMissingImports": "none", + "reportMissingModuleSource": "none" + }, + "python.analysis.useLibraryCodeForTypes": true, // Pyright + "python.languageServer": "Pylance", + "python.testing.pytestArgs": [ + "tests" + ], + "python.testing.unittestEnabled": false, + "python.testing.pytestEnabled": true, + "python.analysis.typeCheckingMode": "basic" +} \ No newline at end of file diff --git a/libs/file_linking/README.md b/libs/file_linking/README.md new file mode 100644 index 0000000..5ce34f7 --- /dev/null +++ b/libs/file_linking/README.md @@ -0,0 +1,2 @@ +# File Linking +A File Linking helper library. \ No newline at end of file diff --git a/libs/file_linking/file_linking/__init__.py b/libs/file_linking/file_linking/__init__.py new file mode 100644 index 0000000..6120ee7 --- /dev/null +++ b/libs/file_linking/file_linking/__init__.py @@ -0,0 +1,16 @@ +""" +File linking system for Nemesis. + +This module provides rule-based detection and tracking of file dependencies and relationships. +""" + +from .database_service import FileLinkingDatabaseService, FileListingStatus +from .helpers import add_file_linking +from .rules_engine import FileLinkingEngine + +__all__ = [ + "FileLinkingEngine", + "FileLinkingDatabaseService", + "FileListingStatus", + "add_file_linking", +] diff --git a/libs/file_linking/file_linking/database_service.py b/libs/file_linking/file_linking/database_service.py new file mode 100644 index 0000000..423eac5 --- /dev/null +++ b/libs/file_linking/file_linking/database_service.py @@ -0,0 +1,301 @@ +""" +Database service layer for file linking system. + +Handles all database operations for file_listings and file_linkings tables. +""" + +from enum import Enum + +import asyncpg +from common.logger import get_logger + +logger = get_logger(__name__) + + +class FileListingStatus(str, Enum): + NEEDS_TO_BE_COLLECTED = "needs_to_be_collected" + NOT_EXISTS = "not_exists" + COLLECTED = "collected" + NOT_WANTED = "not_wanted" + + +class FileLinkingDatabaseService: + """Service for managing file listings and linkings in the database.""" + + def __init__(self, connection_pool: asyncpg.Pool): + self.pool = connection_pool + + async def add_file_listing(self, source: str, path: str, status: FileListingStatus, object_id: str | None = None) -> bool: + """ + Add or update entry in file_listings table. + + Args: + source: Source identifier (e.g., agent_id/source) + path: File path + status: Current collection status + object_id: UUID if file is already collected + + Returns: + bool: True if successful, False otherwise + """ + if source and path: + try: + query = """ + INSERT INTO file_listings (source, path, object_id, status) + VALUES ($1, $2, $3, $4) + ON CONFLICT (source, path_lower) DO UPDATE SET + object_id = CASE + WHEN file_listings.status = 'collected' THEN file_listings.object_id + ELSE EXCLUDED.object_id + END, + status = CASE + WHEN file_listings.status = 'collected' THEN file_listings.status + ELSE EXCLUDED.status + END, + updated_at = CURRENT_TIMESTAMP + """ + + async with self.pool.acquire() as conn: + await conn.execute(query, source, path, object_id, status.value) + + logger.debug( + "Added/updated file listing", + source=source, + path=path, + status=status.value, + object_id=object_id, + ) + return True + + except Exception as e: + logger.exception( + "Error adding file listing", source=source, path=path, status=status.value, error=str(e) + ) + return False + + return False + + async def add_file_linking(self, source: str, file_path_1: str, file_path_2: str, link_type: str | None = None) -> bool: + """ + Add relationship between two files in file_linkings table. + + Args: + source: Source identifier + file_path_1: First file path + file_path_2: Second file path (linked to first) + link_type: Type of relationship (optional) + + Returns: + bool: True if successful, False otherwise + """ + if source and file_path_1 and file_path_2: + try: + query = """ + INSERT INTO file_linkings (source, file_path_1, file_path_2, link_type) + VALUES ($1, $2, $3, $4) + ON CONFLICT (source, file_path_1, file_path_2) DO UPDATE SET + link_type = EXCLUDED.link_type, + updated_at = CURRENT_TIMESTAMP + """ + + async with self.pool.acquire() as conn: + await conn.execute(query, source, file_path_1, file_path_2, link_type) + + logger.debug( + "Added file linking", + source=source, + file_path_1=file_path_1, + file_path_2=file_path_2, + link_type=link_type, + ) + return True + + except Exception as e: + logger.exception( + "Error adding file linking", + source=source, + file_path_1=file_path_1, + file_path_2=file_path_2, + error=str(e), + ) + return False + + return False + + async def get_placeholder_entries(self, source: str) -> list[dict]: + """ + Query entries containing any known placeholders for a given source. + + Dynamically reads the PLACEHOLDERS registry to build the query. + + Args: + source: Source identifier + + Returns: + List of dicts with 'table_name' and 'path' keys + + Example: + [ + {'table_name': 'file_listings', 'path': '/C:/Users//...'}, + {'table_name': 'file_linkings', 'path': '/C:/Users//...'} + ] + """ + try: + # Import PLACEHOLDERS dynamically to avoid circular dependency + from .placeholder_resolver import PLACEHOLDERS + + if not PLACEHOLDERS: + return [] + + # Build LIKE conditions from PLACEHOLDERS array for each table + placeholder_conditions_listings = " OR ".join([f"file_listings.path LIKE '%{p.name}%'" for p in PLACEHOLDERS]) + placeholder_conditions_linkings = " OR ".join([f"file_linkings.file_path_2 LIKE '%{p.name}%'" for p in PLACEHOLDERS]) + + query = f""" + SELECT 'file_listings' as table_name, path + FROM file_listings + WHERE source = $1 AND ({placeholder_conditions_listings}) + UNION + SELECT 'file_linkings' as table_name, file_path_2 as path + FROM file_linkings + WHERE source = $1 AND ({placeholder_conditions_linkings}) + """ + + # Use asyncpg for async operations + async with self.pool.acquire() as conn: + rows = await conn.fetch(query, source) + results = [{"table_name": row["table_name"], "path": row["path"]} for row in rows] + + logger.debug( + "Queried placeholder entries", + source=source, + count=len(results), + ) + return results + + except Exception as e: + logger.exception("Error querying placeholder entries", source=source, error=str(e)) + return [] + + async def get_collected_files(self, source: str) -> list[str]: + """ + Get all file paths that have been collected for a given source. + + Used for backward resolution to check if a real file exists + before inserting a placeholder path. + + Args: + source: Source identifier + + Returns: + List of file paths with status='collected' + """ + try: + query = """ + SELECT DISTINCT path + FROM file_listings + WHERE source = $1 + AND status = 'collected' + AND object_id IS NOT NULL + """ + + async with self.pool.acquire() as conn: + rows = await conn.fetch(query, source) + paths = [row["path"] for row in rows] + + logger.debug( + "Queried collected files", + source=source, + count=len(paths), + ) + return paths + + except Exception as e: + logger.exception("Error querying collected files", source=source, error=str(e)) + return [] + + async def update_file_listing_path(self, source: str, old_path: str, new_path: str) -> bool: + """ + Update path in file_listings table. + + Used to replace placeholder paths with resolved real paths. + + Args: + source: Source identifier + old_path: Current path (with placeholders) + new_path: New path (resolved) + + Returns: + bool: True if successful, False otherwise + """ + try: + query = """ + UPDATE file_listings + SET path = $3, updated_at = CURRENT_TIMESTAMP + WHERE source = $1 AND LOWER(path) = LOWER($2) + """ + + async with self.pool.acquire() as conn: + result = await conn.execute(query, source, old_path, new_path) + + logger.info( + "Updated file listing path", + source=source, + old_path=old_path, + new_path=new_path, + result=result, + ) + return True + + except Exception as e: + logger.exception( + "Error updating file listing path", + source=source, + old_path=old_path, + new_path=new_path, + error=str(e), + ) + return False + + async def update_file_linking_path(self, source: str, old_path: str, new_path: str) -> bool: + """ + Update file_path_2 in file_linkings table. + + Used to replace placeholder paths with resolved real paths. + + Args: + source: Source identifier + old_path: Current path (with placeholders) + new_path: New path (resolved) + + Returns: + bool: True if successful, False otherwise + """ + try: + query = """ + UPDATE file_linkings + SET file_path_2 = $3, updated_at = CURRENT_TIMESTAMP + WHERE source = $1 AND LOWER(file_path_2) = LOWER($2) + """ + + async with self.pool.acquire() as conn: + result = await conn.execute(query, source, old_path, new_path) + + logger.info( + "Updated file linking path", + source=source, + old_path=old_path, + new_path=new_path, + result=result, + ) + return True + + except Exception as e: + logger.exception( + "Error updating file linking path", + source=source, + old_path=old_path, + new_path=new_path, + error=str(e), + ) + return False diff --git a/libs/file_linking/file_linking/helpers.py b/libs/file_linking/file_linking/helpers.py new file mode 100644 index 0000000..a6af81d --- /dev/null +++ b/libs/file_linking/file_linking/helpers.py @@ -0,0 +1,152 @@ +""" +Helper functions for enrichment modules to create file linkings programmatically. + +This module provides a simple interface for enrichment modules to register +file relationships they discover during analysis. +""" + +import asyncpg +from common.db import get_postgres_connection_str +from common.logger import get_logger + +from .rules_engine import FileLinkingEngine + +logger = get_logger(__name__) + + +async def add_file_linkings( + source: str, + source_file_path: str, + linked_file_paths: list[str], + link_type: str, + collection_reason: str | None = None, + connection_pool: asyncpg.Pool | None = None, +) -> int: + """ + Add file linkings programmatically from enrichment modules. + + This is the main function enrichment modules should call to register + file relationships they discover during analysis. + + Args: + source: Source identifier (typically agent_id) + source_file_path: Path of the file that triggered the linking + linked_file_paths: List of file paths to link + link_type: Type of relationship (e.g., "pe_import", "config_dependency") + collection_reason: Optional reason for collection + connection_pool: Optional asyncpg.Pool. If not provided, will create a new connection. + + Returns: + int: Number of linkings created + + Example: + # In a PE analysis module + from file_linking.helpers import add_file_linkings + + # After discovering imported DLL paths + linked_paths = [ + "C:\\Windows\\System32\\kernel32.dll", + "C:\\Windows\\System32\\advapi32.dll" + ] + + await add_file_linkings( + source="agent123", + source_file_path="C:\\malware\\sample.exe", + linked_file_paths=linked_paths, + link_type="pe_import", + collection_reason="Required DLL dependencies for analysis", + connection_pool=pool # Pass pool for better performance + ) + """ + + try: + if connection_pool is not None: + file_linking_engine = FileLinkingEngine(connection_pool) + else: + # Fallback: create temporary pool for backward compatibility + logger.warning( + "add_file_linkings called without connection_pool, creating temporary connection. " + "Consider passing a connection pool for better performance." + ) + temp_pool = await asyncpg.create_pool( + get_postgres_connection_str(), + min_size=1, + max_size=2, + ) + try: + file_linking_engine = FileLinkingEngine(temp_pool) + finally: + # Note: We'll close this pool after use below + pass + except Exception as e: + logger.exception(e, "[add_file_linkings]") + return 0 + + if not linked_file_paths: + return 0 + + try: + result = await file_linking_engine.add_programmatic_linking( + source=source, + source_file_path=source_file_path, + linked_file_paths=linked_file_paths, + link_type=link_type, + collection_reason=collection_reason, + ) + + # Close temporary pool if we created one + if connection_pool is None and temp_pool: + await temp_pool.close() + + return result + + except Exception as e: + logger.exception( + "[add_file_linkings] Error adding programmatic file linkings", + source=source, + source_file_path=source_file_path, + linked_file_paths=linked_file_paths, + link_type=link_type, + error=str(e), + ) + # Close temporary pool if we created one + if connection_pool is None and temp_pool: + await temp_pool.close() + return 0 + + +async def add_file_linking( + source: str, + source_file_path: str, + linked_file_path: str, + link_type: str, + collection_reason: str | None = None, + connection_pool: asyncpg.Pool | None = None, +) -> bool: + """ + Add a single file linking (convenience function). + + Uses FileLinkingEngine.add_programmatic_linking() which handles placeholder resolution. + + Args: + source: Source identifier + source_file_path: Path of the source file + linked_file_path: Path of the linked file + link_type: Type of relationship + collection_reason: Optional reason for collection + connection_pool: Optional asyncpg.Pool. If not provided, will create a new connection. + + Returns: + bool: True if successful, False otherwise + """ + return ( + await add_file_linkings( + source=source, + source_file_path=source_file_path, + linked_file_paths=[linked_file_path], + link_type=link_type, + collection_reason=collection_reason, + connection_pool=connection_pool, + ) + > 0 + ) diff --git a/libs/file_linking/file_linking/placeholder_resolver.py b/libs/file_linking/file_linking/placeholder_resolver.py new file mode 100644 index 0000000..248fa76 --- /dev/null +++ b/libs/file_linking/file_linking/placeholder_resolver.py @@ -0,0 +1,314 @@ +""" +Placeholder resolution for file paths in file_listings and file_linkings tables. + +Provides bidirectional resolution between placeholder paths (e.g., containing ) +and real file paths, handling files arriving in any order. +""" + +import re +from dataclasses import dataclass + +from common.logger import get_logger + +from file_linking.database_service import FileLinkingDatabaseService + +logger = get_logger(__name__) + + +@dataclass +class PlaceholderDefinition: + """Definition of a placeholder with its regex pattern.""" + + name: str # e.g., '' + pattern: str # Regex pattern with capture group + description: str + + +# Registry of known placeholders - single source of truth +# Add new placeholders here to automatically support them throughout the system +PLACEHOLDERS = [ + PlaceholderDefinition( + name="", + pattern=r'([^"\\/\[\]:;|=,+*?<>]+)', + description="Windows username (excludes forbidden characters)", + ), + PlaceholderDefinition( + name="", + pattern=r"(S-1-5-(?:18|19|20|21-\d+-\d+-\d+-\d+))", + description="Windows SID (supports user and system SIDs)", + ), + PlaceholderDefinition( + name="", + pattern=r"([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})", + description="Windows Machine GUID (8-4-4-4-12 hex digits)", + ), +] + + +class PlaceholderResolver: + """ + Resolves placeholders in file paths bidirectionally. + + Supports two resolution modes: + 1. Forward: Real file arrives → update existing placeholder entries + 2. Backward: Placeholder path needed → check if real file already exists + """ + + def __init__(self, db_service: FileLinkingDatabaseService): + """ + Initialize the placeholder resolver. + + Args: + db_service: FileLinkingDatabaseService instance for database operations + """ + self.db_service = db_service + + def _convert_placeholder_to_regex(self, template_path: str) -> re.Pattern | None: + """ + Convert a placeholder template path to a compiled regex pattern. + + Replaces each placeholder with its regex pattern and escapes special characters. + Handles both full paths and bare filenames. + + Args: + template_path: Path containing placeholders (e.g., '/C:/Users//...') + + Returns: + Compiled case-insensitive regex pattern, or None if no placeholders found + + Example: + Input: '/C:/Users//AppData/Roaming/file.txt' + Output: Pattern matching '/C:/Users/john.doe/AppData/Roaming/file.txt' (case-insensitive) + """ + if not template_path or ("<" not in template_path and ">" not in template_path): + return None + + # Start with the template path + regex_str = template_path + + # Track which placeholders we're replacing + found_placeholders = [] + + # Replace each placeholder with its regex pattern + for placeholder_def in PLACEHOLDERS: + if placeholder_def.name in regex_str: + regex_str = regex_str.replace(placeholder_def.name, placeholder_def.pattern) + found_placeholders.append(placeholder_def.name) + + if not found_placeholders: + return None + + # Escape special regex characters, but preserve our capture groups + # First, temporarily replace capture groups with placeholders + group_placeholder = "###CAPTURE_GROUP_{}###" + group_count = 0 + temp_str = regex_str + + # Extract and protect capture groups + capture_groups = [] + while "(" in temp_str: + start = temp_str.find("(") + depth = 1 + i = start + 1 + while i < len(temp_str) and depth > 0: + if temp_str[i] == "(": + depth += 1 + elif temp_str[i] == ")": + depth -= 1 + i += 1 + + if depth == 0: + capture_group = temp_str[start:i] + capture_groups.append(capture_group) + temp_str = temp_str[:start] + group_placeholder.format(group_count) + temp_str[i:] + group_count += 1 + else: + break + + # Escape regex special characters in the non-capture-group parts + temp_str = re.escape(temp_str) + + # Restore capture groups + for i, capture_group in enumerate(capture_groups): + temp_str = temp_str.replace(re.escape(group_placeholder.format(i)), capture_group) + + # Compile with case-insensitive flag for Windows paths + try: + pattern = re.compile(temp_str, re.IGNORECASE) + logger.debug( + "Converted placeholder template to regex", + template=template_path, + placeholders=found_placeholders, + ) + return pattern + except re.error as e: + logger.warning("Failed to compile regex pattern", template=template_path, error=str(e)) + return None + + def _replace_placeholders_with_captures(self, template_path: str, match: re.Match) -> str: + """ + Replace placeholders in template with captured values from regex match. + + Args: + template_path: Original path with placeholders + match: Regex match object with captured groups + + Returns: + Path with placeholders replaced by actual values + + Example: + Input: template='/C:/Users//file.txt', match with group='john.doe' + Output: '/C:/Users/john.doe/file.txt' + """ + result = template_path + captured_values = match.groups() + + if not captured_values: + return template_path + + # Replace placeholders in order with captured values + group_index = 0 + for placeholder_def in PLACEHOLDERS: + if placeholder_def.name in result and group_index < len(captured_values): + captured_value = captured_values[group_index] + result = result.replace(placeholder_def.name, captured_value) + group_index += 1 + logger.debug( + "Replaced placeholder", + placeholder=placeholder_def.name, + value=captured_value, + ) + + return result + + async def resolve_placeholders_for_file(self, file_path: str, source: str) -> int: + """ + Forward resolution: Match a real file against placeholder entries and update them. + + Called when a real file arrives to resolve any existing placeholder entries + that match this file's path. + + Args: + file_path: Real file path that was just collected + source: Source identifier + + Returns: + Number of placeholder entries resolved + + Example: + Placeholder entry: '/C:/Users//AppData/.../abc123' + Real file arrives: '/C:/Users/john.doe/AppData/.../abc123' + → Updates placeholder entry to use real path + """ + if not file_path or not source: + return 0 + + # Query for placeholder entries for this source + placeholder_entries = await self.db_service.get_placeholder_entries(source) + + if not placeholder_entries: + logger.debug("No placeholder entries found for source", source=source) + return 0 + + resolved_count = 0 + + for entry in placeholder_entries: + table_name = entry["table_name"] + placeholder_path = entry["path"] + + # Convert placeholder path to regex + pattern = self._convert_placeholder_to_regex(placeholder_path) + if not pattern: + continue + + # Try to match against the real file path + match = pattern.match(file_path) + + if match: + # Resolve the placeholder path using captured values + resolved_path = self._replace_placeholders_with_captures(placeholder_path, match) + + logger.info( + "Matched placeholder entry with real file", + placeholder_path=placeholder_path, + real_path=file_path, + resolved_path=resolved_path, + table=table_name, + source=source, + ) + + # Update the database + if table_name == "file_listings": + await self.db_service.update_file_listing_path(source, placeholder_path, resolved_path) + elif table_name == "file_linkings": + await self.db_service.update_file_linking_path(source, placeholder_path, resolved_path) + + resolved_count += 1 + + if resolved_count > 0: + logger.info( + "Resolved placeholder entries", + file_path=file_path, + source=source, + count=resolved_count, + ) + + return resolved_count + + async def try_resolve_placeholder_path(self, source: str, placeholder_path: str) -> str | None: + """ + Backward resolution: Try to find a real file that matches a placeholder path. + + Called before inserting a new placeholder path to check if a matching + real file already exists. + + Args: + source: Source identifier + placeholder_path: Path containing placeholders + + Returns: + Real file path if match found, None otherwise + + Example: + Placeholder needed: '/C:/Users//AppData/.../abc123' + Real file exists: '/C:/Users/john.doe/AppData/.../abc123' + → Returns the real path instead of creating placeholder entry + """ + if not placeholder_path or not source: + return None + + # Only process if path contains placeholders + if "<" not in placeholder_path or ">" not in placeholder_path: + return None + + # Convert placeholder path to regex pattern + pattern = self._convert_placeholder_to_regex(placeholder_path) + if not pattern: + return None + + # Get all collected files for this source + collected_files = await self.db_service.get_collected_files(source) + + if not collected_files: + logger.debug("No collected files found for backward resolution", source=source) + return None + + # Try to match each collected file against the placeholder pattern + for real_path in collected_files: + match = pattern.match(real_path) + + if match: + logger.info( + "Found existing file matching placeholder path (backward resolution)", + placeholder_path=placeholder_path, + real_path=real_path, + source=source, + ) + return real_path + + logger.debug( + "No existing file matches placeholder path", + placeholder_path=placeholder_path, + source=source, + ) + return None diff --git a/libs/file_linking/file_linking/rules/chromium/cookies.yaml b/libs/file_linking/file_linking/rules/chromium/cookies.yaml new file mode 100644 index 0000000..ee8d57f --- /dev/null +++ b/libs/file_linking/file_linking/rules/chromium/cookies.yaml @@ -0,0 +1,19 @@ +name: "chromium_cookies" +description: "Link Chromium Cookies files to their Local State" +category: "chromium" +enabled: true + +triggers: + - file_patterns: + - "**/User Data/Default/Network/Cookies" + mime_patterns: + - "application/vnd.sqlite3" + - "application/vnd.sqlite3; charset=binary" + +linked_files: + - name: "local_state" + description: "Chromium Local State file for key decryption" + path_templates: + - "{parent_dir}/../../Local State" + priority: "high" + collection_reason: "Contains master key for cookie decryption" diff --git a/libs/file_linking/file_linking/rules/chromium/local_state.yaml b/libs/file_linking/file_linking/rules/chromium/local_state.yaml new file mode 100644 index 0000000..ff4fd05 --- /dev/null +++ b/libs/file_linking/file_linking/rules/chromium/local_state.yaml @@ -0,0 +1,26 @@ +name: "chromium_local_state" +description: "Link Chromium Local State files to Login Data + Cookies" +category: "chromium" +enabled: true + +triggers: + - file_patterns: + - "**/User Data/Local State" + - "**/Opera Software/Opera Stable/Local State" + mime_patterns: + - "application/json" + +linked_files: + - name: "login_data" + description: "Stored login credentials" + path_templates: + - "{parent_dir}/Default/Login Data" + priority: "high" + collection_reason: "Contains encrypted login credentials" + + - name: "cookies" + description: "Stored cookie values" + path_templates: + - "{parent_dir}/Default/Network/Cookies" + priority: "high" + collection_reason: "Contains encrypted cookie values" diff --git a/libs/file_linking/file_linking/rules/chromium/login_data.yaml b/libs/file_linking/file_linking/rules/chromium/login_data.yaml new file mode 100644 index 0000000..16bc160 --- /dev/null +++ b/libs/file_linking/file_linking/rules/chromium/login_data.yaml @@ -0,0 +1,18 @@ +name: "chromium_login_data" +description: "Link Chromium Login Data files to their Local State" +category: "chromium" +enabled: true + +triggers: + - file_patterns: + - "**/User Data/Default/Login Data" + mime_patterns: + - "application/vnd.sqlite3; charset=binary" + +linked_files: + - name: "local_state" + description: "Chromium Local State file for key decryption" + path_templates: + - "{parent_dir}/../Local State" + priority: "high" + collection_reason: "Contains master key for cookie decryption" diff --git a/libs/file_linking/file_linking/rules_engine.py b/libs/file_linking/file_linking/rules_engine.py new file mode 100644 index 0000000..2ab5d99 --- /dev/null +++ b/libs/file_linking/file_linking/rules_engine.py @@ -0,0 +1,466 @@ +""" +File linking rules engine. + +Processes files to detect relationships and create linkings based on: +1. YAML rule files +2. Programmatic calls from enrichment modules +""" + +import fnmatch +import os +import posixpath +from dataclasses import dataclass + +import asyncpg +import yaml +from common.logger import get_logger +from common.models import FileEnriched + +from .database_service import FileLinkingDatabaseService, FileListingStatus +from .placeholder_resolver import PlaceholderResolver + +logger = get_logger(__name__) + + +@dataclass +class Trigger: + """Represents a trigger condition for a linking rule.""" + + file_patterns: list[str] + mime_patterns: list[str] + magic_patterns: list[str] + + +@dataclass +class LinkedFile: + """Represents a file that should be linked/collected.""" + + name: str + description: str + path_templates: list[str] + priority: str + collection_reason: str + + +@dataclass +class LinkingRule: + """Represents a complete linking rule from YAML.""" + + name: str + description: str + category: str + enabled: bool + triggers: list[Trigger] + linked_files: list[LinkedFile] + + +class FileLinkingEngine: + """ + Engine for processing file linking rules and creating database entries. + + Supports both YAML-based rules and programmatic calls from enrichment modules. + """ + + def __init__(self, connection_pool: asyncpg.Pool, rules_dir: str | None = None): + self.db_service = FileLinkingDatabaseService(connection_pool) + self.placeholder_resolver = PlaceholderResolver(self.db_service) + self.rules: list[LinkingRule] = [] + + if rules_dir is None: + rules_dir = os.path.join(os.path.dirname(__file__), "rules") + + self.rules_dir = rules_dir + self._load_rules() + + def _load_rules(self) -> None: + """Load all YAML rule files from the rules directory.""" + if not os.path.exists(self.rules_dir): + logger.warning("Rules directory does not exist", rules_dir=self.rules_dir) + return + + rules_loaded = 0 + + for root, _, files in os.walk(self.rules_dir): + for file in files: + if file.endswith(".yaml") or file.endswith(".yml"): + rule_path = os.path.join(root, file) + try: + rule = self._load_rule_file(rule_path) + if rule and rule.enabled: + self.rules.append(rule) + rules_loaded += 1 + except Exception as e: + logger.exception("Error loading rule file", rule_path=rule_path, error=str(e)) + + logger.info("Loaded file linking rules", count=rules_loaded, rules_dir=self.rules_dir) + + def _load_rule_file(self, rule_path: str) -> LinkingRule | None: + """Load a single YAML rule file.""" + try: + with open(rule_path) as f: + data = yaml.safe_load(f) + + # Convert triggers dictionaries to Trigger objects + triggers = [] + for trigger_data in data.get("triggers", []): + triggers.append( + Trigger( + file_patterns=trigger_data.get("file_patterns", []), + mime_patterns=trigger_data.get("mime_patterns", []), + magic_patterns=trigger_data.get("magic_patterns", []), + ) + ) + + # Convert linked_files dictionaries to LinkedFile objects + linked_files = [] + for lf_data in data.get("linked_files", []): + linked_files.append( + LinkedFile( + name=lf_data["name"], + description=lf_data["description"], + path_templates=lf_data["path_templates"], + priority=lf_data["priority"], + collection_reason=lf_data["collection_reason"], + ) + ) + + return LinkingRule( + name=data["name"], + description=data["description"], + category=data["category"], + enabled=data.get("enabled", True), + triggers=triggers, + linked_files=linked_files, + ) + + except Exception as e: + logger.exception("Error parsing rule file", rule_path=rule_path, error=str(e)) + return None + + def _matches_trigger(self, file_enriched: FileEnriched, trigger: Trigger) -> bool: + """Check if a file matches a path, mime type, or magic type trigger condition.""" + file_path = file_enriched.path + mime_type = file_enriched.mime_type + magic_type = file_enriched.magic_type + + # Check file patterns + logger.debug(f"file_patterns: {trigger.file_patterns}") + + if trigger.file_patterns: + path_match = any(fnmatch.fnmatch(file_path, pattern) for pattern in trigger.file_patterns) + + if not path_match: + return False + + # Check MIME types + if trigger.mime_patterns and mime_type not in trigger.mime_patterns: + logger.debug(f"path_match but mime types mismatch: {mime_type}") + return False + + # Check magic patterns + if trigger.magic_patterns: + magic_match = any(pattern in magic_type for pattern in trigger.magic_patterns) + if not magic_match: + logger.debug("path_match but magic string mismatch") + return False + + return True + + async def _resolve_backward(self, source: str, linked_path: str) -> tuple[str, FileListingStatus]: + """ + Perform backward resolution: check if a placeholder path has a matching real file. + + Args: + source: Source identifier + linked_path: Path that may contain placeholders + + Returns: + Tuple of (resolved_path, status) where: + - resolved_path: The real path if found, otherwise the original linked_path + - status: COLLECTED if resolved, NEEDS_TO_BE_COLLECTED otherwise + """ + status = FileListingStatus.NEEDS_TO_BE_COLLECTED + final_path = linked_path + + if "<" in linked_path and ">" in linked_path: + try: + resolved_path = await self.placeholder_resolver.try_resolve_placeholder_path(source, linked_path) + if resolved_path: + logger.info( + "Backward resolution: found existing file for placeholder", + placeholder_path=linked_path, + real_path=resolved_path, + source=source, + ) + final_path = resolved_path + status = FileListingStatus.COLLECTED + except Exception as e: + logger.warning( + "Error in backward placeholder resolution", + linked_path=linked_path, + source=source, + error=str(e), + ) + + return final_path, status + + async def _resolve_forward_for_table( + self, source: str, real_path: str, table_name: str + ) -> None: + """ + Perform forward resolution for a specific table: check if a real path matches placeholders. + + Resolves ALL matching placeholders, not just the first one. + + Args: + source: Source identifier + real_path: The real file path (no placeholders) + table_name: Either "file_listings" or "file_linkings" + """ + if "<" in real_path and ">" in real_path: + # This is a placeholder, not a real path, skip forward resolution + return + + try: + placeholder_entries = await self.db_service.get_placeholder_entries(source) + + for entry in placeholder_entries: + if entry["table_name"] != table_name: + continue + + placeholder_path = entry["path"] + + # Convert placeholder to regex and try to match + pattern = self.placeholder_resolver._convert_placeholder_to_regex(placeholder_path) + if pattern and pattern.match(real_path): + # This real path matches an existing placeholder + resolved_path = self.placeholder_resolver._replace_placeholders_with_captures( + placeholder_path, pattern.match(real_path) + ) + + logger.info( + f"Forward resolution matched placeholder in {table_name}", + placeholder_path=placeholder_path, + real_path=real_path, + source=source, + ) + + # Update the placeholder in the appropriate table + if table_name == "file_listings": + await self.db_service.update_file_listing_path(source, placeholder_path, resolved_path) + elif table_name == "file_linkings": + await self.db_service.update_file_linking_path(source, placeholder_path, resolved_path) + # Continue checking other placeholders (no break) + + except Exception as e: + logger.warning( + f"Error in forward placeholder resolution for {table_name}", + real_path=real_path, + source=source, + error=str(e), + ) + + def _expand_path_template(self, template: str, file_path: str) -> str: + """Expand a path template with file-specific values.""" + + if not file_path: + return template + + # Files are already normalized to posix paths, so base it off that + parent_dir = posixpath.dirname(file_path) + basename = posixpath.splitext(posixpath.basename(file_path))[0] + filename = posixpath.basename(file_path) + extension = posixpath.splitext(file_path)[1] + + replacements = { + "{parent_dir}": parent_dir, + "{file_dir}": parent_dir, + "{basename}": basename, + "{filename}": filename, + "{extension}": extension, + } + + expanded = template + for placeholder, value in replacements.items(): + expanded = expanded.replace(placeholder, value) + + logger.debug(f"Template: {template}, File path: {file_path}, Expanded before normpath: {expanded}") + + expanded = posixpath.normpath(expanded) + logger.debug(f"Final expanded path: {expanded}") + + return expanded + + async def apply_linking_rules(self, file_enriched: FileEnriched) -> int: + """ + Apply YAML-based linking rules to an enriched file. + + Evaluates the file against all loaded rule triggers. When a match is found, + expands path templates to identify related files, marks them for collection, + and creates linkings between the source file and related files. + + Also performs bidirectional placeholder resolution: + - Forward: resolves existing placeholder entries using this real file + - Backward: checks if placeholder paths already have matching real files + + Args: + file_enriched: File data from files_enriched table + + Returns: + int: Number of linkings created + """ + + linkings_created = 0 + file_path = file_enriched.path + + if file_enriched.source: + source = file_enriched.source + elif file_enriched.agent_id: + source = file_enriched.agent_id + else: + source = "unknown" + + logger.debug( + f"Processing file: {file_path}, source: {source}, file_enriched: {list(file_enriched.model_dump().keys())}" + ) + + # Skip marking these commonly derived files as collected + if file_path.endswith("/strings.txt") or file_path.endswith("/decompiled.zip"): + return + + # Forward resolution: Try to resolve existing placeholder entries with this real file + # IMPORTANT: Do this BEFORE add_file_listing so the placeholder gets updated first, + # then add_file_listing will find the updated row and not create a duplicate + await self._resolve_forward_for_table(source, file_path, "file_listings") + await self._resolve_forward_for_table(source, file_path, "file_linkings") + + await self.db_service.add_file_listing( + source=source, + path=file_path, + status=FileListingStatus.COLLECTED, + object_id=file_enriched.object_id, + ) + logger.debug("Adding file listing (collected)", file_path=file_path, source=source) + + # Process each rule + for rule in self.rules: + try: + # Check if any trigger matches + for trigger in rule.triggers: + if self._matches_trigger(file_enriched, trigger): + logger.debug("File matches rule trigger", rule_name=rule.name, file_path=file_path) + + # Process linked files for this rule + for linked_file in rule.linked_files: + for template in linked_file.path_templates: + linked_path = self._expand_path_template(template, file_enriched.path) + + # Backward resolution: If linked_path contains placeholders, + # check if a matching real file already exists + linked_path, status = await self._resolve_backward(source, linked_path) + + # Add file listing + await self.db_service.add_file_listing( + source=source, + path=linked_path, + status=status, + ) + + # Add file linking + link_type = f"{rule.category}:{linked_file.name}" + await self.db_service.add_file_linking( + source=source, + file_path_1=file_path, + file_path_2=linked_path, + link_type=link_type, + ) + + linkings_created += 1 + + logger.debug( + "Created file linking", + rule_name=rule.name, + linked_file=linked_file.name, + source_path=file_path, + linked_path=linked_path, + link_type=link_type, + ) + + # Only match first trigger per rule + break + + except Exception as e: + logger.exception("Error processing rule", rule_name=rule.name, file_path=file_path, error=str(e)) + + if linkings_created > 0: + logger.info("Created file linkings from rules", file_path=file_path, linkings_created=linkings_created) + + return linkings_created + + async def add_programmatic_linking( + self, + source: str, + source_file_path: str, + linked_file_paths: list[str], + link_type: str, + collection_reason: str | None = None, + ) -> int: + """ + Add file linkings programmatically (called by enrichment modules). + + Performs bidirectional placeholder resolution: + - If linked_path has placeholders: checks if real file exists (backward resolution) + - If linked_path is real: checks if placeholder exists and resolves it (forward resolution) + + Args: + source: Source identifier + source_file_path: Path of the file that triggered the linking + linked_file_paths: List of file paths to link + link_type: Type of relationship + collection_reason: Reason for collection (stored in link_type if provided) + + Returns: + int: Number of linkings created + """ + linkings_created = 0 + + for linked_path in linked_file_paths: + # Backward resolution: If linked_path contains placeholders, + # check if a matching real file already exists in file_listings + final_linked_path, status = await self._resolve_backward(source, linked_path) + + # Forward resolution: If linked_path is a real path, + # check if placeholders exist that match it, and resolve them + await self._resolve_forward_for_table(source, final_linked_path, "file_linkings") + await self._resolve_forward_for_table(source, final_linked_path, "file_listings") + + # Add file listing + await self.db_service.add_file_listing( + source=source, + path=final_linked_path, + status=status, + ) + + # Add file linking + full_link_type = link_type + if collection_reason: + full_link_type += f":{collection_reason}" + + await self.db_service.add_file_linking( + source=source, + file_path_1=source_file_path, + file_path_2=final_linked_path, + link_type=full_link_type, + ) + + linkings_created += 1 + + logger.debug( + "Created programmatic file linking", + source_path=source_file_path, + linked_path=final_linked_path, + link_type=full_link_type, + ) + + return linkings_created + diff --git a/libs/file_linking/poetry.lock b/libs/file_linking/poetry.lock new file mode 100644 index 0000000..c1a959e --- /dev/null +++ b/libs/file_linking/poetry.lock @@ -0,0 +1,1913 @@ +# This file is automatically @generated by Poetry 2.0.1 and should not be changed by hand. + +[[package]] +name = "aiohappyeyeballs" +version = "2.6.1" +description = "Happy Eyeballs for asyncio" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8"}, + {file = "aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558"}, +] + +[[package]] +name = "aiohttp" +version = "3.13.0" +description = "Async http client/server framework (asyncio)" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "aiohttp-3.13.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ca69ec38adf5cadcc21d0b25e2144f6a25b7db7bea7e730bac25075bc305eff0"}, + {file = "aiohttp-3.13.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:240f99f88a9a6beb53ebadac79a2e3417247aa756202ed234b1dbae13d248092"}, + {file = "aiohttp-3.13.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a4676b978a9711531e7cea499d4cdc0794c617a1c0579310ab46c9fdf5877702"}, + {file = "aiohttp-3.13.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:48fcdd5bc771cbbab8ccc9588b8b6447f6a30f9fe00898b1a5107098e00d6793"}, + {file = "aiohttp-3.13.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:eeea0cdd2f687e210c8f605f322d7b0300ba55145014a5dbe98bd4be6fff1f6c"}, + {file = "aiohttp-3.13.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:10b3f01d5aeb632adaaf39c5e93f040a550464a768d54c514050c635adcbb9d0"}, + {file = "aiohttp-3.13.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a4dc0b83e25267f42ef065ea57653de4365b56d7bc4e4cfc94fabe56998f8ee6"}, + {file = "aiohttp-3.13.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:72714919ed9b90f030f761c20670e529c4af96c31bd000917dd0c9afd1afb731"}, + {file = "aiohttp-3.13.0-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:564be41e85318403fdb176e9e5b3e852d528392f42f2c1d1efcbeeed481126d7"}, + {file = "aiohttp-3.13.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:84912962071087286333f70569362e10793f73f45c48854e6859df11001eb2d3"}, + {file = "aiohttp-3.13.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:90b570f1a146181c3d6ae8f755de66227ded49d30d050479b5ae07710f7894c5"}, + {file = "aiohttp-3.13.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:2d71ca30257ce756e37a6078b1dff2d9475fee13609ad831eac9a6531bea903b"}, + {file = "aiohttp-3.13.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:cd45eb70eca63f41bb156b7dffbe1a7760153b69892d923bdb79a74099e2ed90"}, + {file = "aiohttp-3.13.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:5ae3a19949a27982c7425a7a5a963c1268fdbabf0be15ab59448cbcf0f992519"}, + {file = "aiohttp-3.13.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ea6df292013c9f050cbf3f93eee9953d6e5acd9e64a0bf4ca16404bfd7aa9bcc"}, + {file = "aiohttp-3.13.0-cp310-cp310-win32.whl", hash = "sha256:3b64f22fbb6dcd5663de5ef2d847a5638646ef99112503e6f7704bdecb0d1c4d"}, + {file = "aiohttp-3.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:f8d877aa60d80715b2afc565f0f1aea66565824c229a2d065b31670e09fed6d7"}, + {file = "aiohttp-3.13.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:99eb94e97a42367fef5fc11e28cb2362809d3e70837f6e60557816c7106e2e20"}, + {file = "aiohttp-3.13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4696665b2713021c6eba3e2b882a86013763b442577fe5d2056a42111e732eca"}, + {file = "aiohttp-3.13.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3e6a38366f7f0d0f6ed7a1198055150c52fda552b107dad4785c0852ad7685d1"}, + {file = "aiohttp-3.13.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aab715b1a0c37f7f11f9f1f579c6fbaa51ef569e47e3c0a4644fba46077a9409"}, + {file = "aiohttp-3.13.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7972c82bed87d7bd8e374b60a6b6e816d75ba4f7c2627c2d14eed216e62738e1"}, + {file = "aiohttp-3.13.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ca8313cb852af788c78d5afdea24c40172cbfff8b35e58b407467732fde20390"}, + {file = "aiohttp-3.13.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6c333a2385d2a6298265f4b3e960590f787311b87f6b5e6e21bb8375914ef504"}, + {file = "aiohttp-3.13.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cc6d5fc5edbfb8041d9607f6a417997fa4d02de78284d386bea7ab767b5ea4f3"}, + {file = "aiohttp-3.13.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7ddedba3d0043349edc79df3dc2da49c72b06d59a45a42c1c8d987e6b8d175b8"}, + {file = "aiohttp-3.13.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:23ca762140159417a6bbc959ca1927f6949711851e56f2181ddfe8d63512b5ad"}, + {file = "aiohttp-3.13.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:bfe824d6707a5dc3c5676685f624bc0c63c40d79dc0239a7fd6c034b98c25ebe"}, + {file = "aiohttp-3.13.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:3c11fa5dd2ef773a8a5a6daa40243d83b450915992eab021789498dc87acc114"}, + {file = "aiohttp-3.13.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:00fdfe370cffede3163ba9d3f190b32c0cfc8c774f6f67395683d7b0e48cdb8a"}, + {file = "aiohttp-3.13.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:6475e42ef92717a678bfbf50885a682bb360a6f9c8819fb1a388d98198fdcb80"}, + {file = "aiohttp-3.13.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:77da5305a410910218b99f2a963092f4277d8a9c1f429c1ff1b026d1826bd0b6"}, + {file = "aiohttp-3.13.0-cp311-cp311-win32.whl", hash = "sha256:2f9d9ea547618d907f2ee6670c9a951f059c5994e4b6de8dcf7d9747b420c820"}, + {file = "aiohttp-3.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:0f19f7798996d4458c669bd770504f710014926e9970f4729cf55853ae200469"}, + {file = "aiohttp-3.13.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1c272a9a18a5ecc48a7101882230046b83023bb2a662050ecb9bfcb28d9ab53a"}, + {file = "aiohttp-3.13.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:97891a23d7fd4e1afe9c2f4473e04595e4acb18e4733b910b6577b74e7e21985"}, + {file = "aiohttp-3.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:475bd56492ce5f4cffe32b5533c6533ee0c406d1d0e6924879f83adcf51da0ae"}, + {file = "aiohttp-3.13.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c32ada0abb4bc94c30be2b681c42f058ab104d048da6f0148280a51ce98add8c"}, + {file = "aiohttp-3.13.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:4af1f8877ca46ecdd0bc0d4a6b66d4b2bddc84a79e2e8366bc0d5308e76bceb8"}, + {file = "aiohttp-3.13.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e04ab827ec4f775817736b20cdc8350f40327f9b598dec4e18c9ffdcbea88a93"}, + {file = "aiohttp-3.13.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a6d9487b9471ec36b0faedf52228cd732e89be0a2bbd649af890b5e2ce422353"}, + {file = "aiohttp-3.13.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e66c57416352f36bf98f6641ddadd47c93740a22af7150d3e9a1ef6e983f9a8"}, + {file = "aiohttp-3.13.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:469167d5372f5bb3aedff4fc53035d593884fff2617a75317740e885acd48b04"}, + {file = "aiohttp-3.13.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a9f3546b503975a69b547c9fd1582cad10ede1ce6f3e313a2f547c73a3d7814f"}, + {file = "aiohttp-3.13.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:6b4174fcec98601f0cfdf308ee29a6ae53c55f14359e848dab4e94009112ee7d"}, + {file = "aiohttp-3.13.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a533873a7a4ec2270fb362ee5a0d3b98752e4e1dc9042b257cd54545a96bd8ed"}, + {file = "aiohttp-3.13.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:ce887c5e54411d607ee0959cac15bb31d506d86a9bcaddf0b7e9d63325a7a802"}, + {file = "aiohttp-3.13.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:d871f6a30d43e32fc9252dc7b9febe1a042b3ff3908aa83868d7cf7c9579a59b"}, + {file = "aiohttp-3.13.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:222c828243b4789d79a706a876910f656fad4381661691220ba57b2ab4547865"}, + {file = "aiohttp-3.13.0-cp312-cp312-win32.whl", hash = "sha256:682d2e434ff2f1108314ff7f056ce44e457f12dbed0249b24e106e385cf154b9"}, + {file = "aiohttp-3.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:0a2be20eb23888df130214b91c262a90e2de1553d6fb7de9e9010cec994c0ff2"}, + {file = "aiohttp-3.13.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:00243e51f16f6ec0fb021659d4af92f675f3cf9f9b39efd142aa3ad641d8d1e6"}, + {file = "aiohttp-3.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:059978d2fddc462e9211362cbc8446747ecd930537fa559d3d25c256f032ff54"}, + {file = "aiohttp-3.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:564b36512a7da3b386143c611867e3f7cfb249300a1bf60889bd9985da67ab77"}, + {file = "aiohttp-3.13.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4aa995b9156ae499393d949a456a7ab0b994a8241a96db73a3b73c7a090eff6a"}, + {file = "aiohttp-3.13.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:55ca0e95a3905f62f00900255ed807c580775174252999286f283e646d675a49"}, + {file = "aiohttp-3.13.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:49ce7525853a981fc35d380aa2353536a01a9ec1b30979ea4e35966316cace7e"}, + {file = "aiohttp-3.13.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2117be9883501eaf95503bd313eb4c7a23d567edd44014ba15835a1e9ec6d852"}, + {file = "aiohttp-3.13.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d169c47e40c911f728439da853b6fd06da83761012e6e76f11cb62cddae7282b"}, + {file = "aiohttp-3.13.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:703ad3f742fc81e543638a7bebddd35acadaa0004a5e00535e795f4b6f2c25ca"}, + {file = "aiohttp-3.13.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5bf635c3476f4119b940cc8d94ad454cbe0c377e61b4527f0192aabeac1e9370"}, + {file = "aiohttp-3.13.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:cfe6285ef99e7ee51cef20609be2bc1dd0e8446462b71c9db8bb296ba632810a"}, + {file = "aiohttp-3.13.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:34d8af6391c5f2e69749d7f037b614b8c5c42093c251f336bdbfa4b03c57d6c4"}, + {file = "aiohttp-3.13.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:12f5d820fadc5848d4559ea838aef733cf37ed2a1103bba148ac2f5547c14c29"}, + {file = "aiohttp-3.13.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:0f1338b61ea66f4757a0544ed8a02ccbf60e38d9cfb3225888888dd4475ebb96"}, + {file = "aiohttp-3.13.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:582770f82513419512da096e8df21ca44f86a2e56e25dc93c5ab4df0fe065bf0"}, + {file = "aiohttp-3.13.0-cp313-cp313-win32.whl", hash = "sha256:3194b8cab8dbc882f37c13ef1262e0a3d62064fa97533d3aa124771f7bf1ecee"}, + {file = "aiohttp-3.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:7897298b3eedc790257fef8a6ec582ca04e9dbe568ba4a9a890913b925b8ea21"}, + {file = "aiohttp-3.13.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:c417f8c2e1137775569297c584a8a7144e5d1237789eae56af4faf1894a0b861"}, + {file = "aiohttp-3.13.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:f84b53326abf8e56ebc28a35cebf4a0f396a13a76300f500ab11fe0573bf0b52"}, + {file = "aiohttp-3.13.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:990a53b9d6a30b2878789e490758e568b12b4a7fb2527d0c89deb9650b0e5813"}, + {file = "aiohttp-3.13.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c811612711e01b901e18964b3e5dec0d35525150f5f3f85d0aee2935f059910a"}, + {file = "aiohttp-3.13.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:ee433e594d7948e760b5c2a78cc06ac219df33b0848793cf9513d486a9f90a52"}, + {file = "aiohttp-3.13.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:19bb08e56f57c215e9572cd65cb6f8097804412c54081d933997ddde3e5ac579"}, + {file = "aiohttp-3.13.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f27b7488144eb5dd9151cf839b195edd1569629d90ace4c5b6b18e4e75d1e63a"}, + {file = "aiohttp-3.13.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d812838c109757a11354a161c95708ae4199c4fd4d82b90959b20914c1d097f6"}, + {file = "aiohttp-3.13.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7c20db99da682f9180fa5195c90b80b159632fb611e8dbccdd99ba0be0970620"}, + {file = "aiohttp-3.13.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:cf8b0870047900eb1f17f453b4b3953b8ffbf203ef56c2f346780ff930a4d430"}, + {file = "aiohttp-3.13.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:5b8a5557d5af3f4e3add52a58c4cf2b8e6e59fc56b261768866f5337872d596d"}, + {file = "aiohttp-3.13.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:052bcdd80c1c54b8a18a9ea0cd5e36f473dc8e38d51b804cea34841f677a9971"}, + {file = "aiohttp-3.13.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:76484ba17b2832776581b7ab466d094e48eba74cb65a60aea20154dae485e8bd"}, + {file = "aiohttp-3.13.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:62d8a0adcdaf62ee56bfb37737153251ac8e4b27845b3ca065862fb01d99e247"}, + {file = "aiohttp-3.13.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5004d727499ecb95f7c9147dd0bfc5b5670f71d355f0bd26d7af2d3af8e07d2f"}, + {file = "aiohttp-3.13.0-cp314-cp314-win32.whl", hash = "sha256:a1c20c26af48aea984f63f96e5d7af7567c32cb527e33b60a0ef0a6313cf8b03"}, + {file = "aiohttp-3.13.0-cp314-cp314-win_amd64.whl", hash = "sha256:56f7d230ec66e799fbfd8350e9544f8a45a4353f1cf40c1fea74c1780f555b8f"}, + {file = "aiohttp-3.13.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:2fd35177dc483ae702f07b86c782f4f4b100a8ce4e7c5778cea016979023d9fd"}, + {file = "aiohttp-3.13.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:4df1984c8804ed336089e88ac81a9417b1fd0db7c6f867c50a9264488797e778"}, + {file = "aiohttp-3.13.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:e68c0076052dd911a81d3acc4ef2911cc4ef65bf7cadbfbc8ae762da24da858f"}, + {file = "aiohttp-3.13.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bc95c49853cd29613e4fe4ff96d73068ff89b89d61e53988442e127e8da8e7ba"}, + {file = "aiohttp-3.13.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3b3bdc89413117b40cc39baae08fd09cbdeb839d421c4e7dce6a34f6b54b3ac1"}, + {file = "aiohttp-3.13.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3e77a729df23be2116acc4e9de2767d8e92445fbca68886dd991dc912f473755"}, + {file = "aiohttp-3.13.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e88ab34826d6eeb6c67e6e92400b9ec653faf5092a35f07465f44c9f1c429f82"}, + {file = "aiohttp-3.13.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:019dbef24fe28ce2301419dd63a2b97250d9760ca63ee2976c2da2e3f182f82e"}, + {file = "aiohttp-3.13.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:2c4aeaedd20771b7b4bcdf0ae791904445df6d856c02fc51d809d12d17cffdc7"}, + {file = "aiohttp-3.13.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:b3a8e6a2058a0240cfde542b641d0e78b594311bc1a710cbcb2e1841417d5cb3"}, + {file = "aiohttp-3.13.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:f8e38d55ca36c15f36d814ea414ecb2401d860de177c49f84a327a25b3ee752b"}, + {file = "aiohttp-3.13.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:a921edbe971aade1bf45bcbb3494e30ba6863a5c78f28be992c42de980fd9108"}, + {file = "aiohttp-3.13.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:474cade59a447cb4019c0dce9f0434bf835fb558ea932f62c686fe07fe6db6a1"}, + {file = "aiohttp-3.13.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:99a303ad960747c33b65b1cb65d01a62ac73fa39b72f08a2e1efa832529b01ed"}, + {file = "aiohttp-3.13.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:bb34001fc1f05f6b323e02c278090c07a47645caae3aa77ed7ed8a3ce6abcce9"}, + {file = "aiohttp-3.13.0-cp314-cp314t-win32.whl", hash = "sha256:dea698b64235d053def7d2f08af9302a69fcd760d1c7bd9988fd5d3b6157e657"}, + {file = "aiohttp-3.13.0-cp314-cp314t-win_amd64.whl", hash = "sha256:1f164699a060c0b3616459d13c1464a981fddf36f892f0a5027cbd45121fb14b"}, + {file = "aiohttp-3.13.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:fcc425fb6fd2a00c6d91c85d084c6b75a61bc8bc12159d08e17c5711df6c5ba4"}, + {file = "aiohttp-3.13.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7c2c4c9ce834801651f81d6760d0a51035b8b239f58f298de25162fcf6f8bb64"}, + {file = "aiohttp-3.13.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f91e8f9053a07177868e813656ec57599cd2a63238844393cd01bd69c2e40147"}, + {file = "aiohttp-3.13.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:df46d9a3d78ec19b495b1107bf26e4fcf97c900279901f4f4819ac5bb2a02a4c"}, + {file = "aiohttp-3.13.0-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3b1eb9871cbe43b6ca6fac3544682971539d8a1d229e6babe43446279679609d"}, + {file = "aiohttp-3.13.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:62a3cddf8d9a2eae1f79585fa81d32e13d0c509bb9e7ad47d33c83b45a944df7"}, + {file = "aiohttp-3.13.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0f735e680c323ee7e9ef8e2ea26425c7dbc2ede0086fa83ce9d7ccab8a089f26"}, + {file = "aiohttp-3.13.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6a51839f778b0e283b43cd82bb17f1835ee2cc1bf1101765e90ae886e53e751c"}, + {file = "aiohttp-3.13.0-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ac90cfab65bc281d6752f22db5fa90419e33220af4b4fa53b51f5948f414c0e7"}, + {file = "aiohttp-3.13.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:62fd54f3e6f17976962ba67f911d62723c760a69d54f5d7b74c3ceb1a4e9ef8d"}, + {file = "aiohttp-3.13.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:cf2b60b65df05b6b2fa0d887f2189991a0dbf44a0dd18359001dc8fcdb7f1163"}, + {file = "aiohttp-3.13.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:1ccedfe280e804d9a9d7fe8b8c4309d28e364b77f40309c86596baa754af50b1"}, + {file = "aiohttp-3.13.0-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:ea01ffbe23df53ece0c8732d1585b3d6079bb8c9ee14f3745daf000051415a31"}, + {file = "aiohttp-3.13.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:19ba8625fa69523627b67f7e9901b587a4952470f68814d79cdc5bc460e9b885"}, + {file = "aiohttp-3.13.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:4b14bfae90598d331b5061fd15a7c290ea0c15b34aeb1cf620464bb5ec02a602"}, + {file = "aiohttp-3.13.0-cp39-cp39-win32.whl", hash = "sha256:cf7a4b976da219e726d0043fc94ae8169c0dba1d3a059b3c1e2c964bafc5a77d"}, + {file = "aiohttp-3.13.0-cp39-cp39-win_amd64.whl", hash = "sha256:6b9697d15231aeaed4786f090c9c8bc3ab5f0e0a6da1e76c135a310def271020"}, + {file = "aiohttp-3.13.0.tar.gz", hash = "sha256:378dbc57dd8cf341ce243f13fa1fa5394d68e2e02c15cd5f28eae35a70ec7f67"}, +] + +[package.dependencies] +aiohappyeyeballs = ">=2.5.0" +aiosignal = ">=1.4.0" +attrs = ">=17.3.0" +frozenlist = ">=1.1.1" +multidict = ">=4.5,<7.0" +propcache = ">=0.2.0" +yarl = ">=1.17.0,<2.0" + +[package.extras] +speedups = ["Brotli", "aiodns (>=3.3.0)", "brotlicffi", "zstandard"] + +[[package]] +name = "aiosignal" +version = "1.4.0" +description = "aiosignal: a list of registered asynchronous callbacks" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e"}, + {file = "aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7"}, +] + +[package.dependencies] +frozenlist = ">=1.1.0" +typing-extensions = {version = ">=4.2", markers = "python_version < \"3.13\""} + +[[package]] +name = "annotated-types" +version = "0.7.0" +description = "Reusable constraint types to use with typing.Annotated" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, + {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, +] + +[[package]] +name = "anyio" +version = "4.11.0" +description = "High-level concurrency and networking framework on top of asyncio or Trio" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "anyio-4.11.0-py3-none-any.whl", hash = "sha256:0287e96f4d26d4149305414d4e3bc32f0dcd0862365a4bddea19d7a1ec38c4fc"}, + {file = "anyio-4.11.0.tar.gz", hash = "sha256:82a8d0b81e318cc5ce71a5f1f8b5c4e63619620b63141ef8c995fa0db95a57c4"}, +] + +[package.dependencies] +idna = ">=2.8" +sniffio = ">=1.1" +typing_extensions = {version = ">=4.5", markers = "python_version < \"3.13\""} + +[package.extras] +trio = ["trio (>=0.31.0)"] + +[[package]] +name = "argon2-cffi" +version = "25.1.0" +description = "Argon2 for Python" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "argon2_cffi-25.1.0-py3-none-any.whl", hash = "sha256:fdc8b074db390fccb6eb4a3604ae7231f219aa669a2652e0f20e16ba513d5741"}, + {file = "argon2_cffi-25.1.0.tar.gz", hash = "sha256:694ae5cc8a42f4c4e2bf2ca0e64e51e23a040c6a517a85074683d3959e1346c1"}, +] + +[package.dependencies] +argon2-cffi-bindings = "*" + +[[package]] +name = "argon2-cffi-bindings" +version = "25.1.0" +description = "Low-level CFFI bindings for Argon2" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:3d3f05610594151994ca9ccb3c771115bdb4daef161976a266f0dd8aa9996b8f"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:8b8efee945193e667a396cbc7b4fb7d357297d6234d30a489905d96caabde56b"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:3c6702abc36bf3ccba3f802b799505def420a1b7039862014a65db3205967f5a"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a1c70058c6ab1e352304ac7e3b52554daadacd8d453c1752e547c76e9c99ac44"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e2fd3bfbff3c5d74fef31a722f729bf93500910db650c925c2d6ef879a7e51cb"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c4f9665de60b1b0e99bcd6be4f17d90339698ce954cfd8d9cf4f91c995165a92"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ba92837e4a9aa6a508c8d2d7883ed5a8f6c308c89a4790e1e447a220deb79a85"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-win32.whl", hash = "sha256:84a461d4d84ae1295871329b346a97f68eade8c53b6ed9a7ca2d7467f3c8ff6f"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b55aec3565b65f56455eebc9b9f34130440404f27fe21c3b375bf1ea4d8fbae6"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:87c33a52407e4c41f3b70a9c2d3f6056d88b10dad7695be708c5021673f55623"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:aecba1723ae35330a008418a91ea6cfcedf6d31e5fbaa056a166462ff066d500"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:2630b6240b495dfab90aebe159ff784d08ea999aa4b0d17efa734055a07d2f44"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:7aef0c91e2c0fbca6fc68e7555aa60ef7008a739cbe045541e438373bc54d2b0"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e021e87faa76ae0d413b619fe2b65ab9a037f24c60a1e6cc43457ae20de6dc6"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d3e924cfc503018a714f94a49a149fdc0b644eaead5d1f089330399134fa028a"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:c87b72589133f0346a1cb8d5ecca4b933e3c9b64656c9d175270a000e73b288d"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1db89609c06afa1a214a69a462ea741cf735b29a57530478c06eb81dd403de99"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-win32.whl", hash = "sha256:473bcb5f82924b1becbb637b63303ec8d10e84c8d241119419897a26116515d2"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-win_amd64.whl", hash = "sha256:a98cd7d17e9f7ce244c0803cad3c23a7d379c301ba618a5fa76a67d116618b98"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-win_arm64.whl", hash = "sha256:b0fdbcf513833809c882823f98dc2f931cf659d9a1429616ac3adebb49f5db94"}, + {file = "argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:6dca33a9859abf613e22733131fc9194091c1fa7cb3e131c143056b4856aa47e"}, + {file = "argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:21378b40e1b8d1655dd5310c84a40fc19a9aa5e6366e835ceb8576bf0fea716d"}, + {file = "argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d588dec224e2a83edbdc785a5e6f3c6cd736f46bfd4b441bbb5aa1f5085e584"}, + {file = "argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5acb4e41090d53f17ca1110c3427f0a130f944b896fc8c83973219c97f57b690"}, + {file = "argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:da0c79c23a63723aa5d782250fbf51b768abca630285262fb5144ba5ae01e520"}, + {file = "argon2_cffi_bindings-25.1.0.tar.gz", hash = "sha256:b957f3e6ea4d55d820e40ff76f450952807013d361a65d7f28acc0acbf29229d"}, +] + +[package.dependencies] +cffi = [ + {version = ">=1.0.1", markers = "python_version < \"3.14\""}, + {version = ">=2.0.0b1", markers = "python_version >= \"3.14\""}, +] + +[[package]] +name = "asyncpg" +version = "0.30.0" +description = "An asyncio PostgreSQL driver" +optional = false +python-versions = ">=3.8.0" +groups = ["main"] +files = [ + {file = "asyncpg-0.30.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bfb4dd5ae0699bad2b233672c8fc5ccbd9ad24b89afded02341786887e37927e"}, + {file = "asyncpg-0.30.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dc1f62c792752a49f88b7e6f774c26077091b44caceb1983509edc18a2222ec0"}, + {file = "asyncpg-0.30.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3152fef2e265c9c24eec4ee3d22b4f4d2703d30614b0b6753e9ed4115c8a146f"}, + {file = "asyncpg-0.30.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c7255812ac85099a0e1ffb81b10dc477b9973345793776b128a23e60148dd1af"}, + {file = "asyncpg-0.30.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:578445f09f45d1ad7abddbff2a3c7f7c291738fdae0abffbeb737d3fc3ab8b75"}, + {file = "asyncpg-0.30.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c42f6bb65a277ce4d93f3fba46b91a265631c8df7250592dd4f11f8b0152150f"}, + {file = "asyncpg-0.30.0-cp310-cp310-win32.whl", hash = "sha256:aa403147d3e07a267ada2ae34dfc9324e67ccc4cdca35261c8c22792ba2b10cf"}, + {file = "asyncpg-0.30.0-cp310-cp310-win_amd64.whl", hash = "sha256:fb622c94db4e13137c4c7f98834185049cc50ee01d8f657ef898b6407c7b9c50"}, + {file = "asyncpg-0.30.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5e0511ad3dec5f6b4f7a9e063591d407eee66b88c14e2ea636f187da1dcfff6a"}, + {file = "asyncpg-0.30.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:915aeb9f79316b43c3207363af12d0e6fd10776641a7de8a01212afd95bdf0ed"}, + {file = "asyncpg-0.30.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c198a00cce9506fcd0bf219a799f38ac7a237745e1d27f0e1f66d3707c84a5a"}, + {file = "asyncpg-0.30.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3326e6d7381799e9735ca2ec9fd7be4d5fef5dcbc3cb555d8a463d8460607956"}, + {file = "asyncpg-0.30.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:51da377487e249e35bd0859661f6ee2b81db11ad1f4fc036194bc9cb2ead5056"}, + {file = "asyncpg-0.30.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bc6d84136f9c4d24d358f3b02be4b6ba358abd09f80737d1ac7c444f36108454"}, + {file = "asyncpg-0.30.0-cp311-cp311-win32.whl", hash = "sha256:574156480df14f64c2d76450a3f3aaaf26105869cad3865041156b38459e935d"}, + {file = "asyncpg-0.30.0-cp311-cp311-win_amd64.whl", hash = "sha256:3356637f0bd830407b5597317b3cb3571387ae52ddc3bca6233682be88bbbc1f"}, + {file = "asyncpg-0.30.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c902a60b52e506d38d7e80e0dd5399f657220f24635fee368117b8b5fce1142e"}, + {file = "asyncpg-0.30.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:aca1548e43bbb9f0f627a04666fedaca23db0a31a84136ad1f868cb15deb6e3a"}, + {file = "asyncpg-0.30.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c2a2ef565400234a633da0eafdce27e843836256d40705d83ab7ec42074efb3"}, + {file = "asyncpg-0.30.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1292b84ee06ac8a2ad8e51c7475aa309245874b61333d97411aab835c4a2f737"}, + {file = "asyncpg-0.30.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0f5712350388d0cd0615caec629ad53c81e506b1abaaf8d14c93f54b35e3595a"}, + {file = "asyncpg-0.30.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:db9891e2d76e6f425746c5d2da01921e9a16b5a71a1c905b13f30e12a257c4af"}, + {file = "asyncpg-0.30.0-cp312-cp312-win32.whl", hash = "sha256:68d71a1be3d83d0570049cd1654a9bdfe506e794ecc98ad0873304a9f35e411e"}, + {file = "asyncpg-0.30.0-cp312-cp312-win_amd64.whl", hash = "sha256:9a0292c6af5c500523949155ec17b7fe01a00ace33b68a476d6b5059f9630305"}, + {file = "asyncpg-0.30.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:05b185ebb8083c8568ea8a40e896d5f7af4b8554b64d7719c0eaa1eb5a5c3a70"}, + {file = "asyncpg-0.30.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c47806b1a8cbb0a0db896f4cd34d89942effe353a5035c62734ab13b9f938da3"}, + {file = "asyncpg-0.30.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b6fde867a74e8c76c71e2f64f80c64c0f3163e687f1763cfaf21633ec24ec33"}, + {file = "asyncpg-0.30.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46973045b567972128a27d40001124fbc821c87a6cade040cfcd4fa8a30bcdc4"}, + {file = "asyncpg-0.30.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9110df111cabc2ed81aad2f35394a00cadf4f2e0635603db6ebbd0fc896f46a4"}, + {file = "asyncpg-0.30.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:04ff0785ae7eed6cc138e73fc67b8e51d54ee7a3ce9b63666ce55a0bf095f7ba"}, + {file = "asyncpg-0.30.0-cp313-cp313-win32.whl", hash = "sha256:ae374585f51c2b444510cdf3595b97ece4f233fde739aa14b50e0d64e8a7a590"}, + {file = "asyncpg-0.30.0-cp313-cp313-win_amd64.whl", hash = "sha256:f59b430b8e27557c3fb9869222559f7417ced18688375825f8f12302c34e915e"}, + {file = "asyncpg-0.30.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:29ff1fc8b5bf724273782ff8b4f57b0f8220a1b2324184846b39d1ab4122031d"}, + {file = "asyncpg-0.30.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:64e899bce0600871b55368b8483e5e3e7f1860c9482e7f12e0a771e747988168"}, + {file = "asyncpg-0.30.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b290f4726a887f75dcd1b3006f484252db37602313f806e9ffc4e5996cfe5cb"}, + {file = "asyncpg-0.30.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f86b0e2cd3f1249d6fe6fd6cfe0cd4538ba994e2d8249c0491925629b9104d0f"}, + {file = "asyncpg-0.30.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:393af4e3214c8fa4c7b86da6364384c0d1b3298d45803375572f415b6f673f38"}, + {file = "asyncpg-0.30.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:fd4406d09208d5b4a14db9a9dbb311b6d7aeeab57bded7ed2f8ea41aeef39b34"}, + {file = "asyncpg-0.30.0-cp38-cp38-win32.whl", hash = "sha256:0b448f0150e1c3b96cb0438a0d0aa4871f1472e58de14a3ec320dbb2798fb0d4"}, + {file = "asyncpg-0.30.0-cp38-cp38-win_amd64.whl", hash = "sha256:f23b836dd90bea21104f69547923a02b167d999ce053f3d502081acea2fba15b"}, + {file = "asyncpg-0.30.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6f4e83f067b35ab5e6371f8a4c93296e0439857b4569850b178a01385e82e9ad"}, + {file = "asyncpg-0.30.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5df69d55add4efcd25ea2a3b02025b669a285b767bfbf06e356d68dbce4234ff"}, + {file = "asyncpg-0.30.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3479a0d9a852c7c84e822c073622baca862d1217b10a02dd57ee4a7a081f708"}, + {file = "asyncpg-0.30.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26683d3b9a62836fad771a18ecf4659a30f348a561279d6227dab96182f46144"}, + {file = "asyncpg-0.30.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1b982daf2441a0ed314bd10817f1606f1c28b1136abd9e4f11335358c2c631cb"}, + {file = "asyncpg-0.30.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1c06a3a50d014b303e5f6fc1e5f95eb28d2cee89cf58384b700da621e5d5e547"}, + {file = "asyncpg-0.30.0-cp39-cp39-win32.whl", hash = "sha256:1b11a555a198b08f5c4baa8f8231c74a366d190755aa4f99aacec5970afe929a"}, + {file = "asyncpg-0.30.0-cp39-cp39-win_amd64.whl", hash = "sha256:8b684a3c858a83cd876f05958823b68e8d14ec01bb0c0d14a6704c5bf9711773"}, + {file = "asyncpg-0.30.0.tar.gz", hash = "sha256:c551e9928ab6707602f44811817f82ba3c446e018bfe1d3abecc8ba5f3eac851"}, +] + +[package.extras] +docs = ["Sphinx (>=8.1.3,<8.2.0)", "sphinx-rtd-theme (>=1.2.2)"] +gssauth = ["gssapi", "sspilib"] +test = ["distro (>=1.9.0,<1.10.0)", "flake8 (>=6.1,<7.0)", "flake8-pyi (>=24.1.0,<24.2.0)", "gssapi", "k5test", "mypy (>=1.8.0,<1.9.0)", "sspilib", "uvloop (>=0.15.3)"] + +[[package]] +name = "attrs" +version = "25.4.0" +description = "Classes Without Boilerplate" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373"}, + {file = "attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11"}, +] + +[[package]] +name = "certifi" +version = "2025.10.5" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "certifi-2025.10.5-py3-none-any.whl", hash = "sha256:0f212c2744a9bb6de0c56639a6f68afe01ecd92d91f14ae897c4fe7bbeeef0de"}, + {file = "certifi-2025.10.5.tar.gz", hash = "sha256:47c09d31ccf2acf0be3f701ea53595ee7e0b8fa08801c6624be771df09ae7b43"}, +] + +[[package]] +name = "cffi" +version = "2.0.0" +description = "Foreign Function Interface for Python calling C code." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44"}, + {file = "cffi-2.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49"}, + {file = "cffi-2.0.0-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c"}, + {file = "cffi-2.0.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb"}, + {file = "cffi-2.0.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0"}, + {file = "cffi-2.0.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4"}, + {file = "cffi-2.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453"}, + {file = "cffi-2.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495"}, + {file = "cffi-2.0.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5"}, + {file = "cffi-2.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb"}, + {file = "cffi-2.0.0-cp310-cp310-win32.whl", hash = "sha256:1f72fb8906754ac8a2cc3f9f5aaa298070652a0ffae577e0ea9bd480dc3c931a"}, + {file = "cffi-2.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:b18a3ed7d5b3bd8d9ef7a8cb226502c6bf8308df1525e1cc676c3680e7176739"}, + {file = "cffi-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe"}, + {file = "cffi-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c"}, + {file = "cffi-2.0.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92"}, + {file = "cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93"}, + {file = "cffi-2.0.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5"}, + {file = "cffi-2.0.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664"}, + {file = "cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26"}, + {file = "cffi-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9"}, + {file = "cffi-2.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414"}, + {file = "cffi-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743"}, + {file = "cffi-2.0.0-cp311-cp311-win32.whl", hash = "sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5"}, + {file = "cffi-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5"}, + {file = "cffi-2.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d"}, + {file = "cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d"}, + {file = "cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c"}, + {file = "cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe"}, + {file = "cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062"}, + {file = "cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e"}, + {file = "cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037"}, + {file = "cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba"}, + {file = "cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94"}, + {file = "cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187"}, + {file = "cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18"}, + {file = "cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5"}, + {file = "cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6"}, + {file = "cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb"}, + {file = "cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca"}, + {file = "cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b"}, + {file = "cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b"}, + {file = "cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2"}, + {file = "cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3"}, + {file = "cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26"}, + {file = "cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c"}, + {file = "cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b"}, + {file = "cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27"}, + {file = "cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75"}, + {file = "cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91"}, + {file = "cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5"}, + {file = "cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13"}, + {file = "cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b"}, + {file = "cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c"}, + {file = "cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef"}, + {file = "cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775"}, + {file = "cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205"}, + {file = "cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1"}, + {file = "cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f"}, + {file = "cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25"}, + {file = "cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad"}, + {file = "cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9"}, + {file = "cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d"}, + {file = "cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c"}, + {file = "cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8"}, + {file = "cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc"}, + {file = "cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592"}, + {file = "cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512"}, + {file = "cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4"}, + {file = "cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e"}, + {file = "cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6"}, + {file = "cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9"}, + {file = "cffi-2.0.0-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:fe562eb1a64e67dd297ccc4f5addea2501664954f2692b69a76449ec7913ecbf"}, + {file = "cffi-2.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:de8dad4425a6ca6e4e5e297b27b5c824ecc7581910bf9aee86cb6835e6812aa7"}, + {file = "cffi-2.0.0-cp39-cp39-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:4647afc2f90d1ddd33441e5b0e85b16b12ddec4fca55f0d9671fef036ecca27c"}, + {file = "cffi-2.0.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3f4d46d8b35698056ec29bca21546e1551a205058ae1a181d871e278b0b28165"}, + {file = "cffi-2.0.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:e6e73b9e02893c764e7e8d5bb5ce277f1a009cd5243f8228f75f842bf937c534"}, + {file = "cffi-2.0.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:cb527a79772e5ef98fb1d700678fe031e353e765d1ca2d409c92263c6d43e09f"}, + {file = "cffi-2.0.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:61d028e90346df14fedc3d1e5441df818d095f3b87d286825dfcbd6459b7ef63"}, + {file = "cffi-2.0.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0f6084a0ea23d05d20c3edcda20c3d006f9b6f3fefeac38f59262e10cef47ee2"}, + {file = "cffi-2.0.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:1cd13c99ce269b3ed80b417dcd591415d3372bcac067009b6e0f59c7d4015e65"}, + {file = "cffi-2.0.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:89472c9762729b5ae1ad974b777416bfda4ac5642423fa93bd57a09204712322"}, + {file = "cffi-2.0.0-cp39-cp39-win32.whl", hash = "sha256:2081580ebb843f759b9f617314a24ed5738c51d2aee65d31e02f6f7a2b97707a"}, + {file = "cffi-2.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:b882b3df248017dba09d6b16defe9b5c407fe32fc7c65a9c69798e6175601be9"}, + {file = "cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529"}, +] + +[package.dependencies] +pycparser = {version = "*", markers = "implementation_name != \"PyPy\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["main", "dev"] +markers = "sys_platform == \"win32\"" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "common" +version = "0.1.0" +description = "" +optional = false +python-versions = ">=3.12,<4.0" +groups = ["main"] +files = [] +develop = true + +[package.dependencies] +dapr = "1.16.0" +fastapi = "^0.115.6" +minio = "^7.2.14" +pydantic = "^2.10.5" +structlog = "^25.1.0" + +[package.source] +type = "directory" +url = "../common" + +[[package]] +name = "dapr" +version = "1.16.0" +description = "The official release of Dapr Python SDK." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "dapr-1.16.0-py3-none-any.whl", hash = "sha256:076dd559a0b450eae24b1c2ae779c9299ed3e06a05c1f72719a6613af8d19ced"}, + {file = "dapr-1.16.0.tar.gz", hash = "sha256:c7e3d005552a598d07608d0d502b2bc432e86678f94b2beccc13a096b9198684"}, +] + +[package.dependencies] +aiohttp = ">=3.9.0b0" +grpcio = ">=1.37.0" +grpcio-status = ">=1.37.0" +protobuf = ">=4.22" +python-dateutil = ">=2.8.1" +typing-extensions = ">=4.4.0" + +[[package]] +name = "fastapi" +version = "0.115.14" +description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "fastapi-0.115.14-py3-none-any.whl", hash = "sha256:6c0c8bf9420bd58f565e585036d971872472b4f7d3f6c73b698e10cffdefb3ca"}, + {file = "fastapi-0.115.14.tar.gz", hash = "sha256:b1de15cdc1c499a4da47914db35d0e4ef8f1ce62b624e94e0e5824421df99739"}, +] + +[package.dependencies] +pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0" +starlette = ">=0.40.0,<0.47.0" +typing-extensions = ">=4.8.0" + +[package.extras] +all = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=3.1.5)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.18)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"] +standard = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "jinja2 (>=3.1.5)", "python-multipart (>=0.0.18)", "uvicorn[standard] (>=0.12.0)"] + +[[package]] +name = "frozenlist" +version = "1.8.0" +description = "A list-like structure which implements collections.abc.MutableSequence" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "frozenlist-1.8.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b37f6d31b3dcea7deb5e9696e529a6aa4a898adc33db82da12e4c60a7c4d2011"}, + {file = "frozenlist-1.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ef2b7b394f208233e471abc541cc6991f907ffd47dc72584acee3147899d6565"}, + {file = "frozenlist-1.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a88f062f072d1589b7b46e951698950e7da00442fc1cacbe17e19e025dc327ad"}, + {file = "frozenlist-1.8.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f57fb59d9f385710aa7060e89410aeb5058b99e62f4d16b08b91986b9a2140c2"}, + {file = "frozenlist-1.8.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:799345ab092bee59f01a915620b5d014698547afd011e691a208637312db9186"}, + {file = "frozenlist-1.8.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c23c3ff005322a6e16f71bf8692fcf4d5a304aaafe1e262c98c6d4adc7be863e"}, + {file = "frozenlist-1.8.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8a76ea0f0b9dfa06f254ee06053d93a600865b3274358ca48a352ce4f0798450"}, + {file = "frozenlist-1.8.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c7366fe1418a6133d5aa824ee53d406550110984de7637d65a178010f759c6ef"}, + {file = "frozenlist-1.8.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:13d23a45c4cebade99340c4165bd90eeb4a56c6d8a9d8aa49568cac19a6d0dc4"}, + {file = "frozenlist-1.8.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:e4a3408834f65da56c83528fb52ce7911484f0d1eaf7b761fc66001db1646eff"}, + {file = "frozenlist-1.8.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:42145cd2748ca39f32801dad54aeea10039da6f86e303659db90db1c4b614c8c"}, + {file = "frozenlist-1.8.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:e2de870d16a7a53901e41b64ffdf26f2fbb8917b3e6ebf398098d72c5b20bd7f"}, + {file = "frozenlist-1.8.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:20e63c9493d33ee48536600d1a5c95eefc870cd71e7ab037763d1fbb89cc51e7"}, + {file = "frozenlist-1.8.0-cp310-cp310-win32.whl", hash = "sha256:adbeebaebae3526afc3c96fad434367cafbfd1b25d72369a9e5858453b1bb71a"}, + {file = "frozenlist-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:667c3777ca571e5dbeb76f331562ff98b957431df140b54c85fd4d52eea8d8f6"}, + {file = "frozenlist-1.8.0-cp310-cp310-win_arm64.whl", hash = "sha256:80f85f0a7cc86e7a54c46d99c9e1318ff01f4687c172ede30fd52d19d1da1c8e"}, + {file = "frozenlist-1.8.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:09474e9831bc2b2199fad6da3c14c7b0fbdd377cce9d3d77131be28906cb7d84"}, + {file = "frozenlist-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:17c883ab0ab67200b5f964d2b9ed6b00971917d5d8a92df149dc2c9779208ee9"}, + {file = "frozenlist-1.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fa47e444b8ba08fffd1c18e8cdb9a75db1b6a27f17507522834ad13ed5922b93"}, + {file = "frozenlist-1.8.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2552f44204b744fba866e573be4c1f9048d6a324dfe14475103fd51613eb1d1f"}, + {file = "frozenlist-1.8.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:957e7c38f250991e48a9a73e6423db1bb9dd14e722a10f6b8bb8e16a0f55f695"}, + {file = "frozenlist-1.8.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8585e3bb2cdea02fc88ffa245069c36555557ad3609e83be0ec71f54fd4abb52"}, + {file = "frozenlist-1.8.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:edee74874ce20a373d62dc28b0b18b93f645633c2943fd90ee9d898550770581"}, + {file = "frozenlist-1.8.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c9a63152fe95756b85f31186bddf42e4c02c6321207fd6601a1c89ebac4fe567"}, + {file = "frozenlist-1.8.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b6db2185db9be0a04fecf2f241c70b63b1a242e2805be291855078f2b404dd6b"}, + {file = "frozenlist-1.8.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:f4be2e3d8bc8aabd566f8d5b8ba7ecc09249d74ba3c9ed52e54dc23a293f0b92"}, + {file = "frozenlist-1.8.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:c8d1634419f39ea6f5c427ea2f90ca85126b54b50837f31497f3bf38266e853d"}, + {file = "frozenlist-1.8.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:1a7fa382a4a223773ed64242dbe1c9c326ec09457e6b8428efb4118c685c3dfd"}, + {file = "frozenlist-1.8.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:11847b53d722050808926e785df837353bd4d75f1d494377e59b23594d834967"}, + {file = "frozenlist-1.8.0-cp311-cp311-win32.whl", hash = "sha256:27c6e8077956cf73eadd514be8fb04d77fc946a7fe9f7fe167648b0b9085cc25"}, + {file = "frozenlist-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:ac913f8403b36a2c8610bbfd25b8013488533e71e62b4b4adce9c86c8cea905b"}, + {file = "frozenlist-1.8.0-cp311-cp311-win_arm64.whl", hash = "sha256:d4d3214a0f8394edfa3e303136d0575eece0745ff2b47bd2cb2e66dd92d4351a"}, + {file = "frozenlist-1.8.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:78f7b9e5d6f2fdb88cdde9440dc147259b62b9d3b019924def9f6478be254ac1"}, + {file = "frozenlist-1.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:229bf37d2e4acdaf808fd3f06e854a4a7a3661e871b10dc1f8f1896a3b05f18b"}, + {file = "frozenlist-1.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f833670942247a14eafbb675458b4e61c82e002a148f49e68257b79296e865c4"}, + {file = "frozenlist-1.8.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:494a5952b1c597ba44e0e78113a7266e656b9794eec897b19ead706bd7074383"}, + {file = "frozenlist-1.8.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96f423a119f4777a4a056b66ce11527366a8bb92f54e541ade21f2374433f6d4"}, + {file = "frozenlist-1.8.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3462dd9475af2025c31cc61be6652dfa25cbfb56cbbf52f4ccfe029f38decaf8"}, + {file = "frozenlist-1.8.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4c800524c9cd9bac5166cd6f55285957fcfc907db323e193f2afcd4d9abd69b"}, + {file = "frozenlist-1.8.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d6a5df73acd3399d893dafc71663ad22534b5aa4f94e8a2fabfe856c3c1b6a52"}, + {file = "frozenlist-1.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:405e8fe955c2280ce66428b3ca55e12b3c4e9c336fb2103a4937e891c69a4a29"}, + {file = "frozenlist-1.8.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:908bd3f6439f2fef9e85031b59fd4f1297af54415fb60e4254a95f75b3cab3f3"}, + {file = "frozenlist-1.8.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:294e487f9ec720bd8ffcebc99d575f7eff3568a08a253d1ee1a0378754b74143"}, + {file = "frozenlist-1.8.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:74c51543498289c0c43656701be6b077f4b265868fa7f8a8859c197006efb608"}, + {file = "frozenlist-1.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:776f352e8329135506a1d6bf16ac3f87bc25b28e765949282dcc627af36123aa"}, + {file = "frozenlist-1.8.0-cp312-cp312-win32.whl", hash = "sha256:433403ae80709741ce34038da08511d4a77062aa924baf411ef73d1146e74faf"}, + {file = "frozenlist-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:34187385b08f866104f0c0617404c8eb08165ab1272e884abc89c112e9c00746"}, + {file = "frozenlist-1.8.0-cp312-cp312-win_arm64.whl", hash = "sha256:fe3c58d2f5db5fbd18c2987cba06d51b0529f52bc3a6cdc33d3f4eab725104bd"}, + {file = "frozenlist-1.8.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8d92f1a84bb12d9e56f818b3a746f3efba93c1b63c8387a73dde655e1e42282a"}, + {file = "frozenlist-1.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:96153e77a591c8adc2ee805756c61f59fef4cf4073a9275ee86fe8cba41241f7"}, + {file = "frozenlist-1.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f21f00a91358803399890ab167098c131ec2ddd5f8f5fd5fe9c9f2c6fcd91e40"}, + {file = "frozenlist-1.8.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fb30f9626572a76dfe4293c7194a09fb1fe93ba94c7d4f720dfae3b646b45027"}, + {file = "frozenlist-1.8.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eaa352d7047a31d87dafcacbabe89df0aa506abb5b1b85a2fb91bc3faa02d822"}, + {file = "frozenlist-1.8.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:03ae967b4e297f58f8c774c7eabcce57fe3c2434817d4385c50661845a058121"}, + {file = "frozenlist-1.8.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f6292f1de555ffcc675941d65fffffb0a5bcd992905015f85d0592201793e0e5"}, + {file = "frozenlist-1.8.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29548f9b5b5e3460ce7378144c3010363d8035cea44bc0bf02d57f5a685e084e"}, + {file = "frozenlist-1.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ec3cc8c5d4084591b4237c0a272cc4f50a5b03396a47d9caaf76f5d7b38a4f11"}, + {file = "frozenlist-1.8.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:517279f58009d0b1f2e7c1b130b377a349405da3f7621ed6bfae50b10adf20c1"}, + {file = "frozenlist-1.8.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:db1e72ede2d0d7ccb213f218df6a078a9c09a7de257c2fe8fcef16d5925230b1"}, + {file = "frozenlist-1.8.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b4dec9482a65c54a5044486847b8a66bf10c9cb4926d42927ec4e8fd5db7fed8"}, + {file = "frozenlist-1.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:21900c48ae04d13d416f0e1e0c4d81f7931f73a9dfa0b7a8746fb2fe7dd970ed"}, + {file = "frozenlist-1.8.0-cp313-cp313-win32.whl", hash = "sha256:8b7b94a067d1c504ee0b16def57ad5738701e4ba10cec90529f13fa03c833496"}, + {file = "frozenlist-1.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:878be833caa6a3821caf85eb39c5ba92d28e85df26d57afb06b35b2efd937231"}, + {file = "frozenlist-1.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:44389d135b3ff43ba8cc89ff7f51f5a0bb6b63d829c8300f79a2fe4fe61bcc62"}, + {file = "frozenlist-1.8.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:e25ac20a2ef37e91c1b39938b591457666a0fa835c7783c3a8f33ea42870db94"}, + {file = "frozenlist-1.8.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:07cdca25a91a4386d2e76ad992916a85038a9b97561bf7a3fd12d5d9ce31870c"}, + {file = "frozenlist-1.8.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4e0c11f2cc6717e0a741f84a527c52616140741cd812a50422f83dc31749fb52"}, + {file = "frozenlist-1.8.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b3210649ee28062ea6099cfda39e147fa1bc039583c8ee4481cb7811e2448c51"}, + {file = "frozenlist-1.8.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:581ef5194c48035a7de2aefc72ac6539823bb71508189e5de01d60c9dcd5fa65"}, + {file = "frozenlist-1.8.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3ef2d026f16a2b1866e1d86fc4e1291e1ed8a387b2c333809419a2f8b3a77b82"}, + {file = "frozenlist-1.8.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5500ef82073f599ac84d888e3a8c1f77ac831183244bfd7f11eaa0289fb30714"}, + {file = "frozenlist-1.8.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:50066c3997d0091c411a66e710f4e11752251e6d2d73d70d8d5d4c76442a199d"}, + {file = "frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:5c1c8e78426e59b3f8005e9b19f6ff46e5845895adbde20ece9218319eca6506"}, + {file = "frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:eefdba20de0d938cec6a89bd4d70f346a03108a19b9df4248d3cf0d88f1b0f51"}, + {file = "frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:cf253e0e1c3ceb4aaff6df637ce033ff6535fb8c70a764a8f46aafd3d6ab798e"}, + {file = "frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:032efa2674356903cd0261c4317a561a6850f3ac864a63fc1583147fb05a79b0"}, + {file = "frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6da155091429aeba16851ecb10a9104a108bcd32f6c1642867eadaee401c1c41"}, + {file = "frozenlist-1.8.0-cp313-cp313t-win32.whl", hash = "sha256:0f96534f8bfebc1a394209427d0f8a63d343c9779cda6fc25e8e121b5fd8555b"}, + {file = "frozenlist-1.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5d63a068f978fc69421fb0e6eb91a9603187527c86b7cd3f534a5b77a592b888"}, + {file = "frozenlist-1.8.0-cp313-cp313t-win_arm64.whl", hash = "sha256:bf0a7e10b077bf5fb9380ad3ae8ce20ef919a6ad93b4552896419ac7e1d8e042"}, + {file = "frozenlist-1.8.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cee686f1f4cadeb2136007ddedd0aaf928ab95216e7691c63e50a8ec066336d0"}, + {file = "frozenlist-1.8.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:119fb2a1bd47307e899c2fac7f28e85b9a543864df47aa7ec9d3c1b4545f096f"}, + {file = "frozenlist-1.8.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4970ece02dbc8c3a92fcc5228e36a3e933a01a999f7094ff7c23fbd2beeaa67c"}, + {file = "frozenlist-1.8.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:cba69cb73723c3f329622e34bdbf5ce1f80c21c290ff04256cff1cd3c2036ed2"}, + {file = "frozenlist-1.8.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:778a11b15673f6f1df23d9586f83c4846c471a8af693a22e066508b77d201ec8"}, + {file = "frozenlist-1.8.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0325024fe97f94c41c08872db482cf8ac4800d80e79222c6b0b7b162d5b13686"}, + {file = "frozenlist-1.8.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:97260ff46b207a82a7567b581ab4190bd4dfa09f4db8a8b49d1a958f6aa4940e"}, + {file = "frozenlist-1.8.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:54b2077180eb7f83dd52c40b2750d0a9f175e06a42e3213ce047219de902717a"}, + {file = "frozenlist-1.8.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2f05983daecab868a31e1da44462873306d3cbfd76d1f0b5b69c473d21dbb128"}, + {file = "frozenlist-1.8.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:33f48f51a446114bc5d251fb2954ab0164d5be02ad3382abcbfe07e2531d650f"}, + {file = "frozenlist-1.8.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:154e55ec0655291b5dd1b8731c637ecdb50975a2ae70c606d100750a540082f7"}, + {file = "frozenlist-1.8.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:4314debad13beb564b708b4a496020e5306c7333fa9a3ab90374169a20ffab30"}, + {file = "frozenlist-1.8.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:073f8bf8becba60aa931eb3bc420b217bb7d5b8f4750e6f8b3be7f3da85d38b7"}, + {file = "frozenlist-1.8.0-cp314-cp314-win32.whl", hash = "sha256:bac9c42ba2ac65ddc115d930c78d24ab8d4f465fd3fc473cdedfccadb9429806"}, + {file = "frozenlist-1.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:3e0761f4d1a44f1d1a47996511752cf3dcec5bbdd9cc2b4fe595caf97754b7a0"}, + {file = "frozenlist-1.8.0-cp314-cp314-win_arm64.whl", hash = "sha256:d1eaff1d00c7751b7c6662e9c5ba6eb2c17a2306ba5e2a37f24ddf3cc953402b"}, + {file = "frozenlist-1.8.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:d3bb933317c52d7ea5004a1c442eef86f426886fba134ef8cf4226ea6ee1821d"}, + {file = "frozenlist-1.8.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:8009897cdef112072f93a0efdce29cd819e717fd2f649ee3016efd3cd885a7ed"}, + {file = "frozenlist-1.8.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2c5dcbbc55383e5883246d11fd179782a9d07a986c40f49abe89ddf865913930"}, + {file = "frozenlist-1.8.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:39ecbc32f1390387d2aa4f5a995e465e9e2f79ba3adcac92d68e3e0afae6657c"}, + {file = "frozenlist-1.8.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92db2bf818d5cc8d9c1f1fc56b897662e24ea5adb36ad1f1d82875bd64e03c24"}, + {file = "frozenlist-1.8.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2dc43a022e555de94c3b68a4ef0b11c4f747d12c024a520c7101709a2144fb37"}, + {file = "frozenlist-1.8.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cb89a7f2de3602cfed448095bab3f178399646ab7c61454315089787df07733a"}, + {file = "frozenlist-1.8.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:33139dc858c580ea50e7e60a1b0ea003efa1fd42e6ec7fdbad78fff65fad2fd2"}, + {file = "frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:168c0969a329b416119507ba30b9ea13688fafffac1b7822802537569a1cb0ef"}, + {file = "frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:28bd570e8e189d7f7b001966435f9dac6718324b5be2990ac496cf1ea9ddb7fe"}, + {file = "frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b2a095d45c5d46e5e79ba1e5b9cb787f541a8dee0433836cea4b96a2c439dcd8"}, + {file = "frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:eab8145831a0d56ec9c4139b6c3e594c7a83c2c8be25d5bcf2d86136a532287a"}, + {file = "frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:974b28cf63cc99dfb2188d8d222bc6843656188164848c4f679e63dae4b0708e"}, + {file = "frozenlist-1.8.0-cp314-cp314t-win32.whl", hash = "sha256:342c97bf697ac5480c0a7ec73cd700ecfa5a8a40ac923bd035484616efecc2df"}, + {file = "frozenlist-1.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:06be8f67f39c8b1dc671f5d83aaefd3358ae5cdcf8314552c57e7ed3e6475bdd"}, + {file = "frozenlist-1.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:102e6314ca4da683dca92e3b1355490fed5f313b768500084fbe6371fddfdb79"}, + {file = "frozenlist-1.8.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:d8b7138e5cd0647e4523d6685b0eac5d4be9a184ae9634492f25c6eb38c12a47"}, + {file = "frozenlist-1.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a6483e309ca809f1efd154b4d37dc6d9f61037d6c6a81c2dc7a15cb22c8c5dca"}, + {file = "frozenlist-1.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1b9290cf81e95e93fdf90548ce9d3c1211cf574b8e3f4b3b7cb0537cf2227068"}, + {file = "frozenlist-1.8.0-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:59a6a5876ca59d1b63af8cd5e7ffffb024c3dc1e9cf9301b21a2e76286505c95"}, + {file = "frozenlist-1.8.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6dc4126390929823e2d2d9dc79ab4046ed74680360fc5f38b585c12c66cdf459"}, + {file = "frozenlist-1.8.0-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:332db6b2563333c5671fecacd085141b5800cb866be16d5e3eb15a2086476675"}, + {file = "frozenlist-1.8.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9ff15928d62a0b80bb875655c39bf517938c7d589554cbd2669be42d97c2cb61"}, + {file = "frozenlist-1.8.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7bf6cdf8e07c8151fba6fe85735441240ec7f619f935a5205953d58009aef8c6"}, + {file = "frozenlist-1.8.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:48e6d3f4ec5c7273dfe83ff27c91083c6c9065af655dc2684d2c200c94308bb5"}, + {file = "frozenlist-1.8.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:1a7607e17ad33361677adcd1443edf6f5da0ce5e5377b798fba20fae194825f3"}, + {file = "frozenlist-1.8.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:5a3a935c3a4e89c733303a2d5a7c257ea44af3a56c8202df486b7f5de40f37e1"}, + {file = "frozenlist-1.8.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:940d4a017dbfed9daf46a3b086e1d2167e7012ee297fef9e1c545c4d022f5178"}, + {file = "frozenlist-1.8.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:b9be22a69a014bc47e78072d0ecae716f5eb56c15238acca0f43d6eb8e4a5bda"}, + {file = "frozenlist-1.8.0-cp39-cp39-win32.whl", hash = "sha256:1aa77cb5697069af47472e39612976ed05343ff2e84a3dcf15437b232cbfd087"}, + {file = "frozenlist-1.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:7398c222d1d405e796970320036b1b563892b65809d9e5261487bb2c7f7b5c6a"}, + {file = "frozenlist-1.8.0-cp39-cp39-win_arm64.whl", hash = "sha256:b4f3b365f31c6cd4af24545ca0a244a53688cad8834e32f56831c4923b50a103"}, + {file = "frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d"}, + {file = "frozenlist-1.8.0.tar.gz", hash = "sha256:3ede829ed8d842f6cd48fc7081d7a41001a56f1f38603f9d49bf3020d59a31ad"}, +] + +[[package]] +name = "googleapis-common-protos" +version = "1.70.0" +description = "Common protobufs used in Google APIs" +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "googleapis_common_protos-1.70.0-py3-none-any.whl", hash = "sha256:b8bfcca8c25a2bb253e0e0b0adaf8c00773e5e6af6fd92397576680b807e0fd8"}, + {file = "googleapis_common_protos-1.70.0.tar.gz", hash = "sha256:0e1b44e0ea153e6594f9f394fef15193a68aaaea2d843f83e2742717ca753257"}, +] + +[package.dependencies] +protobuf = ">=3.20.2,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<7.0.0" + +[package.extras] +grpc = ["grpcio (>=1.44.0,<2.0.0)"] + +[[package]] +name = "grpcio" +version = "1.75.1" +description = "HTTP/2-based RPC framework" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "grpcio-1.75.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:1712b5890b22547dd29f3215c5788d8fc759ce6dd0b85a6ba6e2731f2d04c088"}, + {file = "grpcio-1.75.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:8d04e101bba4b55cea9954e4aa71c24153ba6182481b487ff376da28d4ba46cf"}, + {file = "grpcio-1.75.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:683cfc70be0c1383449097cba637317e4737a357cfc185d887fd984206380403"}, + {file = "grpcio-1.75.1-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:491444c081a54dcd5e6ada57314321ae526377f498d4aa09d975c3241c5b9e1c"}, + {file = "grpcio-1.75.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ce08d4e112d0d38487c2b631ec8723deac9bc404e9c7b1011426af50a79999e4"}, + {file = "grpcio-1.75.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5a2acda37fc926ccc4547977ac3e56b1df48fe200de968e8c8421f6e3093df6c"}, + {file = "grpcio-1.75.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:745c5fe6bf05df6a04bf2d11552c7d867a2690759e7ab6b05c318a772739bd75"}, + {file = "grpcio-1.75.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:259526a7159d39e2db40d566fe3e8f8e034d0fb2db5bf9c00e09aace655a4c2b"}, + {file = "grpcio-1.75.1-cp310-cp310-win32.whl", hash = "sha256:f4b29b9aabe33fed5df0a85e5f13b09ff25e2c05bd5946d25270a8bd5682dac9"}, + {file = "grpcio-1.75.1-cp310-cp310-win_amd64.whl", hash = "sha256:cf2e760978dcce7ff7d465cbc7e276c3157eedc4c27aa6de7b594c7a295d3d61"}, + {file = "grpcio-1.75.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:573855ca2e58e35032aff30bfbd1ee103fbcf4472e4b28d4010757700918e326"}, + {file = "grpcio-1.75.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:6a4996a2c8accc37976dc142d5991adf60733e223e5c9a2219e157dc6a8fd3a2"}, + {file = "grpcio-1.75.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b1ea1bbe77ecbc1be00af2769f4ae4a88ce93be57a4f3eebd91087898ed749f9"}, + {file = "grpcio-1.75.1-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:e5b425aee54cc5e3e3c58f00731e8a33f5567965d478d516d35ef99fd648ab68"}, + {file = "grpcio-1.75.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0049a7bf547dafaeeb1db17079ce79596c298bfe308fc084d023c8907a845b9a"}, + {file = "grpcio-1.75.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5b8ea230c7f77c0a1a3208a04a1eda164633fb0767b4cefd65a01079b65e5b1f"}, + {file = "grpcio-1.75.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:36990d629c3c9fb41e546414e5af52d0a7af37ce7113d9682c46d7e2919e4cca"}, + {file = "grpcio-1.75.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b10ad908118d38c2453ade7ff790e5bce36580c3742919007a2a78e3a1e521ca"}, + {file = "grpcio-1.75.1-cp311-cp311-win32.whl", hash = "sha256:d6be2b5ee7bea656c954dcf6aa8093c6f0e6a3ef9945c99d99fcbfc88c5c0bfe"}, + {file = "grpcio-1.75.1-cp311-cp311-win_amd64.whl", hash = "sha256:61c692fb05956b17dd6d1ab480f7f10ad0536dba3bc8fd4e3c7263dc244ed772"}, + {file = "grpcio-1.75.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:7b888b33cd14085d86176b1628ad2fcbff94cfbbe7809465097aa0132e58b018"}, + {file = "grpcio-1.75.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:8775036efe4ad2085975531d221535329f5dac99b6c2a854a995456098f99546"}, + {file = "grpcio-1.75.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:bb658f703468d7fbb5dcc4037c65391b7dc34f808ac46ed9136c24fc5eeb041d"}, + {file = "grpcio-1.75.1-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:4b7177a1cdb3c51b02b0c0a256b0a72fdab719600a693e0e9037949efffb200b"}, + {file = "grpcio-1.75.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7d4fa6ccc3ec2e68a04f7b883d354d7fea22a34c44ce535a2f0c0049cf626ddf"}, + {file = "grpcio-1.75.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3d86880ecaeb5b2f0a8afa63824de93adb8ebe4e49d0e51442532f4e08add7d6"}, + {file = "grpcio-1.75.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a8041d2f9e8a742aeae96f4b047ee44e73619f4f9d24565e84d5446c623673b6"}, + {file = "grpcio-1.75.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3652516048bf4c314ce12be37423c79829f46efffb390ad64149a10c6071e8de"}, + {file = "grpcio-1.75.1-cp312-cp312-win32.whl", hash = "sha256:44b62345d8403975513af88da2f3d5cc76f73ca538ba46596f92a127c2aea945"}, + {file = "grpcio-1.75.1-cp312-cp312-win_amd64.whl", hash = "sha256:b1e191c5c465fa777d4cafbaacf0c01e0d5278022082c0abbd2ee1d6454ed94d"}, + {file = "grpcio-1.75.1-cp313-cp313-linux_armv7l.whl", hash = "sha256:3bed22e750d91d53d9e31e0af35a7b0b51367e974e14a4ff229db5b207647884"}, + {file = "grpcio-1.75.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:5b8f381eadcd6ecaa143a21e9e80a26424c76a0a9b3d546febe6648f3a36a5ac"}, + {file = "grpcio-1.75.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5bf4001d3293e3414d0cf99ff9b1139106e57c3a66dfff0c5f60b2a6286ec133"}, + {file = "grpcio-1.75.1-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:9f82ff474103e26351dacfe8d50214e7c9322960d8d07ba7fa1d05ff981c8b2d"}, + {file = "grpcio-1.75.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0ee119f4f88d9f75414217823d21d75bfe0e6ed40135b0cbbfc6376bc9f7757d"}, + {file = "grpcio-1.75.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:664eecc3abe6d916fa6cf8dd6b778e62fb264a70f3430a3180995bf2da935446"}, + {file = "grpcio-1.75.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:c32193fa08b2fbebf08fe08e84f8a0aad32d87c3ad42999c65e9449871b1c66e"}, + {file = "grpcio-1.75.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5cebe13088b9254f6e615bcf1da9131d46cfa4e88039454aca9cb65f639bd3bc"}, + {file = "grpcio-1.75.1-cp313-cp313-win32.whl", hash = "sha256:4b4c678e7ed50f8ae8b8dbad15a865ee73ce12668b6aaf411bf3258b5bc3f970"}, + {file = "grpcio-1.75.1-cp313-cp313-win_amd64.whl", hash = "sha256:5573f51e3f296a1bcf71e7a690c092845fb223072120f4bdb7a5b48e111def66"}, + {file = "grpcio-1.75.1-cp314-cp314-linux_armv7l.whl", hash = "sha256:c05da79068dd96723793bffc8d0e64c45f316248417515f28d22204d9dae51c7"}, + {file = "grpcio-1.75.1-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:06373a94fd16ec287116a825161dca179a0402d0c60674ceeec8c9fba344fe66"}, + {file = "grpcio-1.75.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4484f4b7287bdaa7a5b3980f3c7224c3c622669405d20f69549f5fb956ad0421"}, + {file = "grpcio-1.75.1-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:2720c239c1180eee69f7883c1d4c83fc1a495a2535b5fa322887c70bf02b16e8"}, + {file = "grpcio-1.75.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:07a554fa31c668cf0e7a188678ceeca3cb8fead29bbe455352e712ec33ca701c"}, + {file = "grpcio-1.75.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:3e71a2105210366bfc398eef7f57a664df99194f3520edb88b9c3a7e46ee0d64"}, + {file = "grpcio-1.75.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:8679aa8a5b67976776d3c6b0521e99d1c34db8a312a12bcfd78a7085cb9b604e"}, + {file = "grpcio-1.75.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:aad1c774f4ebf0696a7f148a56d39a3432550612597331792528895258966dc0"}, + {file = "grpcio-1.75.1-cp314-cp314-win32.whl", hash = "sha256:62ce42d9994446b307649cb2a23335fa8e927f7ab2cbf5fcb844d6acb4d85f9c"}, + {file = "grpcio-1.75.1-cp314-cp314-win_amd64.whl", hash = "sha256:f86e92275710bea3000cb79feca1762dc0ad3b27830dd1a74e82ab321d4ee464"}, + {file = "grpcio-1.75.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:c09fba33327c3ac11b5c33dbdd8218eef8990d78f83b1656d628831812a8c0fb"}, + {file = "grpcio-1.75.1-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:7e21400b037be29545704889e72e586c238e346dcb2d08d8a7288d16c883a9ec"}, + {file = "grpcio-1.75.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c12121e509b9f8b0914d10054d24120237d19e870b1cd82acbb8a9b9ddd198a3"}, + {file = "grpcio-1.75.1-cp39-cp39-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:73577a93e692b3474b1bfe84285d098de36705dbd838bb4d6a056d326e4dc880"}, + {file = "grpcio-1.75.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e19e7dfa0d7ca7dea22be464339e18ac608fd75d88c56770c646cdabe54bc724"}, + {file = "grpcio-1.75.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:4e1c28f51c1cf67eccdfc1065e8e866c9ed622f09773ca60947089c117f848a1"}, + {file = "grpcio-1.75.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:030a6164bc2ca726052778c0cf8e3249617a34e368354f9e6107c27ad4af8c28"}, + {file = "grpcio-1.75.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:67697efef5a98d46d5db7b1720fa4043536f8b8e5072a5d61cfca762f287e939"}, + {file = "grpcio-1.75.1-cp39-cp39-win32.whl", hash = "sha256:52015cf73eb5d76f6404e0ce0505a69b51fd1f35810b3a01233b34b10baafb41"}, + {file = "grpcio-1.75.1-cp39-cp39-win_amd64.whl", hash = "sha256:9fe51e4a1f896ea84ac750900eae34d9e9b896b5b1e4a30b02dc31ad29f36383"}, + {file = "grpcio-1.75.1.tar.gz", hash = "sha256:3e81d89ece99b9ace23a6916880baca613c03a799925afb2857887efa8b1b3d2"}, +] + +[package.dependencies] +typing-extensions = ">=4.12,<5.0" + +[package.extras] +protobuf = ["grpcio-tools (>=1.75.1)"] + +[[package]] +name = "grpcio-status" +version = "1.75.1" +description = "Status proto mapping for gRPC" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "grpcio_status-1.75.1-py3-none-any.whl", hash = "sha256:f681b301be26dcf7abf5c765d4a22e4098765e1a65cbdfa3efca384edf8e4e3c"}, + {file = "grpcio_status-1.75.1.tar.gz", hash = "sha256:8162afa21833a2085c91089cc395ad880fac1378a1d60233d976649ed724cbf8"}, +] + +[package.dependencies] +googleapis-common-protos = ">=1.5.5" +grpcio = ">=1.75.1" +protobuf = ">=6.31.1,<7.0.0" + +[[package]] +name = "idna" +version = "3.10" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.6" +groups = ["main"] +files = [ + {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, + {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, +] + +[package.extras] +all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] + +[[package]] +name = "iniconfig" +version = "2.1.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.8" +groups = ["main", "dev"] +files = [ + {file = "iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760"}, + {file = "iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"}, +] + +[[package]] +name = "minio" +version = "7.2.18" +description = "MinIO Python SDK for Amazon S3 Compatible Cloud Storage" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "minio-7.2.18-py3-none-any.whl", hash = "sha256:f23a6edbff8d0bc4b5c1a61b2628a01c5a3342aefc613ff9c276012e6321108f"}, + {file = "minio-7.2.18.tar.gz", hash = "sha256:173402a5716099159c5659f9de75be204ebe248557b9f1cc9cf45aa70e9d3024"}, +] + +[package.dependencies] +argon2-cffi = "*" +certifi = "*" +pycryptodome = "*" +typing-extensions = "*" +urllib3 = "*" + +[[package]] +name = "multidict" +version = "6.7.0" +description = "multidict implementation" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "multidict-6.7.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:9f474ad5acda359c8758c8accc22032c6abe6dc87a8be2440d097785e27a9349"}, + {file = "multidict-6.7.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4b7a9db5a870f780220e931d0002bbfd88fb53aceb6293251e2c839415c1b20e"}, + {file = "multidict-6.7.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:03ca744319864e92721195fa28c7a3b2bc7b686246b35e4078c1e4d0eb5466d3"}, + {file = "multidict-6.7.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f0e77e3c0008bc9316e662624535b88d360c3a5d3f81e15cf12c139a75250046"}, + {file = "multidict-6.7.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:08325c9e5367aa379a3496aa9a022fe8837ff22e00b94db256d3a1378c76ab32"}, + {file = "multidict-6.7.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e2862408c99f84aa571ab462d25236ef9cb12a602ea959ba9c9009a54902fc73"}, + {file = "multidict-6.7.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4d72a9a2d885f5c208b0cb91ff2ed43636bb7e345ec839ff64708e04f69a13cc"}, + {file = "multidict-6.7.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:478cc36476687bac1514d651cbbaa94b86b0732fb6855c60c673794c7dd2da62"}, + {file = "multidict-6.7.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6843b28b0364dc605f21481c90fadb5f60d9123b442eb8a726bb74feef588a84"}, + {file = "multidict-6.7.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:23bfeee5316266e5ee2d625df2d2c602b829435fc3a235c2ba2131495706e4a0"}, + {file = "multidict-6.7.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:680878b9f3d45c31e1f730eef731f9b0bc1da456155688c6745ee84eb818e90e"}, + {file = "multidict-6.7.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:eb866162ef2f45063acc7a53a88ef6fe8bf121d45c30ea3c9cd87ce7e191a8d4"}, + {file = "multidict-6.7.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:df0e3bf7993bdbeca5ac25aa859cf40d39019e015c9c91809ba7093967f7a648"}, + {file = "multidict-6.7.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:661709cdcd919a2ece2234f9bae7174e5220c80b034585d7d8a755632d3e2111"}, + {file = "multidict-6.7.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:096f52730c3fb8ed419db2d44391932b63891b2c5ed14850a7e215c0ba9ade36"}, + {file = "multidict-6.7.0-cp310-cp310-win32.whl", hash = "sha256:afa8a2978ec65d2336305550535c9c4ff50ee527914328c8677b3973ade52b85"}, + {file = "multidict-6.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:b15b3afff74f707b9275d5ba6a91ae8f6429c3ffb29bbfd216b0b375a56f13d7"}, + {file = "multidict-6.7.0-cp310-cp310-win_arm64.whl", hash = "sha256:4b73189894398d59131a66ff157837b1fafea9974be486d036bb3d32331fdbf0"}, + {file = "multidict-6.7.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4d409aa42a94c0b3fa617708ef5276dfe81012ba6753a0370fcc9d0195d0a1fc"}, + {file = "multidict-6.7.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:14c9e076eede3b54c636f8ce1c9c252b5f057c62131211f0ceeec273810c9721"}, + {file = "multidict-6.7.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4c09703000a9d0fa3c3404b27041e574cc7f4df4c6563873246d0e11812a94b6"}, + {file = "multidict-6.7.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:a265acbb7bb33a3a2d626afbe756371dce0279e7b17f4f4eda406459c2b5ff1c"}, + {file = "multidict-6.7.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:51cb455de290ae462593e5b1cb1118c5c22ea7f0d3620d9940bf695cea5a4bd7"}, + {file = "multidict-6.7.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:db99677b4457c7a5c5a949353e125ba72d62b35f74e26da141530fbb012218a7"}, + {file = "multidict-6.7.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f470f68adc395e0183b92a2f4689264d1ea4b40504a24d9882c27375e6662bb9"}, + {file = "multidict-6.7.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0db4956f82723cc1c270de9c6e799b4c341d327762ec78ef82bb962f79cc07d8"}, + {file = "multidict-6.7.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3e56d780c238f9e1ae66a22d2adf8d16f485381878250db8d496623cd38b22bd"}, + {file = "multidict-6.7.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9d14baca2ee12c1a64740d4531356ba50b82543017f3ad6de0deb943c5979abb"}, + {file = "multidict-6.7.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:295a92a76188917c7f99cda95858c822f9e4aae5824246bba9b6b44004ddd0a6"}, + {file = "multidict-6.7.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:39f1719f57adbb767ef592a50ae5ebb794220d1188f9ca93de471336401c34d2"}, + {file = "multidict-6.7.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:0a13fb8e748dfc94749f622de065dd5c1def7e0d2216dba72b1d8069a389c6ff"}, + {file = "multidict-6.7.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e3aa16de190d29a0ea1b48253c57d99a68492c8dd8948638073ab9e74dc9410b"}, + {file = "multidict-6.7.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a048ce45dcdaaf1defb76b2e684f997fb5abf74437b6cb7b22ddad934a964e34"}, + {file = "multidict-6.7.0-cp311-cp311-win32.whl", hash = "sha256:a90af66facec4cebe4181b9e62a68be65e45ac9b52b67de9eec118701856e7ff"}, + {file = "multidict-6.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:95b5ffa4349df2887518bb839409bcf22caa72d82beec453216802f475b23c81"}, + {file = "multidict-6.7.0-cp311-cp311-win_arm64.whl", hash = "sha256:329aa225b085b6f004a4955271a7ba9f1087e39dcb7e65f6284a988264a63912"}, + {file = "multidict-6.7.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8a3862568a36d26e650a19bb5cbbba14b71789032aebc0423f8cc5f150730184"}, + {file = "multidict-6.7.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:960c60b5849b9b4f9dcc9bea6e3626143c252c74113df2c1540aebce70209b45"}, + {file = "multidict-6.7.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2049be98fb57a31b4ccf870bf377af2504d4ae35646a19037ec271e4c07998aa"}, + {file = "multidict-6.7.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0934f3843a1860dd465d38895c17fce1f1cb37295149ab05cd1b9a03afacb2a7"}, + {file = "multidict-6.7.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b3e34f3a1b8131ba06f1a73adab24f30934d148afcd5f5de9a73565a4404384e"}, + {file = "multidict-6.7.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:efbb54e98446892590dc2458c19c10344ee9a883a79b5cec4bc34d6656e8d546"}, + {file = "multidict-6.7.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a35c5fc61d4f51eb045061e7967cfe3123d622cd500e8868e7c0c592a09fedc4"}, + {file = "multidict-6.7.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29fe6740ebccba4175af1b9b87bf553e9c15cd5868ee967e010efcf94e4fd0f1"}, + {file = "multidict-6.7.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:123e2a72e20537add2f33a79e605f6191fba2afda4cbb876e35c1a7074298a7d"}, + {file = "multidict-6.7.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b284e319754366c1aee2267a2036248b24eeb17ecd5dc16022095e747f2f4304"}, + {file = "multidict-6.7.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:803d685de7be4303b5a657b76e2f6d1240e7e0a8aa2968ad5811fa2285553a12"}, + {file = "multidict-6.7.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c04a328260dfd5db8c39538f999f02779012268f54614902d0afc775d44e0a62"}, + {file = "multidict-6.7.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8a19cdb57cd3df4cd865849d93ee14920fb97224300c88501f16ecfa2604b4e0"}, + {file = "multidict-6.7.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9b2fd74c52accced7e75de26023b7dccee62511a600e62311b918ec5c168fc2a"}, + {file = "multidict-6.7.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3e8bfdd0e487acf992407a140d2589fe598238eaeffa3da8448d63a63cd363f8"}, + {file = "multidict-6.7.0-cp312-cp312-win32.whl", hash = "sha256:dd32a49400a2c3d52088e120ee00c1e3576cbff7e10b98467962c74fdb762ed4"}, + {file = "multidict-6.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:92abb658ef2d7ef22ac9f8bb88e8b6c3e571671534e029359b6d9e845923eb1b"}, + {file = "multidict-6.7.0-cp312-cp312-win_arm64.whl", hash = "sha256:490dab541a6a642ce1a9d61a4781656b346a55c13038f0b1244653828e3a83ec"}, + {file = "multidict-6.7.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:bee7c0588aa0076ce77c0ea5d19a68d76ad81fcd9fe8501003b9a24f9d4000f6"}, + {file = "multidict-6.7.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7ef6b61cad77091056ce0e7ce69814ef72afacb150b7ac6a3e9470def2198159"}, + {file = "multidict-6.7.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9c0359b1ec12b1d6849c59f9d319610b7f20ef990a6d454ab151aa0e3b9f78ca"}, + {file = "multidict-6.7.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cd240939f71c64bd658f186330603aac1a9a81bf6273f523fca63673cb7378a8"}, + {file = "multidict-6.7.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a60a4d75718a5efa473ebd5ab685786ba0c67b8381f781d1be14da49f1a2dc60"}, + {file = "multidict-6.7.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:53a42d364f323275126aff81fb67c5ca1b7a04fda0546245730a55c8c5f24bc4"}, + {file = "multidict-6.7.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3b29b980d0ddbecb736735ee5bef69bb2ddca56eff603c86f3f29a1128299b4f"}, + {file = "multidict-6.7.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f8a93b1c0ed2d04b97a5e9336fd2d33371b9a6e29ab7dd6503d63407c20ffbaf"}, + {file = "multidict-6.7.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9ff96e8815eecacc6645da76c413eb3b3d34cfca256c70b16b286a687d013c32"}, + {file = "multidict-6.7.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7516c579652f6a6be0e266aec0acd0db80829ca305c3d771ed898538804c2036"}, + {file = "multidict-6.7.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:040f393368e63fb0f3330e70c26bfd336656bed925e5cbe17c9da839a6ab13ec"}, + {file = "multidict-6.7.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b3bc26a951007b1057a1c543af845f1c7e3e71cc240ed1ace7bf4484aa99196e"}, + {file = "multidict-6.7.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:7b022717c748dd1992a83e219587aabe45980d88969f01b316e78683e6285f64"}, + {file = "multidict-6.7.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:9600082733859f00d79dee64effc7aef1beb26adb297416a4ad2116fd61374bd"}, + {file = "multidict-6.7.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:94218fcec4d72bc61df51c198d098ce2b378e0ccbac41ddbed5ef44092913288"}, + {file = "multidict-6.7.0-cp313-cp313-win32.whl", hash = "sha256:a37bd74c3fa9d00be2d7b8eca074dc56bd8077ddd2917a839bd989612671ed17"}, + {file = "multidict-6.7.0-cp313-cp313-win_amd64.whl", hash = "sha256:30d193c6cc6d559db42b6bcec8a5d395d34d60c9877a0b71ecd7c204fcf15390"}, + {file = "multidict-6.7.0-cp313-cp313-win_arm64.whl", hash = "sha256:ea3334cabe4d41b7ccd01e4d349828678794edbc2d3ae97fc162a3312095092e"}, + {file = "multidict-6.7.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:ad9ce259f50abd98a1ca0aa6e490b58c316a0fce0617f609723e40804add2c00"}, + {file = "multidict-6.7.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:07f5594ac6d084cbb5de2df218d78baf55ef150b91f0ff8a21cc7a2e3a5a58eb"}, + {file = "multidict-6.7.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:0591b48acf279821a579282444814a2d8d0af624ae0bc600aa4d1b920b6e924b"}, + {file = "multidict-6.7.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:749a72584761531d2b9467cfbdfd29487ee21124c304c4b6cb760d8777b27f9c"}, + {file = "multidict-6.7.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b4c3d199f953acd5b446bf7c0de1fe25d94e09e79086f8dc2f48a11a129cdf1"}, + {file = "multidict-6.7.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:9fb0211dfc3b51efea2f349ec92c114d7754dd62c01f81c3e32b765b70c45c9b"}, + {file = "multidict-6.7.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a027ec240fe73a8d6281872690b988eed307cd7d91b23998ff35ff577ca688b5"}, + {file = "multidict-6.7.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1d964afecdf3a8288789df2f5751dc0a8261138c3768d9af117ed384e538fad"}, + {file = "multidict-6.7.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:caf53b15b1b7df9fbd0709aa01409000a2b4dd03a5f6f5cc548183c7c8f8b63c"}, + {file = "multidict-6.7.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:654030da3197d927f05a536a66186070e98765aa5142794c9904555d3a9d8fb5"}, + {file = "multidict-6.7.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:2090d3718829d1e484706a2f525e50c892237b2bf9b17a79b059cb98cddc2f10"}, + {file = "multidict-6.7.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:2d2cfeec3f6f45651b3d408c4acec0ebf3daa9bc8a112a084206f5db5d05b754"}, + {file = "multidict-6.7.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:4ef089f985b8c194d341eb2c24ae6e7408c9a0e2e5658699c92f497437d88c3c"}, + {file = "multidict-6.7.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e93a0617cd16998784bf4414c7e40f17a35d2350e5c6f0bd900d3a8e02bd3762"}, + {file = "multidict-6.7.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f0feece2ef8ebc42ed9e2e8c78fc4aa3cf455733b507c09ef7406364c94376c6"}, + {file = "multidict-6.7.0-cp313-cp313t-win32.whl", hash = "sha256:19a1d55338ec1be74ef62440ca9e04a2f001a04d0cc49a4983dc320ff0f3212d"}, + {file = "multidict-6.7.0-cp313-cp313t-win_amd64.whl", hash = "sha256:3da4fb467498df97e986af166b12d01f05d2e04f978a9c1c680ea1988e0bc4b6"}, + {file = "multidict-6.7.0-cp313-cp313t-win_arm64.whl", hash = "sha256:b4121773c49a0776461f4a904cdf6264c88e42218aaa8407e803ca8025872792"}, + {file = "multidict-6.7.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3bab1e4aff7adaa34410f93b1f8e57c4b36b9af0426a76003f441ee1d3c7e842"}, + {file = "multidict-6.7.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:b8512bac933afc3e45fb2b18da8e59b78d4f408399a960339598374d4ae3b56b"}, + {file = "multidict-6.7.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:79dcf9e477bc65414ebfea98ffd013cb39552b5ecd62908752e0e413d6d06e38"}, + {file = "multidict-6.7.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:31bae522710064b5cbeddaf2e9f32b1abab70ac6ac91d42572502299e9953128"}, + {file = "multidict-6.7.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a0df7ff02397bb63e2fd22af2c87dfa39e8c7f12947bc524dbdc528282c7e34"}, + {file = "multidict-6.7.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7a0222514e8e4c514660e182d5156a415c13ef0aabbd71682fc714e327b95e99"}, + {file = "multidict-6.7.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2397ab4daaf2698eb51a76721e98db21ce4f52339e535725de03ea962b5a3202"}, + {file = "multidict-6.7.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8891681594162635948a636c9fe0ff21746aeb3dd5463f6e25d9bea3a8a39ca1"}, + {file = "multidict-6.7.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:18706cc31dbf402a7945916dd5cddf160251b6dab8a2c5f3d6d5a55949f676b3"}, + {file = "multidict-6.7.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f844a1bbf1d207dd311a56f383f7eda2d0e134921d45751842d8235e7778965d"}, + {file = "multidict-6.7.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:d4393e3581e84e5645506923816b9cc81f5609a778c7e7534054091acc64d1c6"}, + {file = "multidict-6.7.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:fbd18dc82d7bf274b37aa48d664534330af744e03bccf696d6f4c6042e7d19e7"}, + {file = "multidict-6.7.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:b6234e14f9314731ec45c42fc4554b88133ad53a09092cc48a88e771c125dadb"}, + {file = "multidict-6.7.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:08d4379f9744d8f78d98c8673c06e202ffa88296f009c71bbafe8a6bf847d01f"}, + {file = "multidict-6.7.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:9fe04da3f79387f450fd0061d4dd2e45a72749d31bf634aecc9e27f24fdc4b3f"}, + {file = "multidict-6.7.0-cp314-cp314-win32.whl", hash = "sha256:fbafe31d191dfa7c4c51f7a6149c9fb7e914dcf9ffead27dcfd9f1ae382b3885"}, + {file = "multidict-6.7.0-cp314-cp314-win_amd64.whl", hash = "sha256:2f67396ec0310764b9222a1728ced1ab638f61aadc6226f17a71dd9324f9a99c"}, + {file = "multidict-6.7.0-cp314-cp314-win_arm64.whl", hash = "sha256:ba672b26069957ee369cfa7fc180dde1fc6f176eaf1e6beaf61fbebbd3d9c000"}, + {file = "multidict-6.7.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:c1dcc7524066fa918c6a27d61444d4ee7900ec635779058571f70d042d86ed63"}, + {file = "multidict-6.7.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:27e0b36c2d388dc7b6ced3406671b401e84ad7eb0656b8f3a2f46ed0ce483718"}, + {file = "multidict-6.7.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2a7baa46a22e77f0988e3b23d4ede5513ebec1929e34ee9495be535662c0dfe2"}, + {file = "multidict-6.7.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7bf77f54997a9166a2f5675d1201520586439424c2511723a7312bdb4bcc034e"}, + {file = "multidict-6.7.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e011555abada53f1578d63389610ac8a5400fc70ce71156b0aa30d326f1a5064"}, + {file = "multidict-6.7.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:28b37063541b897fd6a318007373930a75ca6d6ac7c940dbe14731ffdd8d498e"}, + {file = "multidict-6.7.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:05047ada7a2fde2631a0ed706f1fd68b169a681dfe5e4cf0f8e4cb6618bbc2cd"}, + {file = "multidict-6.7.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:716133f7d1d946a4e1b91b1756b23c088881e70ff180c24e864c26192ad7534a"}, + {file = "multidict-6.7.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d1bed1b467ef657f2a0ae62844a607909ef1c6889562de5e1d505f74457d0b96"}, + {file = "multidict-6.7.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:ca43bdfa5d37bd6aee89d85e1d0831fb86e25541be7e9d376ead1b28974f8e5e"}, + {file = "multidict-6.7.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:44b546bd3eb645fd26fb949e43c02a25a2e632e2ca21a35e2e132c8105dc8599"}, + {file = "multidict-6.7.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:a6ef16328011d3f468e7ebc326f24c1445f001ca1dec335b2f8e66bed3006394"}, + {file = "multidict-6.7.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:5aa873cbc8e593d361ae65c68f85faadd755c3295ea2c12040ee146802f23b38"}, + {file = "multidict-6.7.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:3d7b6ccce016e29df4b7ca819659f516f0bc7a4b3efa3bb2012ba06431b044f9"}, + {file = "multidict-6.7.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:171b73bd4ee683d307599b66793ac80981b06f069b62eea1c9e29c9241aa66b0"}, + {file = "multidict-6.7.0-cp314-cp314t-win32.whl", hash = "sha256:b2d7f80c4e1fd010b07cb26820aae86b7e73b681ee4889684fb8d2d4537aab13"}, + {file = "multidict-6.7.0-cp314-cp314t-win_amd64.whl", hash = "sha256:09929cab6fcb68122776d575e03c6cc64ee0b8fca48d17e135474b042ce515cd"}, + {file = "multidict-6.7.0-cp314-cp314t-win_arm64.whl", hash = "sha256:cc41db090ed742f32bd2d2c721861725e6109681eddf835d0a82bd3a5c382827"}, + {file = "multidict-6.7.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:363eb68a0a59bd2303216d2346e6c441ba10d36d1f9969fcb6f1ba700de7bb5c"}, + {file = "multidict-6.7.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d874eb056410ca05fed180b6642e680373688efafc7f077b2a2f61811e873a40"}, + {file = "multidict-6.7.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8b55d5497b51afdfde55925e04a022f1de14d4f4f25cdfd4f5d9b0aa96166851"}, + {file = "multidict-6.7.0-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f8e5c0031b90ca9ce555e2e8fd5c3b02a25f14989cbc310701823832c99eb687"}, + {file = "multidict-6.7.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9cf41880c991716f3c7cec48e2f19ae4045fc9db5fc9cff27347ada24d710bb5"}, + {file = "multidict-6.7.0-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8cfc12a8630a29d601f48d47787bd7eb730e475e83edb5d6c5084317463373eb"}, + {file = "multidict-6.7.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3996b50c3237c4aec17459217c1e7bbdead9a22a0fcd3c365564fbd16439dde6"}, + {file = "multidict-6.7.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7f5170993a0dd3ab871c74f45c0a21a4e2c37a2f2b01b5f722a2ad9c6650469e"}, + {file = "multidict-6.7.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ec81878ddf0e98817def1e77d4f50dae5ef5b0e4fe796fae3bd674304172416e"}, + {file = "multidict-6.7.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:9281bf5b34f59afbc6b1e477a372e9526b66ca446f4bf62592839c195a718b32"}, + {file = "multidict-6.7.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:68af405971779d8b37198726f2b6fe3955db846fee42db7a4286fc542203934c"}, + {file = "multidict-6.7.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3ba3ef510467abb0667421a286dc906e30eb08569365f5cdb131d7aff7c2dd84"}, + {file = "multidict-6.7.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:b61189b29081a20c7e4e0b49b44d5d44bb0dc92be3c6d06a11cc043f81bf9329"}, + {file = "multidict-6.7.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:fb287618b9c7aa3bf8d825f02d9201b2f13078a5ed3b293c8f4d953917d84d5e"}, + {file = "multidict-6.7.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:521f33e377ff64b96c4c556b81c55d0cfffb96a11c194fd0c3f1e56f3d8dd5a4"}, + {file = "multidict-6.7.0-cp39-cp39-win32.whl", hash = "sha256:ce8fdc2dca699f8dbf055a61d73eaa10482569ad20ee3c36ef9641f69afa8c91"}, + {file = "multidict-6.7.0-cp39-cp39-win_amd64.whl", hash = "sha256:7e73299c99939f089dd9b2120a04a516b95cdf8c1cd2b18c53ebf0de80b1f18f"}, + {file = "multidict-6.7.0-cp39-cp39-win_arm64.whl", hash = "sha256:6bdce131e14b04fd34a809b6380dbfd826065c3e2fe8a50dbae659fa0c390546"}, + {file = "multidict-6.7.0-py3-none-any.whl", hash = "sha256:394fc5c42a333c9ffc3e421a4c85e08580d990e08b99f6bf35b4132114c5dcb3"}, + {file = "multidict-6.7.0.tar.gz", hash = "sha256:c6e99d9a65ca282e578dfea819cfa9c0a62b2499d8677392e09feaf305e9e6f5"}, +] + +[[package]] +name = "packaging" +version = "25.0" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.8" +groups = ["main", "dev"] +files = [ + {file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"}, + {file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"}, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.9" +groups = ["main", "dev"] +files = [ + {file = "pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746"}, + {file = "pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["coverage", "pytest", "pytest-benchmark"] + +[[package]] +name = "propcache" +version = "0.4.1" +description = "Accelerated property cache" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "propcache-0.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c2d1fa3201efaf55d730400d945b5b3ab6e672e100ba0f9a409d950ab25d7db"}, + {file = "propcache-0.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1eb2994229cc8ce7fe9b3db88f5465f5fd8651672840b2e426b88cdb1a30aac8"}, + {file = "propcache-0.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:66c1f011f45a3b33d7bcb22daed4b29c0c9e2224758b6be00686731e1b46f925"}, + {file = "propcache-0.4.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9a52009f2adffe195d0b605c25ec929d26b36ef986ba85244891dee3b294df21"}, + {file = "propcache-0.4.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5d4e2366a9c7b837555cf02fb9be2e3167d333aff716332ef1b7c3a142ec40c5"}, + {file = "propcache-0.4.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:9d2b6caef873b4f09e26ea7e33d65f42b944837563a47a94719cc3544319a0db"}, + {file = "propcache-0.4.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2b16ec437a8c8a965ecf95739448dd938b5c7f56e67ea009f4300d8df05f32b7"}, + {file = "propcache-0.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:296f4c8ed03ca7476813fe666c9ea97869a8d7aec972618671b33a38a5182ef4"}, + {file = "propcache-0.4.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:1f0978529a418ebd1f49dad413a2b68af33f85d5c5ca5c6ca2a3bed375a7ac60"}, + {file = "propcache-0.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fd138803047fb4c062b1c1dd95462f5209456bfab55c734458f15d11da288f8f"}, + {file = "propcache-0.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8c9b3cbe4584636d72ff556d9036e0c9317fa27b3ac1f0f558e7e84d1c9c5900"}, + {file = "propcache-0.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f93243fdc5657247533273ac4f86ae106cc6445a0efacb9a1bfe982fcfefd90c"}, + {file = "propcache-0.4.1-cp310-cp310-win32.whl", hash = "sha256:a0ee98db9c5f80785b266eb805016e36058ac72c51a064040f2bc43b61101cdb"}, + {file = "propcache-0.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:1cdb7988c4e5ac7f6d175a28a9aa0c94cb6f2ebe52756a3c0cda98d2809a9e37"}, + {file = "propcache-0.4.1-cp310-cp310-win_arm64.whl", hash = "sha256:d82ad62b19645419fe79dd63b3f9253e15b30e955c0170e5cebc350c1844e581"}, + {file = "propcache-0.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:60a8fda9644b7dfd5dece8c61d8a85e271cb958075bfc4e01083c148b61a7caf"}, + {file = "propcache-0.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c30b53e7e6bda1d547cabb47c825f3843a0a1a42b0496087bb58d8fedf9f41b5"}, + {file = "propcache-0.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6918ecbd897443087a3b7cd978d56546a812517dcaaca51b49526720571fa93e"}, + {file = "propcache-0.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3d902a36df4e5989763425a8ab9e98cd8ad5c52c823b34ee7ef307fd50582566"}, + {file = "propcache-0.4.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a9695397f85973bb40427dedddf70d8dc4a44b22f1650dd4af9eedf443d45165"}, + {file = "propcache-0.4.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2bb07ffd7eaad486576430c89f9b215f9e4be68c4866a96e97db9e97fead85dc"}, + {file = "propcache-0.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fd6f30fdcf9ae2a70abd34da54f18da086160e4d7d9251f81f3da0ff84fc5a48"}, + {file = "propcache-0.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fc38cba02d1acba4e2869eef1a57a43dfbd3d49a59bf90dda7444ec2be6a5570"}, + {file = "propcache-0.4.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:67fad6162281e80e882fb3ec355398cf72864a54069d060321f6cd0ade95fe85"}, + {file = "propcache-0.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f10207adf04d08bec185bae14d9606a1444715bc99180f9331c9c02093e1959e"}, + {file = "propcache-0.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e9b0d8d0845bbc4cfcdcbcdbf5086886bc8157aa963c31c777ceff7846c77757"}, + {file = "propcache-0.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:981333cb2f4c1896a12f4ab92a9cc8f09ea664e9b7dbdc4eff74627af3a11c0f"}, + {file = "propcache-0.4.1-cp311-cp311-win32.whl", hash = "sha256:f1d2f90aeec838a52f1c1a32fe9a619fefd5e411721a9117fbf82aea638fe8a1"}, + {file = "propcache-0.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:364426a62660f3f699949ac8c621aad6977be7126c5807ce48c0aeb8e7333ea6"}, + {file = "propcache-0.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:e53f3a38d3510c11953f3e6a33f205c6d1b001129f972805ca9b42fc308bc239"}, + {file = "propcache-0.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e153e9cd40cc8945138822807139367f256f89c6810c2634a4f6902b52d3b4e2"}, + {file = "propcache-0.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cd547953428f7abb73c5ad82cbb32109566204260d98e41e5dfdc682eb7f8403"}, + {file = "propcache-0.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f048da1b4f243fc44f205dfd320933a951b8d89e0afd4c7cacc762a8b9165207"}, + {file = "propcache-0.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ec17c65562a827bba85e3872ead335f95405ea1674860d96483a02f5c698fa72"}, + {file = "propcache-0.4.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:405aac25c6394ef275dee4c709be43745d36674b223ba4eb7144bf4d691b7367"}, + {file = "propcache-0.4.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0013cb6f8dde4b2a2f66903b8ba740bdfe378c943c4377a200551ceb27f379e4"}, + {file = "propcache-0.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:15932ab57837c3368b024473a525e25d316d8353016e7cc0e5ba9eb343fbb1cf"}, + {file = "propcache-0.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:031dce78b9dc099f4c29785d9cf5577a3faf9ebf74ecbd3c856a7b92768c3df3"}, + {file = "propcache-0.4.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ab08df6c9a035bee56e31af99be621526bd237bea9f32def431c656b29e41778"}, + {file = "propcache-0.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4d7af63f9f93fe593afbf104c21b3b15868efb2c21d07d8732c0c4287e66b6a6"}, + {file = "propcache-0.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:cfc27c945f422e8b5071b6e93169679e4eb5bf73bbcbf1ba3ae3a83d2f78ebd9"}, + {file = "propcache-0.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:35c3277624a080cc6ec6f847cbbbb5b49affa3598c4535a0a4682a697aaa5c75"}, + {file = "propcache-0.4.1-cp312-cp312-win32.whl", hash = "sha256:671538c2262dadb5ba6395e26c1731e1d52534bfe9ae56d0b5573ce539266aa8"}, + {file = "propcache-0.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:cb2d222e72399fcf5890d1d5cc1060857b9b236adff2792ff48ca2dfd46c81db"}, + {file = "propcache-0.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:204483131fb222bdaaeeea9f9e6c6ed0cac32731f75dfc1d4a567fc1926477c1"}, + {file = "propcache-0.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:43eedf29202c08550aac1d14e0ee619b0430aaef78f85864c1a892294fbc28cf"}, + {file = "propcache-0.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d62cdfcfd89ccb8de04e0eda998535c406bf5e060ffd56be6c586cbcc05b3311"}, + {file = "propcache-0.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cae65ad55793da34db5f54e4029b89d3b9b9490d8abe1b4c7ab5d4b8ec7ebf74"}, + {file = "propcache-0.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:333ddb9031d2704a301ee3e506dc46b1fe5f294ec198ed6435ad5b6a085facfe"}, + {file = "propcache-0.4.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:fd0858c20f078a32cf55f7e81473d96dcf3b93fd2ccdb3d40fdf54b8573df3af"}, + {file = "propcache-0.4.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:678ae89ebc632c5c204c794f8dab2837c5f159aeb59e6ed0539500400577298c"}, + {file = "propcache-0.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d472aeb4fbf9865e0c6d622d7f4d54a4e101a89715d8904282bb5f9a2f476c3f"}, + {file = "propcache-0.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4d3df5fa7e36b3225954fba85589da77a0fe6a53e3976de39caf04a0db4c36f1"}, + {file = "propcache-0.4.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:ee17f18d2498f2673e432faaa71698032b0127ebf23ae5974eeaf806c279df24"}, + {file = "propcache-0.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:580e97762b950f993ae618e167e7be9256b8353c2dcd8b99ec100eb50f5286aa"}, + {file = "propcache-0.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:501d20b891688eb8e7aa903021f0b72d5a55db40ffaab27edefd1027caaafa61"}, + {file = "propcache-0.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a0bd56e5b100aef69bd8562b74b46254e7c8812918d3baa700c8a8009b0af66"}, + {file = "propcache-0.4.1-cp313-cp313-win32.whl", hash = "sha256:bcc9aaa5d80322bc2fb24bb7accb4a30f81e90ab8d6ba187aec0744bc302ad81"}, + {file = "propcache-0.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:381914df18634f5494334d201e98245c0596067504b9372d8cf93f4bb23e025e"}, + {file = "propcache-0.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:8873eb4460fd55333ea49b7d189749ecf6e55bf85080f11b1c4530ed3034cba1"}, + {file = "propcache-0.4.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:92d1935ee1f8d7442da9c0c4fa7ac20d07e94064184811b685f5c4fada64553b"}, + {file = "propcache-0.4.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:473c61b39e1460d386479b9b2f337da492042447c9b685f28be4f74d3529e566"}, + {file = "propcache-0.4.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:c0ef0aaafc66fbd87842a3fe3902fd889825646bc21149eafe47be6072725835"}, + {file = "propcache-0.4.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f95393b4d66bfae908c3ca8d169d5f79cd65636ae15b5e7a4f6e67af675adb0e"}, + {file = "propcache-0.4.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c07fda85708bc48578467e85099645167a955ba093be0a2dcba962195676e859"}, + {file = "propcache-0.4.1-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:af223b406d6d000830c6f65f1e6431783fc3f713ba3e6cc8c024d5ee96170a4b"}, + {file = "propcache-0.4.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a78372c932c90ee474559c5ddfffd718238e8673c340dc21fe45c5b8b54559a0"}, + {file = "propcache-0.4.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:564d9f0d4d9509e1a870c920a89b2fec951b44bf5ba7d537a9e7c1ccec2c18af"}, + {file = "propcache-0.4.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:17612831fda0138059cc5546f4d12a2aacfb9e47068c06af35c400ba58ba7393"}, + {file = "propcache-0.4.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:41a89040cb10bd345b3c1a873b2bf36413d48da1def52f268a055f7398514874"}, + {file = "propcache-0.4.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e35b88984e7fa64aacecea39236cee32dd9bd8c55f57ba8a75cf2399553f9bd7"}, + {file = "propcache-0.4.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6f8b465489f927b0df505cbe26ffbeed4d6d8a2bbc61ce90eb074ff129ef0ab1"}, + {file = "propcache-0.4.1-cp313-cp313t-win32.whl", hash = "sha256:2ad890caa1d928c7c2965b48f3a3815c853180831d0e5503d35cf00c472f4717"}, + {file = "propcache-0.4.1-cp313-cp313t-win_amd64.whl", hash = "sha256:f7ee0e597f495cf415bcbd3da3caa3bd7e816b74d0d52b8145954c5e6fd3ff37"}, + {file = "propcache-0.4.1-cp313-cp313t-win_arm64.whl", hash = "sha256:929d7cbe1f01bb7baffb33dc14eb5691c95831450a26354cd210a8155170c93a"}, + {file = "propcache-0.4.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3f7124c9d820ba5548d431afb4632301acf965db49e666aa21c305cbe8c6de12"}, + {file = "propcache-0.4.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:c0d4b719b7da33599dfe3b22d3db1ef789210a0597bc650b7cee9c77c2be8c5c"}, + {file = "propcache-0.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9f302f4783709a78240ebc311b793f123328716a60911d667e0c036bc5dcbded"}, + {file = "propcache-0.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c80ee5802e3fb9ea37938e7eecc307fb984837091d5fd262bb37238b1ae97641"}, + {file = "propcache-0.4.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ed5a841e8bb29a55fb8159ed526b26adc5bdd7e8bd7bf793ce647cb08656cdf4"}, + {file = "propcache-0.4.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:55c72fd6ea2da4c318e74ffdf93c4fe4e926051133657459131a95c846d16d44"}, + {file = "propcache-0.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8326e144341460402713f91df60ade3c999d601e7eb5ff8f6f7862d54de0610d"}, + {file = "propcache-0.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:060b16ae65bc098da7f6d25bf359f1f31f688384858204fe5d652979e0015e5b"}, + {file = "propcache-0.4.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:89eb3fa9524f7bec9de6e83cf3faed9d79bffa560672c118a96a171a6f55831e"}, + {file = "propcache-0.4.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:dee69d7015dc235f526fe80a9c90d65eb0039103fe565776250881731f06349f"}, + {file = "propcache-0.4.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:5558992a00dfd54ccbc64a32726a3357ec93825a418a401f5cc67df0ac5d9e49"}, + {file = "propcache-0.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c9b822a577f560fbd9554812526831712c1436d2c046cedee4c3796d3543b144"}, + {file = "propcache-0.4.1-cp314-cp314-win32.whl", hash = "sha256:ab4c29b49d560fe48b696cdcb127dd36e0bc2472548f3bf56cc5cb3da2b2984f"}, + {file = "propcache-0.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:5a103c3eb905fcea0ab98be99c3a9a5ab2de60228aa5aceedc614c0281cf6153"}, + {file = "propcache-0.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:74c1fb26515153e482e00177a1ad654721bf9207da8a494a0c05e797ad27b992"}, + {file = "propcache-0.4.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:824e908bce90fb2743bd6b59db36eb4f45cd350a39637c9f73b1c1ea66f5b75f"}, + {file = "propcache-0.4.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:c2b5e7db5328427c57c8e8831abda175421b709672f6cfc3d630c3b7e2146393"}, + {file = "propcache-0.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6f6ff873ed40292cd4969ef5310179afd5db59fdf055897e282485043fc80ad0"}, + {file = "propcache-0.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:49a2dc67c154db2c1463013594c458881a069fcf98940e61a0569016a583020a"}, + {file = "propcache-0.4.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:005f08e6a0529984491e37d8dbc3dd86f84bd78a8ceb5fa9a021f4c48d4984be"}, + {file = "propcache-0.4.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5c3310452e0d31390da9035c348633b43d7e7feb2e37be252be6da45abd1abcc"}, + {file = "propcache-0.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c3c70630930447f9ef1caac7728c8ad1c56bc5015338b20fed0d08ea2480b3a"}, + {file = "propcache-0.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8e57061305815dfc910a3634dcf584f08168a8836e6999983569f51a8544cd89"}, + {file = "propcache-0.4.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:521a463429ef54143092c11a77e04056dd00636f72e8c45b70aaa3140d639726"}, + {file = "propcache-0.4.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:120c964da3fdc75e3731aa392527136d4ad35868cc556fd09bb6d09172d9a367"}, + {file = "propcache-0.4.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:d8f353eb14ee3441ee844ade4277d560cdd68288838673273b978e3d6d2c8f36"}, + {file = "propcache-0.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ab2943be7c652f09638800905ee1bab2c544e537edb57d527997a24c13dc1455"}, + {file = "propcache-0.4.1-cp314-cp314t-win32.whl", hash = "sha256:05674a162469f31358c30bcaa8883cb7829fa3110bf9c0991fe27d7896c42d85"}, + {file = "propcache-0.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:990f6b3e2a27d683cb7602ed6c86f15ee6b43b1194736f9baaeb93d0016633b1"}, + {file = "propcache-0.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:ecef2343af4cc68e05131e45024ba34f6095821988a9d0a02aa7c73fcc448aa9"}, + {file = "propcache-0.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3d233076ccf9e450c8b3bc6720af226b898ef5d051a2d145f7d765e6e9f9bcff"}, + {file = "propcache-0.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:357f5bb5c377a82e105e44bd3d52ba22b616f7b9773714bff93573988ef0a5fb"}, + {file = "propcache-0.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cbc3b6dfc728105b2a57c06791eb07a94229202ea75c59db644d7d496b698cac"}, + {file = "propcache-0.4.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:182b51b421f0501952d938dc0b0eb45246a5b5153c50d42b495ad5fb7517c888"}, + {file = "propcache-0.4.1-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4b536b39c5199b96fc6245eb5fb796c497381d3942f169e44e8e392b29c9ebcc"}, + {file = "propcache-0.4.1-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:db65d2af507bbfbdcedb254a11149f894169d90488dd3e7190f7cdcb2d6cd57a"}, + {file = "propcache-0.4.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fd2dbc472da1f772a4dae4fa24be938a6c544671a912e30529984dd80400cd88"}, + {file = "propcache-0.4.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:daede9cd44e0f8bdd9e6cc9a607fc81feb80fae7a5fc6cecaff0e0bb32e42d00"}, + {file = "propcache-0.4.1-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:71b749281b816793678ae7f3d0d84bd36e694953822eaad408d682efc5ca18e0"}, + {file = "propcache-0.4.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:0002004213ee1f36cfb3f9a42b5066100c44276b9b72b4e1504cddd3d692e86e"}, + {file = "propcache-0.4.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:fe49d0a85038f36ba9e3ffafa1103e61170b28e95b16622e11be0a0ea07c6781"}, + {file = "propcache-0.4.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:99d43339c83aaf4d32bda60928231848eee470c6bda8d02599cc4cebe872d183"}, + {file = "propcache-0.4.1-cp39-cp39-win32.whl", hash = "sha256:a129e76735bc792794d5177069691c3217898b9f5cee2b2661471e52ffe13f19"}, + {file = "propcache-0.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:948dab269721ae9a87fd16c514a0a2c2a1bdb23a9a61b969b0f9d9ee2968546f"}, + {file = "propcache-0.4.1-cp39-cp39-win_arm64.whl", hash = "sha256:5fd37c406dd6dc85aa743e214cef35dc54bbdd1419baac4f6ae5e5b1a2976938"}, + {file = "propcache-0.4.1-py3-none-any.whl", hash = "sha256:af2a6052aeb6cf17d3e46ee169099044fd8224cbaf75c76a2ef596e8163e2237"}, + {file = "propcache-0.4.1.tar.gz", hash = "sha256:f48107a8c637e80362555f37ecf49abe20370e557cc4ab374f04ec4423c97c3d"}, +] + +[[package]] +name = "protobuf" +version = "6.32.1" +description = "" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "protobuf-6.32.1-cp310-abi3-win32.whl", hash = "sha256:a8a32a84bc9f2aad712041b8b366190f71dde248926da517bde9e832e4412085"}, + {file = "protobuf-6.32.1-cp310-abi3-win_amd64.whl", hash = "sha256:b00a7d8c25fa471f16bc8153d0e53d6c9e827f0953f3c09aaa4331c718cae5e1"}, + {file = "protobuf-6.32.1-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:d8c7e6eb619ffdf105ee4ab76af5a68b60a9d0f66da3ea12d1640e6d8dab7281"}, + {file = "protobuf-6.32.1-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:2f5b80a49e1eb7b86d85fcd23fe92df154b9730a725c3b38c4e43b9d77018bf4"}, + {file = "protobuf-6.32.1-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:b1864818300c297265c83a4982fd3169f97122c299f56a56e2445c3698d34710"}, + {file = "protobuf-6.32.1-cp39-cp39-win32.whl", hash = "sha256:68ff170bac18c8178f130d1ccb94700cf72852298e016a2443bdb9502279e5f1"}, + {file = "protobuf-6.32.1-cp39-cp39-win_amd64.whl", hash = "sha256:d0975d0b2f3e6957111aa3935d08a0eb7e006b1505d825f862a1fffc8348e122"}, + {file = "protobuf-6.32.1-py3-none-any.whl", hash = "sha256:2601b779fc7d32a866c6b4404f9d42a3f67c5b9f3f15b4db3cccabe06b95c346"}, + {file = "protobuf-6.32.1.tar.gz", hash = "sha256:ee2469e4a021474ab9baafea6cd070e5bf27c7d29433504ddea1a4ee5850f68d"}, +] + +[[package]] +name = "psycopg" +version = "3.2.10" +description = "PostgreSQL database adapter for Python" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "psycopg-3.2.10-py3-none-any.whl", hash = "sha256:ab5caf09a9ec42e314a21f5216dbcceac528e0e05142e42eea83a3b28b320ac3"}, + {file = "psycopg-3.2.10.tar.gz", hash = "sha256:0bce99269d16ed18401683a8569b2c5abd94f72f8364856d56c0389bcd50972a"}, +] + +[package.dependencies] +psycopg-binary = {version = "3.2.10", optional = true, markers = "implementation_name != \"pypy\" and extra == \"binary\""} +typing-extensions = {version = ">=4.6", markers = "python_version < \"3.13\""} +tzdata = {version = "*", markers = "sys_platform == \"win32\""} + +[package.extras] +binary = ["psycopg-binary (==3.2.10)"] +c = ["psycopg-c (==3.2.10)"] +dev = ["ast-comments (>=1.1.2)", "black (>=24.1.0)", "codespell (>=2.2)", "dnspython (>=2.1)", "flake8 (>=4.0)", "isort-psycopg", "isort[colors] (>=6.0)", "mypy (>=1.14)", "pre-commit (>=4.0.1)", "types-setuptools (>=57.4)", "types-shapely (>=2.0)", "wheel (>=0.37)"] +docs = ["Sphinx (>=5.0)", "furo (==2022.6.21)", "sphinx-autobuild (>=2021.3.14)", "sphinx-autodoc-typehints (>=1.12)"] +pool = ["psycopg-pool"] +test = ["anyio (>=4.0)", "mypy (>=1.14)", "pproxy (>=2.7)", "pytest (>=6.2.5)", "pytest-cov (>=3.0)", "pytest-randomly (>=3.5)"] + +[[package]] +name = "psycopg-binary" +version = "3.2.10" +description = "PostgreSQL database adapter for Python -- C optimisation distribution" +optional = false +python-versions = ">=3.8" +groups = ["main"] +markers = "implementation_name != \"pypy\"" +files = [ + {file = "psycopg_binary-3.2.10-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:037dc92fc7d3f2adae7680e17216934c15b919d6528b908ac2eb52aecc0addcf"}, + {file = "psycopg_binary-3.2.10-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:84f7e8c5e5031db342ae697c2e8fb48cd708ba56990573b33e53ce626445371d"}, + {file = "psycopg_binary-3.2.10-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:a5a81104d88780018005fe17c37fa55b4afbb6dd3c205963cc56c025d5f1cc32"}, + {file = "psycopg_binary-3.2.10-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:0c23e88e048bbc33f32f5a35981707c9418723d469552dd5ac4e956366e58492"}, + {file = "psycopg_binary-3.2.10-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9c9f2728488ac5848acdbf14bb4fde50f8ba783cbf3c19e9abd506741389fa7f"}, + {file = "psycopg_binary-3.2.10-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ab1c6d761c4ee581016823dcc02f29b16ad69177fcbba88a9074c924fc31813e"}, + {file = "psycopg_binary-3.2.10-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:a024b3ee539a475cbc59df877c8ecdd6f8552a1b522b69196935bc26dc6152fb"}, + {file = "psycopg_binary-3.2.10-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:50130c0d1a2a01ec3d41631df86b6c1646c76718be000600a399dc1aad80b813"}, + {file = "psycopg_binary-3.2.10-cp310-cp310-win_amd64.whl", hash = "sha256:7fa1626225a162924d2da0ff4ef77869f7a8501d320355d2732be5bf2dda6138"}, + {file = "psycopg_binary-3.2.10-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:db0eb06a19e4c64a08db0db80875ede44939af6a2afc281762c338fad5d6e547"}, + {file = "psycopg_binary-3.2.10-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d922fdd49ed17c558b6b2f9ae2054c3d0cced2a34e079ce5a41c86904d0203f7"}, + {file = "psycopg_binary-3.2.10-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d557a94cd6d2e775b3af6cc0bd0ff0d9d641820b5cc3060ccf1f5ca2bf971217"}, + {file = "psycopg_binary-3.2.10-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:29b6bb87959515bc8b6abef10d8d23a9a681f03e48e9f0c8adb4b9fb7fa73f11"}, + {file = "psycopg_binary-3.2.10-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1b29285474e3339d0840e1b5079fdb0481914108f92ec62de0c87ae333c60b24"}, + {file = "psycopg_binary-3.2.10-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:62590dd113d10cd9c08251cb80b32e2e8aaf01ece04a700322e776b1d216959f"}, + {file = "psycopg_binary-3.2.10-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:764a5b9b40ad371c55dfdf95374d89e44a82fd62272d4fceebea0adb8930e2fb"}, + {file = "psycopg_binary-3.2.10-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bd3676a04970cf825d2c771b0c147f91182c5a3653e0dbe958e12383668d0f79"}, + {file = "psycopg_binary-3.2.10-cp311-cp311-win_amd64.whl", hash = "sha256:646048f46192c8d23786cc6ef19f35b7488d4110396391e407eca695fdfe9dcd"}, + {file = "psycopg_binary-3.2.10-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:1dee2f4d2adc9adacbfecf8254bd82f6ac95cff707e1b9b99aa721cd1ef16b47"}, + {file = "psycopg_binary-3.2.10-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8b45e65383da9c4a42a56f817973e521e893f4faae897fe9f1a971f9fe799742"}, + {file = "psycopg_binary-3.2.10-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:484d2b1659afe0f8f1cef5ea960bb640e96fa864faf917086f9f833f5c7a8034"}, + {file = "psycopg_binary-3.2.10-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:3bb4046973264ebc8cb7e20a83882d68577c1f26a6f8ad4fe52e4468cd9a8eee"}, + {file = "psycopg_binary-3.2.10-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:14bcbcac0cab465d88b2581e43ec01af4b01c9833e663f1352e05cb41be19e44"}, + {file = "psycopg_binary-3.2.10-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:70bb7f665587dfd79e69f48b34efe226149454d7aab138ed22d5431d703de2f6"}, + {file = "psycopg_binary-3.2.10-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:d2fe9eaa367f6171ab1a21a7dcb335eb2398be7f8bb7e04a20e2260aedc6f782"}, + {file = "psycopg_binary-3.2.10-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:299834cce3eec0c48aae5a5207fc8f0c558fd65f2ceab1a36693329847da956b"}, + {file = "psycopg_binary-3.2.10-cp312-cp312-win_amd64.whl", hash = "sha256:e037aac8dc894d147ef33056fc826ee5072977107a3fdf06122224353a057598"}, + {file = "psycopg_binary-3.2.10-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:55b14f2402be027fe1568bc6c4d75ac34628ff5442a70f74137dadf99f738e3b"}, + {file = "psycopg_binary-3.2.10-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:43d803fb4e108a67c78ba58f3e6855437ca25d56504cae7ebbfbd8fce9b59247"}, + {file = "psycopg_binary-3.2.10-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:470594d303928ab72a1ffd179c9c7bde9d00f76711d6b0c28f8a46ddf56d9807"}, + {file = "psycopg_binary-3.2.10-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:a1d4e4d309049e3cb61269652a3ca56cb598da30ecd7eb8cea561e0d18bc1a43"}, + {file = "psycopg_binary-3.2.10-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a92ff1c2cd79b3966d6a87e26ceb222ecd5581b5ae4b58961f126af806a861ed"}, + {file = "psycopg_binary-3.2.10-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac0365398947879c9827b319217096be727da16c94422e0eb3cf98c930643162"}, + {file = "psycopg_binary-3.2.10-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:42ee399c2613b470a87084ed79b06d9d277f19b0457c10e03a4aef7059097abc"}, + {file = "psycopg_binary-3.2.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2028073fc12cd70ba003309d1439c0c4afab4a7eee7653b8c91213064fffe12b"}, + {file = "psycopg_binary-3.2.10-cp313-cp313-win_amd64.whl", hash = "sha256:8390db6d2010ffcaf7f2b42339a2da620a7125d37029c1f9b72dfb04a8e7be6f"}, + {file = "psycopg_binary-3.2.10-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:b34c278a58aa79562afe7f45e0455b1f4cad5974fc3d5674cc5f1f9f57e97fc5"}, + {file = "psycopg_binary-3.2.10-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:810f65b9ef1fe9dddb5c05937884ea9563aaf4e1a2c3d138205231ed5f439511"}, + {file = "psycopg_binary-3.2.10-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8923487c3898c65e1450847e15d734bb2e6adbd2e79d2d1dd5ad829a1306bdc0"}, + {file = "psycopg_binary-3.2.10-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:7950ff79df7a453ac8a7d7a74694055b6c15905b0a2b6e3c99eb59c51a3f9bf7"}, + {file = "psycopg_binary-3.2.10-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0c2b95e83fda70ed2b0b4fadd8538572e4a4d987b721823981862d1ab56cc760"}, + {file = "psycopg_binary-3.2.10-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:20384985fbc650c09a547a13c6d7f91bb42020d38ceafd2b68b7fc4a48a1f160"}, + {file = "psycopg_binary-3.2.10-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:1f6982609b8ff8fcd67299b67cd5787da1876f3bb28fedd547262cfa8ddedf94"}, + {file = "psycopg_binary-3.2.10-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:bf30dcf6aaaa8d4779a20d2158bdf81cc8e84ce8eee595d748a7671c70c7b890"}, + {file = "psycopg_binary-3.2.10-cp314-cp314-win_amd64.whl", hash = "sha256:d5c6a66a76022af41970bf19f51bc6bf87bd10165783dd1d40484bfd87d6b382"}, + {file = "psycopg_binary-3.2.10-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:901729188b3fd5625970650ca1167786847dee0b92930c2858724d1a5e25dee1"}, + {file = "psycopg_binary-3.2.10-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d7d05174276bb403b8a57e01b857d96b0ac2a6879c5ce06a5cac2d1115763081"}, + {file = "psycopg_binary-3.2.10-cp38-cp38-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:37b42b2f5f58df1f07a5df1b0c2bcc9bd3b9c105e2e988923bfa47aa4ae967da"}, + {file = "psycopg_binary-3.2.10-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6fe450a98a0788b721b1b8302f0ba9be6eca82faf74bf7a86d794cd6484c7e27"}, + {file = "psycopg_binary-3.2.10-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:a28f24a7b68456bd31209b027a5b04304d37eb1d622ef847bf8c47933218a738"}, + {file = "psycopg_binary-3.2.10-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:5369202e0e764193eac311b5a337d8cd58b1e23b822ddb7a559ed9f683d97623"}, + {file = "psycopg_binary-3.2.10-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:8f4ae059c6c9e491cdc3f39f9fc4f09373ef281c6cc381499269dcff21abafc9"}, + {file = "psycopg_binary-3.2.10-cp38-cp38-win_amd64.whl", hash = "sha256:3e115930af2f38f4bbb5f1b61b598ceb802f091c1592c0fe0571c796b714b89a"}, + {file = "psycopg_binary-3.2.10-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0738320a8d405f98743227ff70ed8fac9670870289435f4861dc640cef4a61d3"}, + {file = "psycopg_binary-3.2.10-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:89440355d1b163b11dc661ae64a5667578aab1b80bbf71ced90693d88e9863e1"}, + {file = "psycopg_binary-3.2.10-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3234605839e7d7584bd0a20716395eba34d368a5099dafe7896c943facac98fc"}, + {file = "psycopg_binary-3.2.10-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:725843fd444075cc6c9989f5b25ca83ac68d8d70b58e1f476fbb4096975e43cc"}, + {file = "psycopg_binary-3.2.10-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:447afc326cbc95ed67c0cd27606c0f81fa933b830061e096dbd37e08501cb3de"}, + {file = "psycopg_binary-3.2.10-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5334a61a00ccb722f0b28789e265c7a273cfd10d5a1ed6bf062686fbb71e7032"}, + {file = "psycopg_binary-3.2.10-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:183a59cbdcd7e156669577fd73a9e917b1ee664e620f1e31ae138d24c7714693"}, + {file = "psycopg_binary-3.2.10-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:8fa2efaf5e2f8c289a185c91c80a624a8f97aa17fbedcbc68f373d089b332afd"}, + {file = "psycopg_binary-3.2.10-cp39-cp39-win_amd64.whl", hash = "sha256:6220d6efd6e2df7b67d70ed60d653106cd3b70c5cb8cbe4e9f0a142a5db14015"}, +] + +[[package]] +name = "pycparser" +version = "2.23" +description = "C parser in Python" +optional = false +python-versions = ">=3.8" +groups = ["main"] +markers = "implementation_name != \"PyPy\"" +files = [ + {file = "pycparser-2.23-py3-none-any.whl", hash = "sha256:e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934"}, + {file = "pycparser-2.23.tar.gz", hash = "sha256:78816d4f24add8f10a06d6f05b4d424ad9e96cfebf68a4ddc99c65c0720d00c2"}, +] + +[[package]] +name = "pycryptodome" +version = "3.23.0" +description = "Cryptographic library for Python" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["main"] +files = [ + {file = "pycryptodome-3.23.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a176b79c49af27d7f6c12e4b178b0824626f40a7b9fed08f712291b6d54bf566"}, + {file = "pycryptodome-3.23.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:573a0b3017e06f2cffd27d92ef22e46aa3be87a2d317a5abf7cc0e84e321bd75"}, + {file = "pycryptodome-3.23.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:63dad881b99ca653302b2c7191998dd677226222a3f2ea79999aa51ce695f720"}, + {file = "pycryptodome-3.23.0-cp27-cp27m-win32.whl", hash = "sha256:b34e8e11d97889df57166eda1e1ddd7676da5fcd4d71a0062a760e75060514b4"}, + {file = "pycryptodome-3.23.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:7ac1080a8da569bde76c0a104589c4f414b8ba296c0b3738cf39a466a9fb1818"}, + {file = "pycryptodome-3.23.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:6fe8258e2039eceb74dfec66b3672552b6b7d2c235b2dfecc05d16b8921649a8"}, + {file = "pycryptodome-3.23.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:0011f7f00cdb74879142011f95133274741778abba114ceca229adbf8e62c3e4"}, + {file = "pycryptodome-3.23.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:90460fc9e088ce095f9ee8356722d4f10f86e5be06e2354230a9880b9c549aae"}, + {file = "pycryptodome-3.23.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4764e64b269fc83b00f682c47443c2e6e85b18273712b98aa43bcb77f8570477"}, + {file = "pycryptodome-3.23.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb8f24adb74984aa0e5d07a2368ad95276cf38051fe2dc6605cbcf482e04f2a7"}, + {file = "pycryptodome-3.23.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d97618c9c6684a97ef7637ba43bdf6663a2e2e77efe0f863cce97a76af396446"}, + {file = "pycryptodome-3.23.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9a53a4fe5cb075075d515797d6ce2f56772ea7e6a1e5e4b96cf78a14bac3d265"}, + {file = "pycryptodome-3.23.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:763d1d74f56f031788e5d307029caef067febf890cd1f8bf61183ae142f1a77b"}, + {file = "pycryptodome-3.23.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:954af0e2bd7cea83ce72243b14e4fb518b18f0c1649b576d114973e2073b273d"}, + {file = "pycryptodome-3.23.0-cp313-cp313t-win32.whl", hash = "sha256:257bb3572c63ad8ba40b89f6fc9d63a2a628e9f9708d31ee26560925ebe0210a"}, + {file = "pycryptodome-3.23.0-cp313-cp313t-win_amd64.whl", hash = "sha256:6501790c5b62a29fcb227bd6b62012181d886a767ce9ed03b303d1f22eb5c625"}, + {file = "pycryptodome-3.23.0-cp313-cp313t-win_arm64.whl", hash = "sha256:9a77627a330ab23ca43b48b130e202582e91cc69619947840ea4d2d1be21eb39"}, + {file = "pycryptodome-3.23.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:187058ab80b3281b1de11c2e6842a357a1f71b42cb1e15bce373f3d238135c27"}, + {file = "pycryptodome-3.23.0-cp37-abi3-macosx_10_9_x86_64.whl", hash = "sha256:cfb5cd445280c5b0a4e6187a7ce8de5a07b5f3f897f235caa11f1f435f182843"}, + {file = "pycryptodome-3.23.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67bd81fcbe34f43ad9422ee8fd4843c8e7198dd88dd3d40e6de42ee65fbe1490"}, + {file = "pycryptodome-3.23.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8987bd3307a39bc03df5c8e0e3d8be0c4c3518b7f044b0f4c15d1aa78f52575"}, + {file = "pycryptodome-3.23.0-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aa0698f65e5b570426fc31b8162ed4603b0c2841cbb9088e2b01641e3065915b"}, + {file = "pycryptodome-3.23.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:53ecbafc2b55353edcebd64bf5da94a2a2cdf5090a6915bcca6eca6cc452585a"}, + {file = "pycryptodome-3.23.0-cp37-abi3-musllinux_1_2_i686.whl", hash = "sha256:156df9667ad9f2ad26255926524e1c136d6664b741547deb0a86a9acf5ea631f"}, + {file = "pycryptodome-3.23.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:dea827b4d55ee390dc89b2afe5927d4308a8b538ae91d9c6f7a5090f397af1aa"}, + {file = "pycryptodome-3.23.0-cp37-abi3-win32.whl", hash = "sha256:507dbead45474b62b2bbe318eb1c4c8ee641077532067fec9c1aa82c31f84886"}, + {file = "pycryptodome-3.23.0-cp37-abi3-win_amd64.whl", hash = "sha256:c75b52aacc6c0c260f204cbdd834f76edc9fb0d8e0da9fbf8352ef58202564e2"}, + {file = "pycryptodome-3.23.0-cp37-abi3-win_arm64.whl", hash = "sha256:11eeeb6917903876f134b56ba11abe95c0b0fd5e3330def218083c7d98bbcb3c"}, + {file = "pycryptodome-3.23.0-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:350ebc1eba1da729b35ab7627a833a1a355ee4e852d8ba0447fafe7b14504d56"}, + {file = "pycryptodome-3.23.0-pp27-pypy_73-win32.whl", hash = "sha256:93837e379a3e5fd2bb00302a47aee9fdf7940d83595be3915752c74033d17ca7"}, + {file = "pycryptodome-3.23.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ddb95b49df036ddd264a0ad246d1be5b672000f12d6961ea2c267083a5e19379"}, + {file = "pycryptodome-3.23.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e95564beb8782abfd9e431c974e14563a794a4944c29d6d3b7b5ea042110b4"}, + {file = "pycryptodome-3.23.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14e15c081e912c4b0d75632acd8382dfce45b258667aa3c67caf7a4d4c13f630"}, + {file = "pycryptodome-3.23.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7fc76bf273353dc7e5207d172b83f569540fc9a28d63171061c42e361d22353"}, + {file = "pycryptodome-3.23.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:45c69ad715ca1a94f778215a11e66b7ff989d792a4d63b68dc586a1da1392ff5"}, + {file = "pycryptodome-3.23.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:865d83c906b0fc6a59b510deceee656b6bc1c4fa0d82176e2b77e97a420a996a"}, + {file = "pycryptodome-3.23.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89d4d56153efc4d81defe8b65fd0821ef8b2d5ddf8ed19df31ba2f00872b8002"}, + {file = "pycryptodome-3.23.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3f2d0aaf8080bda0587d58fc9fe4766e012441e2eed4269a77de6aea981c8be"}, + {file = "pycryptodome-3.23.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:64093fc334c1eccfd3933c134c4457c34eaca235eeae49d69449dc4728079339"}, + {file = "pycryptodome-3.23.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:ce64e84a962b63a47a592690bdc16a7eaf709d2c2697ababf24a0def566899a6"}, + {file = "pycryptodome-3.23.0.tar.gz", hash = "sha256:447700a657182d60338bab09fdb27518f8856aecd80ae4c6bdddb67ff5da44ef"}, +] + +[[package]] +name = "pydantic" +version = "2.12.2" +description = "Data validation using Python type hints" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "pydantic-2.12.2-py3-none-any.whl", hash = "sha256:25ff718ee909acd82f1ff9b1a4acfd781bb23ab3739adaa7144f19a6a4e231ae"}, + {file = "pydantic-2.12.2.tar.gz", hash = "sha256:7b8fa15b831a4bbde9d5b84028641ac3080a4ca2cbd4a621a661687e741624fd"}, +] + +[package.dependencies] +annotated-types = ">=0.6.0" +pydantic-core = "2.41.4" +typing-extensions = ">=4.14.1" +typing-inspection = ">=0.4.2" + +[package.extras] +email = ["email-validator (>=2.0.0)"] +timezone = ["tzdata"] + +[[package]] +name = "pydantic-core" +version = "2.41.4" +description = "Core functionality for Pydantic validation and serialization" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "pydantic_core-2.41.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2442d9a4d38f3411f22eb9dd0912b7cbf4b7d5b6c92c4173b75d3e1ccd84e36e"}, + {file = "pydantic_core-2.41.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:30a9876226dda131a741afeab2702e2d127209bde3c65a2b8133f428bc5d006b"}, + {file = "pydantic_core-2.41.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d55bbac04711e2980645af68b97d445cdbcce70e5216de444a6c4b6943ebcccd"}, + {file = "pydantic_core-2.41.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e1d778fb7849a42d0ee5927ab0f7453bf9f85eef8887a546ec87db5ddb178945"}, + {file = "pydantic_core-2.41.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1b65077a4693a98b90ec5ad8f203ad65802a1b9b6d4a7e48066925a7e1606706"}, + {file = "pydantic_core-2.41.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:62637c769dee16eddb7686bf421be48dfc2fae93832c25e25bc7242e698361ba"}, + {file = "pydantic_core-2.41.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2dfe3aa529c8f501babf6e502936b9e8d4698502b2cfab41e17a028d91b1ac7b"}, + {file = "pydantic_core-2.41.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ca2322da745bf2eeb581fc9ea3bbb31147702163ccbcbf12a3bb630e4bf05e1d"}, + {file = "pydantic_core-2.41.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e8cd3577c796be7231dcf80badcf2e0835a46665eaafd8ace124d886bab4d700"}, + {file = "pydantic_core-2.41.4-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:1cae8851e174c83633f0833e90636832857297900133705ee158cf79d40f03e6"}, + {file = "pydantic_core-2.41.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a26d950449aae348afe1ac8be5525a00ae4235309b729ad4d3399623125b43c9"}, + {file = "pydantic_core-2.41.4-cp310-cp310-win32.whl", hash = "sha256:0cf2a1f599efe57fa0051312774280ee0f650e11152325e41dfd3018ef2c1b57"}, + {file = "pydantic_core-2.41.4-cp310-cp310-win_amd64.whl", hash = "sha256:a8c2e340d7e454dc3340d3d2e8f23558ebe78c98aa8f68851b04dcb7bc37abdc"}, + {file = "pydantic_core-2.41.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:28ff11666443a1a8cf2a044d6a545ebffa8382b5f7973f22c36109205e65dc80"}, + {file = "pydantic_core-2.41.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:61760c3925d4633290292bad462e0f737b840508b4f722247d8729684f6539ae"}, + {file = "pydantic_core-2.41.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eae547b7315d055b0de2ec3965643b0ab82ad0106a7ffd29615ee9f266a02827"}, + {file = "pydantic_core-2.41.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ef9ee5471edd58d1fcce1c80ffc8783a650e3e3a193fe90d52e43bb4d87bff1f"}, + {file = "pydantic_core-2.41.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:15dd504af121caaf2c95cb90c0ebf71603c53de98305621b94da0f967e572def"}, + {file = "pydantic_core-2.41.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3a926768ea49a8af4d36abd6a8968b8790f7f76dd7cbd5a4c180db2b4ac9a3a2"}, + {file = "pydantic_core-2.41.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6916b9b7d134bff5440098a4deb80e4cb623e68974a87883299de9124126c2a8"}, + {file = "pydantic_core-2.41.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5cf90535979089df02e6f17ffd076f07237efa55b7343d98760bde8743c4b265"}, + {file = "pydantic_core-2.41.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:7533c76fa647fade2d7ec75ac5cc079ab3f34879626dae5689b27790a6cf5a5c"}, + {file = "pydantic_core-2.41.4-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:37e516bca9264cbf29612539801ca3cd5d1be465f940417b002905e6ed79d38a"}, + {file = "pydantic_core-2.41.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0c19cb355224037c83642429b8ce261ae108e1c5fbf5c028bac63c77b0f8646e"}, + {file = "pydantic_core-2.41.4-cp311-cp311-win32.whl", hash = "sha256:09c2a60e55b357284b5f31f5ab275ba9f7f70b7525e18a132ec1f9160b4f1f03"}, + {file = "pydantic_core-2.41.4-cp311-cp311-win_amd64.whl", hash = "sha256:711156b6afb5cb1cb7c14a2cc2c4a8b4c717b69046f13c6b332d8a0a8f41ca3e"}, + {file = "pydantic_core-2.41.4-cp311-cp311-win_arm64.whl", hash = "sha256:6cb9cf7e761f4f8a8589a45e49ed3c0d92d1d696a45a6feaee8c904b26efc2db"}, + {file = "pydantic_core-2.41.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:ab06d77e053d660a6faaf04894446df7b0a7e7aba70c2797465a0a1af00fc887"}, + {file = "pydantic_core-2.41.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c53ff33e603a9c1179a9364b0a24694f183717b2e0da2b5ad43c316c956901b2"}, + {file = "pydantic_core-2.41.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:304c54176af2c143bd181d82e77c15c41cbacea8872a2225dd37e6544dce9999"}, + {file = "pydantic_core-2.41.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:025ba34a4cf4fb32f917d5d188ab5e702223d3ba603be4d8aca2f82bede432a4"}, + {file = "pydantic_core-2.41.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b9f5f30c402ed58f90c70e12eff65547d3ab74685ffe8283c719e6bead8ef53f"}, + {file = "pydantic_core-2.41.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd96e5d15385d301733113bcaa324c8bcf111275b7675a9c6e88bfb19fc05e3b"}, + {file = "pydantic_core-2.41.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98f348cbb44fae6e9653c1055db7e29de67ea6a9ca03a5fa2c2e11a47cff0e47"}, + {file = "pydantic_core-2.41.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ec22626a2d14620a83ca583c6f5a4080fa3155282718b6055c2ea48d3ef35970"}, + {file = "pydantic_core-2.41.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3a95d4590b1f1a43bf33ca6d647b990a88f4a3824a8c4572c708f0b45a5290ed"}, + {file = "pydantic_core-2.41.4-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:f9672ab4d398e1b602feadcffcdd3af44d5f5e6ddc15bc7d15d376d47e8e19f8"}, + {file = "pydantic_core-2.41.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:84d8854db5f55fead3b579f04bda9a36461dab0730c5d570e1526483e7bb8431"}, + {file = "pydantic_core-2.41.4-cp312-cp312-win32.whl", hash = "sha256:9be1c01adb2ecc4e464392c36d17f97e9110fbbc906bcbe1c943b5b87a74aabd"}, + {file = "pydantic_core-2.41.4-cp312-cp312-win_amd64.whl", hash = "sha256:d682cf1d22bab22a5be08539dca3d1593488a99998f9f412137bc323179067ff"}, + {file = "pydantic_core-2.41.4-cp312-cp312-win_arm64.whl", hash = "sha256:833eebfd75a26d17470b58768c1834dfc90141b7afc6eb0429c21fc5a21dcfb8"}, + {file = "pydantic_core-2.41.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:85e050ad9e5f6fe1004eec65c914332e52f429bc0ae12d6fa2092407a462c746"}, + {file = "pydantic_core-2.41.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e7393f1d64792763a48924ba31d1e44c2cfbc05e3b1c2c9abb4ceeadd912cced"}, + {file = "pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94dab0940b0d1fb28bcab847adf887c66a27a40291eedf0b473be58761c9799a"}, + {file = "pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:de7c42f897e689ee6f9e93c4bec72b99ae3b32a2ade1c7e4798e690ff5246e02"}, + {file = "pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:664b3199193262277b8b3cd1e754fb07f2c6023289c815a1e1e8fb415cb247b1"}, + {file = "pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d95b253b88f7d308b1c0b417c4624f44553ba4762816f94e6986819b9c273fb2"}, + {file = "pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a1351f5bbdbbabc689727cb91649a00cb9ee7203e0a6e54e9f5ba9e22e384b84"}, + {file = "pydantic_core-2.41.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1affa4798520b148d7182da0615d648e752de4ab1a9566b7471bc803d88a062d"}, + {file = "pydantic_core-2.41.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7b74e18052fea4aa8dea2fb7dbc23d15439695da6cbe6cfc1b694af1115df09d"}, + {file = "pydantic_core-2.41.4-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:285b643d75c0e30abda9dc1077395624f314a37e3c09ca402d4015ef5979f1a2"}, + {file = "pydantic_core-2.41.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:f52679ff4218d713b3b33f88c89ccbf3a5c2c12ba665fb80ccc4192b4608dbab"}, + {file = "pydantic_core-2.41.4-cp313-cp313-win32.whl", hash = "sha256:ecde6dedd6fff127c273c76821bb754d793be1024bc33314a120f83a3c69460c"}, + {file = "pydantic_core-2.41.4-cp313-cp313-win_amd64.whl", hash = "sha256:d081a1f3800f05409ed868ebb2d74ac39dd0c1ff6c035b5162356d76030736d4"}, + {file = "pydantic_core-2.41.4-cp313-cp313-win_arm64.whl", hash = "sha256:f8e49c9c364a7edcbe2a310f12733aad95b022495ef2a8d653f645e5d20c1564"}, + {file = "pydantic_core-2.41.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:ed97fd56a561f5eb5706cebe94f1ad7c13b84d98312a05546f2ad036bafe87f4"}, + {file = "pydantic_core-2.41.4-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a870c307bf1ee91fc58a9a61338ff780d01bfae45922624816878dce784095d2"}, + {file = "pydantic_core-2.41.4-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d25e97bc1f5f8f7985bdc2335ef9e73843bb561eb1fa6831fdfc295c1c2061cf"}, + {file = "pydantic_core-2.41.4-cp313-cp313t-win_amd64.whl", hash = "sha256:d405d14bea042f166512add3091c1af40437c2e7f86988f3915fabd27b1e9cd2"}, + {file = "pydantic_core-2.41.4-cp313-cp313t-win_arm64.whl", hash = "sha256:19f3684868309db5263a11bace3c45d93f6f24afa2ffe75a647583df22a2ff89"}, + {file = "pydantic_core-2.41.4-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:e9205d97ed08a82ebb9a307e92914bb30e18cdf6f6b12ca4bedadb1588a0bfe1"}, + {file = "pydantic_core-2.41.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:82df1f432b37d832709fbcc0e24394bba04a01b6ecf1ee87578145c19cde12ac"}, + {file = "pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc3b4cc4539e055cfa39a3763c939f9d409eb40e85813257dcd761985a108554"}, + {file = "pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b1eb1754fce47c63d2ff57fdb88c351a6c0150995890088b33767a10218eaa4e"}, + {file = "pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e6ab5ab30ef325b443f379ddb575a34969c333004fca5a1daa0133a6ffaad616"}, + {file = "pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:31a41030b1d9ca497634092b46481b937ff9397a86f9f51bd41c4767b6fc04af"}, + {file = "pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a44ac1738591472c3d020f61c6df1e4015180d6262ebd39bf2aeb52571b60f12"}, + {file = "pydantic_core-2.41.4-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d72f2b5e6e82ab8f94ea7d0d42f83c487dc159c5240d8f83beae684472864e2d"}, + {file = "pydantic_core-2.41.4-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:c4d1e854aaf044487d31143f541f7aafe7b482ae72a022c664b2de2e466ed0ad"}, + {file = "pydantic_core-2.41.4-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:b568af94267729d76e6ee5ececda4e283d07bbb28e8148bb17adad93d025d25a"}, + {file = "pydantic_core-2.41.4-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:6d55fb8b1e8929b341cc313a81a26e0d48aa3b519c1dbaadec3a6a2b4fcad025"}, + {file = "pydantic_core-2.41.4-cp314-cp314-win32.whl", hash = "sha256:5b66584e549e2e32a1398df11da2e0a7eff45d5c2d9db9d5667c5e6ac764d77e"}, + {file = "pydantic_core-2.41.4-cp314-cp314-win_amd64.whl", hash = "sha256:557a0aab88664cc552285316809cab897716a372afaf8efdbef756f8b890e894"}, + {file = "pydantic_core-2.41.4-cp314-cp314-win_arm64.whl", hash = "sha256:3f1ea6f48a045745d0d9f325989d8abd3f1eaf47dd00485912d1a3a63c623a8d"}, + {file = "pydantic_core-2.41.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6c1fe4c5404c448b13188dd8bd2ebc2bdd7e6727fa61ff481bcc2cca894018da"}, + {file = "pydantic_core-2.41.4-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:523e7da4d43b113bf8e7b49fa4ec0c35bf4fe66b2230bfc5c13cc498f12c6c3e"}, + {file = "pydantic_core-2.41.4-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5729225de81fb65b70fdb1907fcf08c75d498f4a6f15af005aabb1fdadc19dfa"}, + {file = "pydantic_core-2.41.4-cp314-cp314t-win_amd64.whl", hash = "sha256:de2cfbb09e88f0f795fd90cf955858fc2c691df65b1f21f0aa00b99f3fbc661d"}, + {file = "pydantic_core-2.41.4-cp314-cp314t-win_arm64.whl", hash = "sha256:d34f950ae05a83e0ede899c595f312ca976023ea1db100cd5aa188f7005e3ab0"}, + {file = "pydantic_core-2.41.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:646e76293345954acea6966149683047b7b2ace793011922208c8e9da12b0062"}, + {file = "pydantic_core-2.41.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cc8e85a63085a137d286e2791037f5fdfff0aabb8b899483ca9c496dd5797338"}, + {file = "pydantic_core-2.41.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:692c622c8f859a17c156492783902d8370ac7e121a611bd6fe92cc71acf9ee8d"}, + {file = "pydantic_core-2.41.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d1e2906efb1031a532600679b424ef1d95d9f9fb507f813951f23320903adbd7"}, + {file = "pydantic_core-2.41.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e04e2f7f8916ad3ddd417a7abdd295276a0bf216993d9318a5d61cc058209166"}, + {file = "pydantic_core-2.41.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:df649916b81822543d1c8e0e1d079235f68acdc7d270c911e8425045a8cfc57e"}, + {file = "pydantic_core-2.41.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66c529f862fdba70558061bb936fe00ddbaaa0c647fd26e4a4356ef1d6561891"}, + {file = "pydantic_core-2.41.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fc3b4c5a1fd3a311563ed866c2c9b62da06cb6398bee186484ce95c820db71cb"}, + {file = "pydantic_core-2.41.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:6e0fc40d84448f941df9b3334c4b78fe42f36e3bf631ad54c3047a0cdddc2514"}, + {file = "pydantic_core-2.41.4-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:44e7625332683b6c1c8b980461475cde9595eff94447500e80716db89b0da005"}, + {file = "pydantic_core-2.41.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:170ee6835f6c71081d031ef1c3b4dc4a12b9efa6a9540f93f95b82f3c7571ae8"}, + {file = "pydantic_core-2.41.4-cp39-cp39-win32.whl", hash = "sha256:3adf61415efa6ce977041ba9745183c0e1f637ca849773afa93833e04b163feb"}, + {file = "pydantic_core-2.41.4-cp39-cp39-win_amd64.whl", hash = "sha256:a238dd3feee263eeaeb7dc44aea4ba1364682c4f9f9467e6af5596ba322c2332"}, + {file = "pydantic_core-2.41.4-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:a1b2cfec3879afb742a7b0bcfa53e4f22ba96571c9e54d6a3afe1052d17d843b"}, + {file = "pydantic_core-2.41.4-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:d175600d975b7c244af6eb9c9041f10059f20b8bbffec9e33fdd5ee3f67cdc42"}, + {file = "pydantic_core-2.41.4-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f184d657fa4947ae5ec9c47bd7e917730fa1cbb78195037e32dcbab50aca5ee"}, + {file = "pydantic_core-2.41.4-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ed810568aeffed3edc78910af32af911c835cc39ebbfacd1f0ab5dd53028e5c"}, + {file = "pydantic_core-2.41.4-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:4f5d640aeebb438517150fdeec097739614421900e4a08db4a3ef38898798537"}, + {file = "pydantic_core-2.41.4-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:4a9ab037b71927babc6d9e7fc01aea9e66dc2a4a34dff06ef0724a4049629f94"}, + {file = "pydantic_core-2.41.4-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4dab9484ec605c3016df9ad4fd4f9a390bc5d816a3b10c6550f8424bb80b18c"}, + {file = "pydantic_core-2.41.4-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd8a5028425820731d8c6c098ab642d7b8b999758e24acae03ed38a66eca8335"}, + {file = "pydantic_core-2.41.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:1e5ab4fc177dd41536b3c32b2ea11380dd3d4619a385860621478ac2d25ceb00"}, + {file = "pydantic_core-2.41.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:3d88d0054d3fa11ce936184896bed3c1c5441d6fa483b498fac6a5d0dd6f64a9"}, + {file = "pydantic_core-2.41.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b2a054a8725f05b4b6503357e0ac1c4e8234ad3b0c2ac130d6ffc66f0e170e2"}, + {file = "pydantic_core-2.41.4-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b0d9db5a161c99375a0c68c058e227bee1d89303300802601d76a3d01f74e258"}, + {file = "pydantic_core-2.41.4-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:6273ea2c8ffdac7b7fda2653c49682db815aebf4a89243a6feccf5e36c18c347"}, + {file = "pydantic_core-2.41.4-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:4c973add636efc61de22530b2ef83a65f39b6d6f656df97f678720e20de26caa"}, + {file = "pydantic_core-2.41.4-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:b69d1973354758007f46cf2d44a4f3d0933f10b6dc9bf15cf1356e037f6f731a"}, + {file = "pydantic_core-2.41.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:3619320641fd212aaf5997b6ca505e97540b7e16418f4a241f44cdf108ffb50d"}, + {file = "pydantic_core-2.41.4-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:491535d45cd7ad7e4a2af4a5169b0d07bebf1adfd164b0368da8aa41e19907a5"}, + {file = "pydantic_core-2.41.4-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:54d86c0cada6aba4ec4c047d0e348cbad7063b87ae0f005d9f8c9ad04d4a92a2"}, + {file = "pydantic_core-2.41.4-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eca1124aced216b2500dc2609eade086d718e8249cb9696660ab447d50a758bd"}, + {file = "pydantic_core-2.41.4-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6c9024169becccf0cb470ada03ee578d7348c119a0d42af3dcf9eda96e3a247c"}, + {file = "pydantic_core-2.41.4-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:26895a4268ae5a2849269f4991cdc97236e4b9c010e51137becf25182daac405"}, + {file = "pydantic_core-2.41.4-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:ca4df25762cf71308c446e33c9b1fdca2923a3f13de616e2a949f38bf21ff5a8"}, + {file = "pydantic_core-2.41.4-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:5a28fcedd762349519276c36634e71853b4541079cab4acaaac60c4421827308"}, + {file = "pydantic_core-2.41.4-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:c173ddcd86afd2535e2b695217e82191580663a1d1928239f877f5a1649ef39f"}, + {file = "pydantic_core-2.41.4.tar.gz", hash = "sha256:70e47929a9d4a1905a67e4b687d5946026390568a8e952b92824118063cee4d5"}, +] + +[package.dependencies] +typing-extensions = ">=4.14.1" + +[[package]] +name = "pygments" +version = "2.19.2" +description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=3.8" +groups = ["main", "dev"] +files = [ + {file = "pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b"}, + {file = "pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887"}, +] + +[package.extras] +windows-terminal = ["colorama (>=0.4.6)"] + +[[package]] +name = "pytest" +version = "8.4.2" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.9" +groups = ["main", "dev"] +files = [ + {file = "pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79"}, + {file = "pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01"}, +] + +[package.dependencies] +colorama = {version = ">=0.4", markers = "sys_platform == \"win32\""} +iniconfig = ">=1" +packaging = ">=20" +pluggy = ">=1.5,<2" +pygments = ">=2.7.2" + +[package.extras] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "pytest-asyncio" +version = "1.2.0" +description = "Pytest support for asyncio" +optional = false +python-versions = ">=3.9" +groups = ["main", "dev"] +files = [ + {file = "pytest_asyncio-1.2.0-py3-none-any.whl", hash = "sha256:8e17ae5e46d8e7efe51ab6494dd2010f4ca8dae51652aa3c8d55acf50bfb2e99"}, + {file = "pytest_asyncio-1.2.0.tar.gz", hash = "sha256:c609a64a2a8768462d0c99811ddb8bd2583c33fd33cf7f21af1c142e824ffb57"}, +] + +[package.dependencies] +pytest = ">=8.2,<9" +typing-extensions = {version = ">=4.12", markers = "python_version < \"3.13\""} + +[package.extras] +docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1)"] +testing = ["coverage (>=6.2)", "hypothesis (>=5.7.1)"] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +description = "Extensions to the standard Python datetime module" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +groups = ["main"] +files = [ + {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, + {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, +] + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "pyyaml" +version = "6.0.3" +description = "YAML parser and emitter for Python" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "PyYAML-6.0.3-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:c2514fceb77bc5e7a2f7adfaa1feb2fb311607c9cb518dbc378688ec73d8292f"}, + {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c57bb8c96f6d1808c030b1687b9b5fb476abaa47f0db9c0101f5e9f394e97f4"}, + {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:efd7b85f94a6f21e4932043973a7ba2613b059c4a000551892ac9f1d11f5baf3"}, + {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22ba7cfcad58ef3ecddc7ed1db3409af68d023b7f940da23c6c2a1890976eda6"}, + {file = "PyYAML-6.0.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:6344df0d5755a2c9a276d4473ae6b90647e216ab4757f8426893b5dd2ac3f369"}, + {file = "PyYAML-6.0.3-cp38-cp38-win32.whl", hash = "sha256:3ff07ec89bae51176c0549bc4c63aa6202991da2d9a6129d7aef7f1407d3f295"}, + {file = "PyYAML-6.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:5cf4e27da7e3fbed4d6c3d8e797387aaad68102272f8f9752883bc32d61cb87b"}, + {file = "pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b"}, + {file = "pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956"}, + {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8"}, + {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198"}, + {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b"}, + {file = "pyyaml-6.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0"}, + {file = "pyyaml-6.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69"}, + {file = "pyyaml-6.0.3-cp310-cp310-win32.whl", hash = "sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e"}, + {file = "pyyaml-6.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c"}, + {file = "pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e"}, + {file = "pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824"}, + {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c"}, + {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00"}, + {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d"}, + {file = "pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a"}, + {file = "pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4"}, + {file = "pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b"}, + {file = "pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf"}, + {file = "pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196"}, + {file = "pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0"}, + {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28"}, + {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c"}, + {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc"}, + {file = "pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e"}, + {file = "pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea"}, + {file = "pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5"}, + {file = "pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b"}, + {file = "pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd"}, + {file = "pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8"}, + {file = "pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1"}, + {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c"}, + {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5"}, + {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6"}, + {file = "pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6"}, + {file = "pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be"}, + {file = "pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26"}, + {file = "pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c"}, + {file = "pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb"}, + {file = "pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac"}, + {file = "pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310"}, + {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7"}, + {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788"}, + {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5"}, + {file = "pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764"}, + {file = "pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35"}, + {file = "pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac"}, + {file = "pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3"}, + {file = "pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3"}, + {file = "pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba"}, + {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c"}, + {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702"}, + {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c"}, + {file = "pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065"}, + {file = "pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65"}, + {file = "pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9"}, + {file = "pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b"}, + {file = "pyyaml-6.0.3-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:b865addae83924361678b652338317d1bd7e79b1f4596f96b96c77a5a34b34da"}, + {file = "pyyaml-6.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c3355370a2c156cffb25e876646f149d5d68f5e0a3ce86a5084dd0b64a994917"}, + {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3c5677e12444c15717b902a5798264fa7909e41153cdf9ef7ad571b704a63dd9"}, + {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5ed875a24292240029e4483f9d4a4b8a1ae08843b9c54f43fcc11e404532a8a5"}, + {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0150219816b6a1fa26fb4699fb7daa9caf09eb1999f3b70fb6e786805e80375a"}, + {file = "pyyaml-6.0.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:fa160448684b4e94d80416c0fa4aac48967a969efe22931448d853ada8baf926"}, + {file = "pyyaml-6.0.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:27c0abcb4a5dac13684a37f76e701e054692a9b2d3064b70f5e4eb54810553d7"}, + {file = "pyyaml-6.0.3-cp39-cp39-win32.whl", hash = "sha256:1ebe39cb5fc479422b83de611d14e2c0d3bb2a18bbcb01f229ab3cfbd8fee7a0"}, + {file = "pyyaml-6.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:2e71d11abed7344e42a8849600193d15b6def118602c4c176f748e4583246007"}, + {file = "pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f"}, +] + +[[package]] +name = "ruff" +version = "0.9.10" +description = "An extremely fast Python linter and code formatter, written in Rust." +optional = false +python-versions = ">=3.7" +groups = ["dev"] +files = [ + {file = "ruff-0.9.10-py3-none-linux_armv6l.whl", hash = "sha256:eb4d25532cfd9fe461acc83498361ec2e2252795b4f40b17e80692814329e42d"}, + {file = "ruff-0.9.10-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:188a6638dab1aa9bb6228a7302387b2c9954e455fb25d6b4470cb0641d16759d"}, + {file = "ruff-0.9.10-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5284dcac6b9dbc2fcb71fdfc26a217b2ca4ede6ccd57476f52a587451ebe450d"}, + {file = "ruff-0.9.10-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47678f39fa2a3da62724851107f438c8229a3470f533894b5568a39b40029c0c"}, + {file = "ruff-0.9.10-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:99713a6e2766b7a17147b309e8c915b32b07a25c9efd12ada79f217c9c778b3e"}, + {file = "ruff-0.9.10-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:524ee184d92f7c7304aa568e2db20f50c32d1d0caa235d8ddf10497566ea1a12"}, + {file = "ruff-0.9.10-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:df92aeac30af821f9acf819fc01b4afc3dfb829d2782884f8739fb52a8119a16"}, + {file = "ruff-0.9.10-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de42e4edc296f520bb84954eb992a07a0ec5a02fecb834498415908469854a52"}, + {file = "ruff-0.9.10-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d257f95b65806104b6b1ffca0ea53f4ef98454036df65b1eda3693534813ecd1"}, + {file = "ruff-0.9.10-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b60dec7201c0b10d6d11be00e8f2dbb6f40ef1828ee75ed739923799513db24c"}, + {file = "ruff-0.9.10-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:d838b60007da7a39c046fcdd317293d10b845001f38bcb55ba766c3875b01e43"}, + {file = "ruff-0.9.10-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:ccaf903108b899beb8e09a63ffae5869057ab649c1e9231c05ae354ebc62066c"}, + {file = "ruff-0.9.10-py3-none-musllinux_1_2_i686.whl", hash = "sha256:f9567d135265d46e59d62dc60c0bfad10e9a6822e231f5b24032dba5a55be6b5"}, + {file = "ruff-0.9.10-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5f202f0d93738c28a89f8ed9eaba01b7be339e5d8d642c994347eaa81c6d75b8"}, + {file = "ruff-0.9.10-py3-none-win32.whl", hash = "sha256:bfb834e87c916521ce46b1788fbb8484966e5113c02df216680102e9eb960029"}, + {file = "ruff-0.9.10-py3-none-win_amd64.whl", hash = "sha256:f2160eeef3031bf4b17df74e307d4c5fb689a6f3a26a2de3f7ef4044e3c484f1"}, + {file = "ruff-0.9.10-py3-none-win_arm64.whl", hash = "sha256:5fd804c0327a5e5ea26615550e706942f348b197d5475ff34c19733aee4b2e69"}, + {file = "ruff-0.9.10.tar.gz", hash = "sha256:9bacb735d7bada9cfb0f2c227d3658fc443d90a727b47f206fb33f52f3c0eac7"}, +] + +[[package]] +name = "six" +version = "1.17.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +groups = ["main"] +files = [ + {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, + {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, +] + +[[package]] +name = "sniffio" +version = "1.3.1" +description = "Sniff out which async library your code is running under" +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, + {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, +] + +[[package]] +name = "starlette" +version = "0.46.2" +description = "The little ASGI library that shines." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "starlette-0.46.2-py3-none-any.whl", hash = "sha256:595633ce89f8ffa71a015caed34a5b2dc1c0cdb3f0f1fbd1e69339cf2abeec35"}, + {file = "starlette-0.46.2.tar.gz", hash = "sha256:7f7361f34eed179294600af672f565727419830b54b7b084efe44bb82d2fccd5"}, +] + +[package.dependencies] +anyio = ">=3.6.2,<5" + +[package.extras] +full = ["httpx (>=0.27.0,<0.29.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.18)", "pyyaml"] + +[[package]] +name = "structlog" +version = "25.4.0" +description = "Structured Logging for Python" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "structlog-25.4.0-py3-none-any.whl", hash = "sha256:fe809ff5c27e557d14e613f45ca441aabda051d119ee5a0102aaba6ce40eed2c"}, + {file = "structlog-25.4.0.tar.gz", hash = "sha256:186cd1b0a8ae762e29417095664adf1d6a31702160a46dacb7796ea82f7409e4"}, +] + +[[package]] +name = "typing-extensions" +version = "4.15.0" +description = "Backported and Experimental Type Hints for Python 3.9+" +optional = false +python-versions = ">=3.9" +groups = ["main", "dev"] +files = [ + {file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"}, + {file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"}, +] +markers = {dev = "python_version < \"3.13\""} + +[[package]] +name = "typing-inspection" +version = "0.4.2" +description = "Runtime typing introspection tools" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7"}, + {file = "typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464"}, +] + +[package.dependencies] +typing-extensions = ">=4.12.0" + +[[package]] +name = "tzdata" +version = "2025.2" +description = "Provider of IANA time zone data" +optional = false +python-versions = ">=2" +groups = ["main"] +markers = "sys_platform == \"win32\"" +files = [ + {file = "tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8"}, + {file = "tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9"}, +] + +[[package]] +name = "urllib3" +version = "2.5.0" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc"}, + {file = "urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +h2 = ["h2 (>=4,<5)"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] + +[[package]] +name = "yarl" +version = "1.22.0" +description = "Yet another URL library" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "yarl-1.22.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c7bd6683587567e5a49ee6e336e0612bec8329be1b7d4c8af5687dcdeb67ee1e"}, + {file = "yarl-1.22.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5cdac20da754f3a723cceea5b3448e1a2074866406adeb4ef35b469d089adb8f"}, + {file = "yarl-1.22.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:07a524d84df0c10f41e3ee918846e1974aba4ec017f990dc735aad487a0bdfdf"}, + {file = "yarl-1.22.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e1b329cb8146d7b736677a2440e422eadd775d1806a81db2d4cded80a48efc1a"}, + {file = "yarl-1.22.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:75976c6945d85dbb9ee6308cd7ff7b1fb9409380c82d6119bd778d8fcfe2931c"}, + {file = "yarl-1.22.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:80ddf7a5f8c86cb3eb4bc9028b07bbbf1f08a96c5c0bc1244be5e8fefcb94147"}, + {file = "yarl-1.22.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d332fc2e3c94dad927f2112395772a4e4fedbcf8f80efc21ed7cdfae4d574fdb"}, + {file = "yarl-1.22.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0cf71bf877efeac18b38d3930594c0948c82b64547c1cf420ba48722fe5509f6"}, + {file = "yarl-1.22.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:663e1cadaddae26be034a6ab6072449a8426ddb03d500f43daf952b74553bba0"}, + {file = "yarl-1.22.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:6dcbb0829c671f305be48a7227918cfcd11276c2d637a8033a99a02b67bf9eda"}, + {file = "yarl-1.22.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f0d97c18dfd9a9af4490631905a3f131a8e4c9e80a39353919e2cfed8f00aedc"}, + {file = "yarl-1.22.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:437840083abe022c978470b942ff832c3940b2ad3734d424b7eaffcd07f76737"}, + {file = "yarl-1.22.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a899cbd98dce6f5d8de1aad31cb712ec0a530abc0a86bd6edaa47c1090138467"}, + {file = "yarl-1.22.0-cp310-cp310-win32.whl", hash = "sha256:595697f68bd1f0c1c159fcb97b661fc9c3f5db46498043555d04805430e79bea"}, + {file = "yarl-1.22.0-cp310-cp310-win_amd64.whl", hash = "sha256:cb95a9b1adaa48e41815a55ae740cfda005758104049a640a398120bf02515ca"}, + {file = "yarl-1.22.0-cp310-cp310-win_arm64.whl", hash = "sha256:b85b982afde6df99ecc996990d4ad7ccbdbb70e2a4ba4de0aecde5922ba98a0b"}, + {file = "yarl-1.22.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1ab72135b1f2db3fed3997d7e7dc1b80573c67138023852b6efb336a5eae6511"}, + {file = "yarl-1.22.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:669930400e375570189492dc8d8341301578e8493aec04aebc20d4717f899dd6"}, + {file = "yarl-1.22.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:792a2af6d58177ef7c19cbf0097aba92ca1b9cb3ffdd9c7470e156c8f9b5e028"}, + {file = "yarl-1.22.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3ea66b1c11c9150f1372f69afb6b8116f2dd7286f38e14ea71a44eee9ec51b9d"}, + {file = "yarl-1.22.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3e2daa88dc91870215961e96a039ec73e4937da13cf77ce17f9cad0c18df3503"}, + {file = "yarl-1.22.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ba440ae430c00eee41509353628600212112cd5018d5def7e9b05ea7ac34eb65"}, + {file = "yarl-1.22.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e6438cc8f23a9c1478633d216b16104a586b9761db62bfacb6425bac0a36679e"}, + {file = "yarl-1.22.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c52a6e78aef5cf47a98ef8e934755abf53953379b7d53e68b15ff4420e6683d"}, + {file = "yarl-1.22.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3b06bcadaac49c70f4c88af4ffcfbe3dc155aab3163e75777818092478bcbbe7"}, + {file = "yarl-1.22.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:6944b2dc72c4d7f7052683487e3677456050ff77fcf5e6204e98caf785ad1967"}, + {file = "yarl-1.22.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:d5372ca1df0f91a86b047d1277c2aaf1edb32d78bbcefffc81b40ffd18f027ed"}, + {file = "yarl-1.22.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:51af598701f5299012b8416486b40fceef8c26fc87dc6d7d1f6fc30609ea0aa6"}, + {file = "yarl-1.22.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b266bd01fedeffeeac01a79ae181719ff848a5a13ce10075adbefc8f1daee70e"}, + {file = "yarl-1.22.0-cp311-cp311-win32.whl", hash = "sha256:a9b1ba5610a4e20f655258d5a1fdc7ebe3d837bb0e45b581398b99eb98b1f5ca"}, + {file = "yarl-1.22.0-cp311-cp311-win_amd64.whl", hash = "sha256:078278b9b0b11568937d9509b589ee83ef98ed6d561dfe2020e24a9fd08eaa2b"}, + {file = "yarl-1.22.0-cp311-cp311-win_arm64.whl", hash = "sha256:b6a6f620cfe13ccec221fa312139135166e47ae169f8253f72a0abc0dae94376"}, + {file = "yarl-1.22.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e340382d1afa5d32b892b3ff062436d592ec3d692aeea3bef3a5cfe11bbf8c6f"}, + {file = "yarl-1.22.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f1e09112a2c31ffe8d80be1b0988fa6a18c5d5cad92a9ffbb1c04c91bfe52ad2"}, + {file = "yarl-1.22.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:939fe60db294c786f6b7c2d2e121576628468f65453d86b0fe36cb52f987bd74"}, + {file = "yarl-1.22.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e1651bf8e0398574646744c1885a41198eba53dc8a9312b954073f845c90a8df"}, + {file = "yarl-1.22.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b8a0588521a26bf92a57a1705b77b8b59044cdceccac7151bd8d229e66b8dedb"}, + {file = "yarl-1.22.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:42188e6a615c1a75bcaa6e150c3fe8f3e8680471a6b10150c5f7e83f47cc34d2"}, + {file = "yarl-1.22.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f6d2cb59377d99718913ad9a151030d6f83ef420a2b8f521d94609ecc106ee82"}, + {file = "yarl-1.22.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50678a3b71c751d58d7908edc96d332af328839eea883bb554a43f539101277a"}, + {file = "yarl-1.22.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1e8fbaa7cec507aa24ea27a01456e8dd4b6fab829059b69844bd348f2d467124"}, + {file = "yarl-1.22.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:433885ab5431bc3d3d4f2f9bd15bfa1614c522b0f1405d62c4f926ccd69d04fa"}, + {file = "yarl-1.22.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:b790b39c7e9a4192dc2e201a282109ed2985a1ddbd5ac08dc56d0e121400a8f7"}, + {file = "yarl-1.22.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:31f0b53913220599446872d757257be5898019c85e7971599065bc55065dc99d"}, + {file = "yarl-1.22.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a49370e8f711daec68d09b821a34e1167792ee2d24d405cbc2387be4f158b520"}, + {file = "yarl-1.22.0-cp312-cp312-win32.whl", hash = "sha256:70dfd4f241c04bd9239d53b17f11e6ab672b9f1420364af63e8531198e3f5fe8"}, + {file = "yarl-1.22.0-cp312-cp312-win_amd64.whl", hash = "sha256:8884d8b332a5e9b88e23f60bb166890009429391864c685e17bd73a9eda9105c"}, + {file = "yarl-1.22.0-cp312-cp312-win_arm64.whl", hash = "sha256:ea70f61a47f3cc93bdf8b2f368ed359ef02a01ca6393916bc8ff877427181e74"}, + {file = "yarl-1.22.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8dee9c25c74997f6a750cd317b8ca63545169c098faee42c84aa5e506c819b53"}, + {file = "yarl-1.22.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:01e73b85a5434f89fc4fe27dcda2aff08ddf35e4d47bbbea3bdcd25321af538a"}, + {file = "yarl-1.22.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:22965c2af250d20c873cdbee8ff958fb809940aeb2e74ba5f20aaf6b7ac8c70c"}, + {file = "yarl-1.22.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b4f15793aa49793ec8d1c708ab7f9eded1aa72edc5174cae703651555ed1b601"}, + {file = "yarl-1.22.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5542339dcf2747135c5c85f68680353d5cb9ffd741c0f2e8d832d054d41f35a"}, + {file = "yarl-1.22.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5c401e05ad47a75869c3ab3e35137f8468b846770587e70d71e11de797d113df"}, + {file = "yarl-1.22.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:243dda95d901c733f5b59214d28b0120893d91777cb8aa043e6ef059d3cddfe2"}, + {file = "yarl-1.22.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bec03d0d388060058f5d291a813f21c011041938a441c593374da6077fe21b1b"}, + {file = "yarl-1.22.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b0748275abb8c1e1e09301ee3cf90c8a99678a4e92e4373705f2a2570d581273"}, + {file = "yarl-1.22.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:47fdb18187e2a4e18fda2c25c05d8251a9e4a521edaed757fef033e7d8498d9a"}, + {file = "yarl-1.22.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c7044802eec4524fde550afc28edda0dd5784c4c45f0be151a2d3ba017daca7d"}, + {file = "yarl-1.22.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:139718f35149ff544caba20fce6e8a2f71f1e39b92c700d8438a0b1d2a631a02"}, + {file = "yarl-1.22.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e1b51bebd221006d3d2f95fbe124b22b247136647ae5dcc8c7acafba66e5ee67"}, + {file = "yarl-1.22.0-cp313-cp313-win32.whl", hash = "sha256:d3e32536234a95f513bd374e93d717cf6b2231a791758de6c509e3653f234c95"}, + {file = "yarl-1.22.0-cp313-cp313-win_amd64.whl", hash = "sha256:47743b82b76d89a1d20b83e60d5c20314cbd5ba2befc9cda8f28300c4a08ed4d"}, + {file = "yarl-1.22.0-cp313-cp313-win_arm64.whl", hash = "sha256:5d0fcda9608875f7d052eff120c7a5da474a6796fe4d83e152e0e4d42f6d1a9b"}, + {file = "yarl-1.22.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:719ae08b6972befcba4310e49edb1161a88cdd331e3a694b84466bd938a6ab10"}, + {file = "yarl-1.22.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:47d8a5c446df1c4db9d21b49619ffdba90e77c89ec6e283f453856c74b50b9e3"}, + {file = "yarl-1.22.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:cfebc0ac8333520d2d0423cbbe43ae43c8838862ddb898f5ca68565e395516e9"}, + {file = "yarl-1.22.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4398557cbf484207df000309235979c79c4356518fd5c99158c7d38203c4da4f"}, + {file = "yarl-1.22.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2ca6fd72a8cd803be290d42f2dec5cdcd5299eeb93c2d929bf060ad9efaf5de0"}, + {file = "yarl-1.22.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ca1f59c4e1ab6e72f0a23c13fca5430f889634166be85dbf1013683e49e3278e"}, + {file = "yarl-1.22.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6c5010a52015e7c70f86eb967db0f37f3c8bd503a695a49f8d45700144667708"}, + {file = "yarl-1.22.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d7672ecf7557476642c88497c2f8d8542f8e36596e928e9bcba0e42e1e7d71f"}, + {file = "yarl-1.22.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:3b7c88eeef021579d600e50363e0b6ee4f7f6f728cd3486b9d0f3ee7b946398d"}, + {file = "yarl-1.22.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:f4afb5c34f2c6fecdcc182dfcfc6af6cccf1aa923eed4d6a12e9d96904e1a0d8"}, + {file = "yarl-1.22.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:59c189e3e99a59cf8d83cbb31d4db02d66cda5a1a4374e8a012b51255341abf5"}, + {file = "yarl-1.22.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:5a3bf7f62a289fa90f1990422dc8dff5a458469ea71d1624585ec3a4c8d6960f"}, + {file = "yarl-1.22.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:de6b9a04c606978fdfe72666fa216ffcf2d1a9f6a381058d4378f8d7b1e5de62"}, + {file = "yarl-1.22.0-cp313-cp313t-win32.whl", hash = "sha256:1834bb90991cc2999f10f97f5f01317f99b143284766d197e43cd5b45eb18d03"}, + {file = "yarl-1.22.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ff86011bd159a9d2dfc89c34cfd8aff12875980e3bd6a39ff097887520e60249"}, + {file = "yarl-1.22.0-cp313-cp313t-win_arm64.whl", hash = "sha256:7861058d0582b847bc4e3a4a4c46828a410bca738673f35a29ba3ca5db0b473b"}, + {file = "yarl-1.22.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:34b36c2c57124530884d89d50ed2c1478697ad7473efd59cfd479945c95650e4"}, + {file = "yarl-1.22.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:0dd9a702591ca2e543631c2a017e4a547e38a5c0f29eece37d9097e04a7ac683"}, + {file = "yarl-1.22.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:594fcab1032e2d2cc3321bb2e51271e7cd2b516c7d9aee780ece81b07ff8244b"}, + {file = "yarl-1.22.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f3d7a87a78d46a2e3d5b72587ac14b4c16952dd0887dbb051451eceac774411e"}, + {file = "yarl-1.22.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:852863707010316c973162e703bddabec35e8757e67fcb8ad58829de1ebc8590"}, + {file = "yarl-1.22.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:131a085a53bfe839a477c0845acf21efc77457ba2bcf5899618136d64f3303a2"}, + {file = "yarl-1.22.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:078a8aefd263f4d4f923a9677b942b445a2be970ca24548a8102689a3a8ab8da"}, + {file = "yarl-1.22.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bca03b91c323036913993ff5c738d0842fc9c60c4648e5c8d98331526df89784"}, + {file = "yarl-1.22.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:68986a61557d37bb90d3051a45b91fa3d5c516d177dfc6dd6f2f436a07ff2b6b"}, + {file = "yarl-1.22.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:4792b262d585ff0dff6bcb787f8492e40698443ec982a3568c2096433660c694"}, + {file = "yarl-1.22.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:ebd4549b108d732dba1d4ace67614b9545b21ece30937a63a65dd34efa19732d"}, + {file = "yarl-1.22.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f87ac53513d22240c7d59203f25cc3beac1e574c6cd681bbfd321987b69f95fd"}, + {file = "yarl-1.22.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:22b029f2881599e2f1b06f8f1db2ee63bd309e2293ba2d566e008ba12778b8da"}, + {file = "yarl-1.22.0-cp314-cp314-win32.whl", hash = "sha256:6a635ea45ba4ea8238463b4f7d0e721bad669f80878b7bfd1f89266e2ae63da2"}, + {file = "yarl-1.22.0-cp314-cp314-win_amd64.whl", hash = "sha256:0d6e6885777af0f110b0e5d7e5dda8b704efed3894da26220b7f3d887b839a79"}, + {file = "yarl-1.22.0-cp314-cp314-win_arm64.whl", hash = "sha256:8218f4e98d3c10d683584cb40f0424f4b9fd6e95610232dd75e13743b070ee33"}, + {file = "yarl-1.22.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:45c2842ff0e0d1b35a6bf1cd6c690939dacb617a70827f715232b2e0494d55d1"}, + {file = "yarl-1.22.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:d947071e6ebcf2e2bee8fce76e10faca8f7a14808ca36a910263acaacef08eca"}, + {file = "yarl-1.22.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:334b8721303e61b00019474cc103bdac3d7b1f65e91f0bfedeec2d56dfe74b53"}, + {file = "yarl-1.22.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e7ce67c34138a058fd092f67d07a72b8e31ff0c9236e751957465a24b28910c"}, + {file = "yarl-1.22.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d77e1b2c6d04711478cb1c4ab90db07f1609ccf06a287d5607fcd90dc9863acf"}, + {file = "yarl-1.22.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4647674b6150d2cae088fc07de2738a84b8bcedebef29802cf0b0a82ab6face"}, + {file = "yarl-1.22.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:efb07073be061c8f79d03d04139a80ba33cbd390ca8f0297aae9cce6411e4c6b"}, + {file = "yarl-1.22.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e51ac5435758ba97ad69617e13233da53908beccc6cfcd6c34bbed8dcbede486"}, + {file = "yarl-1.22.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:33e32a0dd0c8205efa8e83d04fc9f19313772b78522d1bdc7d9aed706bfd6138"}, + {file = "yarl-1.22.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:bf4a21e58b9cde0e401e683ebd00f6ed30a06d14e93f7c8fd059f8b6e8f87b6a"}, + {file = "yarl-1.22.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:e4b582bab49ac33c8deb97e058cd67c2c50dac0dd134874106d9c774fd272529"}, + {file = "yarl-1.22.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:0b5bcc1a9c4839e7e30b7b30dd47fe5e7e44fb7054ec29b5bb8d526aa1041093"}, + {file = "yarl-1.22.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:c0232bce2170103ec23c454e54a57008a9a72b5d1c3105dc2496750da8cfa47c"}, + {file = "yarl-1.22.0-cp314-cp314t-win32.whl", hash = "sha256:8009b3173bcd637be650922ac455946197d858b3630b6d8787aa9e5c4564533e"}, + {file = "yarl-1.22.0-cp314-cp314t-win_amd64.whl", hash = "sha256:9fb17ea16e972c63d25d4a97f016d235c78dd2344820eb35bc034bc32012ee27"}, + {file = "yarl-1.22.0-cp314-cp314t-win_arm64.whl", hash = "sha256:9f6d73c1436b934e3f01df1e1b21ff765cd1d28c77dfb9ace207f746d4610ee1"}, + {file = "yarl-1.22.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3aa27acb6de7a23785d81557577491f6c38a5209a254d1191519d07d8fe51748"}, + {file = "yarl-1.22.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:af74f05666a5e531289cb1cc9c883d1de2088b8e5b4de48004e5ca8a830ac859"}, + {file = "yarl-1.22.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:62441e55958977b8167b2709c164c91a6363e25da322d87ae6dd9c6019ceecf9"}, + {file = "yarl-1.22.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b580e71cac3f8113d3135888770903eaf2f507e9421e5697d6ee6d8cd1c7f054"}, + {file = "yarl-1.22.0-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e81fda2fb4a07eda1a2252b216aa0df23ebcd4d584894e9612e80999a78fd95b"}, + {file = "yarl-1.22.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:99b6fc1d55782461b78221e95fc357b47ad98b041e8e20f47c1411d0aacddc60"}, + {file = "yarl-1.22.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:088e4e08f033db4be2ccd1f34cf29fe994772fb54cfe004bbf54db320af56890"}, + {file = "yarl-1.22.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e4e1f6f0b4da23e61188676e3ed027ef0baa833a2e633c29ff8530800edccba"}, + {file = "yarl-1.22.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:84fc3ec96fce86ce5aa305eb4aa9358279d1aa644b71fab7b8ed33fe3ba1a7ca"}, + {file = "yarl-1.22.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:5dbeefd6ca588b33576a01b0ad58aa934bc1b41ef89dee505bf2932b22ddffba"}, + {file = "yarl-1.22.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:14291620375b1060613f4aab9ebf21850058b6b1b438f386cc814813d901c60b"}, + {file = "yarl-1.22.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:a4fcfc8eb2c34148c118dfa02e6427ca278bfd0f3df7c5f99e33d2c0e81eae3e"}, + {file = "yarl-1.22.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:029866bde8d7b0878b9c160e72305bbf0a7342bcd20b9999381704ae03308dc8"}, + {file = "yarl-1.22.0-cp39-cp39-win32.whl", hash = "sha256:4dcc74149ccc8bba31ce1944acee24813e93cfdee2acda3c172df844948ddf7b"}, + {file = "yarl-1.22.0-cp39-cp39-win_amd64.whl", hash = "sha256:10619d9fdee46d20edc49d3479e2f8269d0779f1b031e6f7c2aa1c76be04b7ed"}, + {file = "yarl-1.22.0-cp39-cp39-win_arm64.whl", hash = "sha256:dd7afd3f8b0bfb4e0d9fc3c31bfe8a4ec7debe124cfd90619305def3c8ca8cd2"}, + {file = "yarl-1.22.0-py3-none-any.whl", hash = "sha256:1380560bdba02b6b6c90de54133c81c9f2a453dee9912fe58c1dcced1edb7cff"}, + {file = "yarl-1.22.0.tar.gz", hash = "sha256:bebf8557577d4401ba8bd9ff33906f1376c877aa78d1fe216ad01b4d6745af71"}, +] + +[package.dependencies] +idna = ">=2.0" +multidict = ">=4.0" +propcache = ">=0.2.1" + +[metadata] +lock-version = "2.1" +python-versions = ">=3.12,<4.0" +content-hash = "fbc7ba4fa5166b82cf3ede942b9a1a0fd4b5d44ef2d71385c785c935269616dd" diff --git a/projects/triage/poetry.toml b/libs/file_linking/poetry.toml similarity index 100% rename from projects/triage/poetry.toml rename to libs/file_linking/poetry.toml diff --git a/libs/file_linking/pyproject.toml b/libs/file_linking/pyproject.toml new file mode 100644 index 0000000..f4fcea7 --- /dev/null +++ b/libs/file_linking/pyproject.toml @@ -0,0 +1,25 @@ +[tool.poetry] +name = "file_linking" +version = "0.1.0" +description = "Modules Nemesis uses to handle file links and listings" +authors = ["SpecterOps"] +readme = "README.md" + +[tool.poetry.dependencies] +python = ">=3.12,<4.0" +psycopg = {extras = ["binary"], version = ">=3.0.0,<4.0.0"} +asyncpg = "^0.30.0" +dapr = "1.16.0" +structlog = ">=20.0.0,<30.0.0" +common = { path = "../common", develop = true } +pyyaml = "^6.0.3" +pytest-asyncio = "^1.2.0" + +[tool.poetry.group.dev.dependencies] +ruff = "^0.9.2" +pytest = "^8.4.2" +pytest-asyncio = "^1.2.0" + +[build-system] +requires = ["poetry-core>=2.0.0,<3.0.0"] +build-backend = "poetry.core.masonry.api" diff --git a/libs/file_linking/tests/__init__.py b/libs/file_linking/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/libs/file_linking/tests/test_placeholder_resolver.py b/libs/file_linking/tests/test_placeholder_resolver.py new file mode 100644 index 0000000..f5dcd61 --- /dev/null +++ b/libs/file_linking/tests/test_placeholder_resolver.py @@ -0,0 +1,398 @@ +"""Tests for the placeholder resolver.""" + +import re +from unittest.mock import AsyncMock, MagicMock + +import pytest +from file_linking.placeholder_resolver import ( + PLACEHOLDERS, + PlaceholderDefinition, + PlaceholderResolver, +) + + +class TestPlaceholderDefinition: + """Tests for PlaceholderDefinition dataclass.""" + + def test_placeholder_definition_creation(self): + """Test creating a PlaceholderDefinition.""" + placeholder = PlaceholderDefinition(name="", pattern=r"([a-z]+)", description="Test placeholder") + + assert placeholder.name == "" + assert placeholder.pattern == r"([a-z]+)" + assert placeholder.description == "Test placeholder" + + +class TestPlaceholdersRegistry: + """Tests for the PLACEHOLDERS registry.""" + + def test_placeholders_registry_exists(self): + """Test that PLACEHOLDERS registry is defined.""" + assert PLACEHOLDERS is not None + assert isinstance(PLACEHOLDERS, list) + assert len(PLACEHOLDERS) >= 2 # At least USERNAME and SID + + def test_placeholders_have_required_fields(self): + """Test that all placeholders have required fields.""" + for placeholder in PLACEHOLDERS: + assert placeholder.name + assert placeholder.pattern + assert placeholder.description + assert placeholder.name.startswith("<") + assert placeholder.name.endswith(">") + + def test_windows_username_placeholder(self): + """Test that WINDOWS_USERNAME placeholder is defined correctly.""" + username_placeholder = next((p for p in PLACEHOLDERS if "USERNAME" in p.name), None) + assert username_placeholder is not None + assert username_placeholder.name == "" + # Test pattern matches valid usernames + pattern = re.compile(username_placeholder.pattern) + assert pattern.match("john.doe") + assert pattern.match("administrator") + assert pattern.match("user123") + + def test_windows_sid_placeholder(self): + """Test that WINDOWS_SECURITY_IDENTIFIER placeholder is defined correctly.""" + sid_placeholder = next((p for p in PLACEHOLDERS if "SECURITY_IDENTIFIER" in p.name), None) + assert sid_placeholder is not None + assert sid_placeholder.name == "" + # Test pattern matches valid SIDs + pattern = re.compile(sid_placeholder.pattern) + assert pattern.match("S-1-5-21-1234567890-1234567890-1234567890-1000") + assert pattern.match("S-1-5-18") # SYSTEM + assert pattern.match("S-1-5-19") # LOCAL SERVICE + assert pattern.match("S-1-5-20") # NETWORK SERVICE + + def test_windows_machine_guid_placeholder(self): + """Test that WINDOWS_MACHINE_GUID placeholder is defined correctly.""" + uuid_placeholder = next((p for p in PLACEHOLDERS if "WINDOWS_MACHINE_GUID" in p.name), None) + assert uuid_placeholder is not None + assert uuid_placeholder.name == "" + # Test pattern matches valid UUIDs + pattern = re.compile(uuid_placeholder.pattern) + assert pattern.match("f26c165b-53c8-414e-8abb-ec5f0f52df22") + assert pattern.match("550e8400-e29b-41d4-a716-446655440000") + assert pattern.match("ABCDEF12-3456-7890-ABCD-EF1234567890") # Mixed case + # Test pattern rejects invalid formats + assert not pattern.match("invalid-uuid") + assert not pattern.match("f26c165b53c8414e8abbec5f0f52df22") # No hyphens + + +class TestConvertPlaceholderToRegex: + """Tests for _convert_placeholder_to_regex method.""" + + def setup_method(self): + """Setup test fixtures.""" + self.db_service = MagicMock() + self.resolver = PlaceholderResolver(self.db_service) + + def test_convert_single_placeholder(self): + """Test converting a template with a single placeholder.""" + template = "/C:/Users//AppData/file.txt" + pattern = self.resolver._convert_placeholder_to_regex(template) + + assert pattern is not None + assert pattern.match("/C:/Users/john.doe/AppData/file.txt") + assert pattern.match("/C:/Users/administrator/AppData/file.txt") + assert not pattern.match("/C:/Users/AppData/file.txt") # Missing username + + def test_convert_multiple_placeholders(self): + """Test converting a template with multiple placeholders.""" + template = "/C:/Users//AppData/Roaming/Microsoft/Protect//abc123" + pattern = self.resolver._convert_placeholder_to_regex(template) + + assert pattern is not None + assert pattern.match( + "/C:/Users/john.doe/AppData/Roaming/Microsoft/Protect/S-1-5-21-1234567890-1234567890-1234567890-1000/abc123" + ) + + def test_convert_case_insensitive(self): + """Test that pattern matching is case-insensitive.""" + template = "/C:/Users//AppData/file.txt" + pattern = self.resolver._convert_placeholder_to_regex(template) + + assert pattern is not None + # Test different case variations + assert pattern.match("/C:/Users/john.doe/AppData/file.txt") + assert pattern.match("/c:/users/john.doe/appdata/file.txt") + assert pattern.match("/C:/USERS/JOHN.DOE/APPDATA/FILE.TXT") + + def test_convert_escape_special_chars(self): + """Test that regex special characters are properly escaped.""" + template = "/C:/Users//AppData/Local/file.txt" + pattern = self.resolver._convert_placeholder_to_regex(template) + + assert pattern is not None + # Periods should be escaped + assert pattern.match("/C:/Users/john.doe/AppData/Local/file.txt") + # Should not match without proper extension due to escaped period + assert not pattern.match("/C:/Users/john.doe/AppData/Local/fileXtxt") + + def test_convert_no_placeholders(self): + """Test converting a template with no placeholders returns None.""" + template = "/C:/Users/john.doe/AppData/file.txt" + pattern = self.resolver._convert_placeholder_to_regex(template) + + assert pattern is None + + def test_convert_empty_string(self): + """Test converting an empty string returns None.""" + pattern = self.resolver._convert_placeholder_to_regex("") + + assert pattern is None + + +class TestReplacePlaceholdersWithCaptures: + """Tests for _replace_placeholders_with_captures method.""" + + def setup_method(self): + """Setup test fixtures.""" + self.db_service = MagicMock() + self.resolver = PlaceholderResolver(self.db_service) + + def test_replace_username_placeholder(self): + """Test replacing USERNAME placeholder with captured value.""" + template = "/C:/Users//AppData/file.txt" + pattern = self.resolver._convert_placeholder_to_regex(template) + assert pattern is not None + match = pattern.match("/C:/Users/john.doe/AppData/file.txt") + assert match is not None + + result = self.resolver._replace_placeholders_with_captures(template, match) + + assert result == "/C:/Users/john.doe/AppData/file.txt" + + def test_replace_sid_placeholder(self): + """Test replacing SID placeholder with captured value.""" + template = "/C:/Windows/System32/Microsoft/Protect//abc123" + pattern = self.resolver._convert_placeholder_to_regex(template) + assert pattern is not None + match = pattern.match("/C:/Windows/System32/Microsoft/Protect/S-1-5-18/abc123") + assert match is not None + + result = self.resolver._replace_placeholders_with_captures(template, match) + + assert result == "/C:/Windows/System32/Microsoft/Protect/S-1-5-18/abc123" + + def test_replace_multiple_placeholders(self): + """Test replacing multiple placeholders in the same path.""" + template = "/C:/Users//AppData/Roaming/Microsoft/Protect//abc123" + pattern = self.resolver._convert_placeholder_to_regex(template) + assert pattern is not None + match = pattern.match( + "/C:/Users/john.doe/AppData/Roaming/Microsoft/Protect/S-1-5-21-1234567890-1234567890-1234567890-1000/abc123" + ) + assert match is not None + + result = self.resolver._replace_placeholders_with_captures(template, match) + + expected = ( + "/C:/Users/john.doe/AppData/Roaming/Microsoft/Protect/S-1-5-21-1234567890-1234567890-1234567890-1000/abc123" + ) + assert result == expected + + + +@pytest.mark.asyncio +class TestTryResolvePlaceholderPath: + """Tests for try_resolve_placeholder_path method (backward resolution).""" + + def setup_method(self): + """Setup test fixtures.""" + self.db_service = MagicMock() + # Methods used by PlaceholderResolver + self.db_service.get_placeholder_entries = AsyncMock() + self.db_service.get_collected_files = AsyncMock() + self.db_service.update_file_listing_path = AsyncMock(return_value=True) + self.db_service.update_file_linking_path = AsyncMock(return_value=True) + # Additional methods for completeness (not currently used by PlaceholderResolver) + self.db_service.add_file_listing = AsyncMock(return_value=True) + self.db_service.add_file_linking = AsyncMock(return_value=True) + self.resolver = PlaceholderResolver(self.db_service) + + async def test_resolve_backward_full_path(self): + """Test backward resolution with full path.""" + # Setup: Real file already exists in DB + self.db_service.get_collected_files.return_value = [ + "/C:/Users/john.doe/AppData/Roaming/file.txt", + ] + + # Placeholder path is being created + placeholder_path = "/C:/Users//AppData/Roaming/file.txt" + source = "test-source" + + result = await self.resolver.try_resolve_placeholder_path(source, placeholder_path) + + assert result == "/C:/Users/john.doe/AppData/Roaming/file.txt" + + async def test_resolve_backward_bare_filename(self): + """Test backward resolution with bare filename.""" + # Setup: Real bare filename already exists + self.db_service.get_collected_files.return_value = ["Local State"] + + # Placeholder path being created + placeholder_path = "/C:/Users//AppData/Local/Google/Chrome/User Data/Local State" + source = "test-source" + + result = await self.resolver.try_resolve_placeholder_path(source, placeholder_path) + + # Cannot match bare filename against full placeholder path + assert result is None + + async def test_resolve_backward_no_match(self): + """Test backward resolution with no matching file.""" + self.db_service.get_collected_files.return_value = [ + "/C:/Users/jane.doe/different/path.txt", + ] + + placeholder_path = "/C:/Users//AppData/file.txt" + source = "test-source" + + result = await self.resolver.try_resolve_placeholder_path(source, placeholder_path) + + assert result is None + + async def test_resolve_backward_no_collected_files(self): + """Test backward resolution when no collected files exist.""" + self.db_service.get_collected_files.return_value = [] + + placeholder_path = "/C:/Users//AppData/file.txt" + source = "test-source" + + result = await self.resolver.try_resolve_placeholder_path(source, placeholder_path) + + assert result is None + + async def test_resolve_backward_no_placeholders(self): + """Test backward resolution with path containing no placeholders.""" + placeholder_path = "/C:/Users/john.doe/AppData/file.txt" + source = "test-source" + + result = await self.resolver.try_resolve_placeholder_path(source, placeholder_path) + + assert result is None + self.db_service.get_collected_files.assert_not_called() + + +@pytest.mark.asyncio +class TestPlaceholderResolutionScenarios: + """Integration-style tests for complete placeholder resolution scenarios.""" + + def setup_method(self): + """Setup test fixtures.""" + self.db_service = MagicMock() + # Methods used by PlaceholderResolver + self.db_service.get_placeholder_entries = AsyncMock() + self.db_service.get_collected_files = AsyncMock() + self.db_service.update_file_listing_path = AsyncMock(return_value=True) + self.db_service.update_file_linking_path = AsyncMock(return_value=True) + # Additional methods for completeness (not currently used by PlaceholderResolver) + self.db_service.add_file_listing = AsyncMock(return_value=True) + self.db_service.add_file_linking = AsyncMock(return_value=True) + self.resolver = PlaceholderResolver(self.db_service) + + async def test_chromium_masterkey_resolution(self): + """Test resolution of Chromium masterkey placeholder.""" + # Scenario: Local State creates placeholder for masterkey, + # then real masterkey file arrives + placeholder_path = ( + "/C:/Users//AppData/Roaming/Microsoft/Protect/" + "/abc-123-def-456" + ) + + self.db_service.get_placeholder_entries.return_value = [ + { + "table_name": "file_listings", + "path": placeholder_path, + } + ] + + # Real masterkey arrives + real_path = ( + "/C:/Users/john.doe/AppData/Roaming/Microsoft/Protect/" + "S-1-5-21-1234567890-1234567890-1234567890-1000/abc-123-def-456" + ) + source = "test-agent" + + count = await self.resolver.resolve_placeholders_for_file(real_path, source) + + assert count == 1 + call_args = self.db_service.update_file_listing_path.call_args + assert call_args[0][0] == source + assert call_args[0][1] == placeholder_path + assert call_args[0][2] == real_path + + async def test_case_insensitive_resolution(self): + """Test that resolution works with different case variations.""" + placeholder_path = "/C:/Users//AppData/file.txt" + self.db_service.get_placeholder_entries.return_value = [ + {"table_name": "file_listings", "path": placeholder_path} + ] + + # Real file with different case + real_path = "/c:/users/john.doe/appdata/file.txt" + source = "test-source" + + count = await self.resolver.resolve_placeholders_for_file(real_path, source) + + assert count == 1 + + async def test_extensibility_new_placeholder(self): + """Test that adding a new placeholder to registry works.""" + # This test verifies the extensibility claim - just check that we can + # read the PLACEHOLDERS registry and it's used + assert len(PLACEHOLDERS) >= 2 + + # Verify the registry is used + placeholder_names = [p.name for p in PLACEHOLDERS] + assert "" in placeholder_names + assert "" in placeholder_names + + async def test_cng_system_private_key_forward_resolution(self): + """Test forward resolution of CNG system private key path with UUID placeholder.""" + # Scenario: Chrome Local State creates placeholder for CNG system private key, + # then real key file arrives (forward propagation) + placeholder_path = ( + "/C:/ProgramData/Microsoft/Crypto/SystemKeys/7096db7aeb75c0d3497ecd56d355a695_" + ) + + self.db_service.get_placeholder_entries.return_value = [ + { + "table_name": "file_linkings", + "path": placeholder_path, + } + ] + + # Real CNG system private key file arrives + real_path = "/C:/ProgramData/Microsoft/Crypto/SystemKeys/7096db7aeb75c0d3497ecd56d355a695_f26c165b-53c8-414e-8abb-ec5f0f52df22" + source = "test-agent" + + count = await self.resolver.resolve_placeholders_for_file(real_path, source) + + assert count == 1 + call_args = self.db_service.update_file_linking_path.call_args + assert call_args[0][0] == source + assert call_args[0][1] == placeholder_path + assert call_args[0][2] == real_path + + async def test_cng_system_private_key_backward_resolution(self): + """Test backward resolution of CNG system private key path with UUID placeholder.""" + # Scenario: Real key file already exists in DB, then Chrome Local State + # tries to create placeholder entry (backward propagation) + + # Real CNG system private key file already collected + real_path = "/C:/ProgramData/Microsoft/Crypto/SystemKeys/7096db7aeb75c0d3497ecd56d355a695_f26c165b-53c8-414e-8abb-ec5f0f52df22" + self.db_service.get_collected_files.return_value = [real_path] + + # Placeholder path being created + placeholder_path = ( + "/C:/ProgramData/Microsoft/Crypto/SystemKeys/7096db7aeb75c0d3497ecd56d355a695_" + ) + source = "test-agent" + + result = await self.resolver.try_resolve_placeholder_path(source, placeholder_path) + + # Should return the real path instead of None + assert result == real_path + self.db_service.get_collected_files.assert_called_once_with(source) diff --git a/libs/file_linking/tests/test_rules_engine.py b/libs/file_linking/tests/test_rules_engine.py new file mode 100644 index 0000000..582460b --- /dev/null +++ b/libs/file_linking/tests/test_rules_engine.py @@ -0,0 +1,804 @@ +"""Tests for the file linking rules engine.""" + +from datetime import datetime, timedelta +from unittest.mock import AsyncMock, MagicMock + +import pytest +from common.models import FileEnriched, FileHashes +from file_linking.rules_engine import FileLinkingEngine, Trigger + + +@pytest.fixture +def mock_asyncpg_pool(): + """Create a mock asyncpg.Pool for testing.""" + pool = MagicMock() + pool.acquire = MagicMock() + pool.acquire.return_value.__aenter__ = AsyncMock() + pool.acquire.return_value.__aexit__ = AsyncMock() + return pool + + +def create_file_enriched(object_id: str, path: str, mime_type: str, magic_type: str) -> FileEnriched: + """Helper function to create a FileEnriched instance with all required fields.""" + return FileEnriched( + object_id=object_id, + agent_id="test-agent-id", + project="test-project", + timestamp=datetime.now(), + expiration=datetime.now() + timedelta(days=30), + path=path, + file_name=path.split("/")[-1], + size=1024, + hashes=FileHashes( + md5="d41d8cd98f00b204e9800998ecf8427e", + sha1="da39a3ee5e6b4b0d3255bfef95601890afd80709", + sha256="e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + ), + magic_type=magic_type, + mime_type=mime_type, + is_plaintext=False, + is_container=False, + ) + + +class TestMatchesTrigger: + """Tests for the _matches_trigger method.""" + + @pytest.fixture + def engine(self, tmp_path, mock_asyncpg_pool): + """Create a FileLinkingEngine instance with a temporary rules directory.""" + rules_dir = tmp_path / "rules" + rules_dir.mkdir() + # Using a mock asyncpg pool since we're only testing _matches_trigger + return FileLinkingEngine(connection_pool=mock_asyncpg_pool, rules_dir=str(rules_dir)) + + def test_matches_trigger_chromium_cookies(self, engine): + """Test matching Chromium cookies with various trigger conditions.""" + cookies_path = "/C:/Users/Alice/AppData/Local/Google/Chrome/User Data/Default/Network/Cookies" + sqlite_mime = "application/vnd.sqlite3; charset=binary" + sqlite_magic = "SQLite 3.x database" + + # Test 1: File pattern only - should match + trigger_file_only = Trigger( + file_patterns=["**/User Data/Default/Network/Cookies"], + mime_patterns=[], + magic_patterns=[], + ) + file_cookies = create_file_enriched( + object_id="test-cookies", + path=cookies_path, + mime_type=sqlite_mime, + magic_type=sqlite_magic, + ) + assert engine._matches_trigger(file_cookies, trigger_file_only) is True + + # Test 2: File pattern no match - should not match: History file is not the Cookies file + file_history = create_file_enriched( + object_id="test-history", + path="C:/Users/Alice/AppData/Local/Google/Chrome/User Data/Default/History", + mime_type=sqlite_mime, + magic_type=sqlite_magic, + ) + assert engine._matches_trigger(file_history, trigger_file_only) is False + + # Test 3: File pattern + MIME type - should match + trigger_with_mime = Trigger( + file_patterns=["**/User Data/Default/Network/Cookies"], + mime_patterns=[sqlite_mime], + magic_patterns=[], + ) + assert engine._matches_trigger(file_cookies, trigger_with_mime) is True + + # Test 4: File pattern matches but MIME type doesn't - should not match + file_wrong_mime = create_file_enriched( + object_id="test-wrong-mime", + path=cookies_path, + mime_type="text/plain", + magic_type="ASCII text", + ) + assert engine._matches_trigger(file_wrong_mime, trigger_with_mime) is False + + # Test 5: File pattern + magic pattern - should match + trigger_with_magic = Trigger( + file_patterns=["**/User Data/Default/Network/Cookies"], + mime_patterns=[], + magic_patterns=["SQLite"], + ) + assert engine._matches_trigger(file_cookies, trigger_with_magic) is True + + # Test 6: File pattern matches but magic pattern doesn't - should not match + assert engine._matches_trigger(file_wrong_mime, trigger_with_magic) is False + + # Test 7: All conditions (file + MIME + magic) - should match + trigger_all = Trigger( + file_patterns=["**/User Data/Default/Network/Cookies"], + mime_patterns=[sqlite_mime], + magic_patterns=["SQLite"], + ) + assert engine._matches_trigger(file_cookies, trigger_all) is True + + def test_matches_trigger_multiple_file_patterns(self, engine): + """Test matching with multiple file patterns.""" + trigger = Trigger( + file_patterns=[ + "**/User Data/Default/Network/Cookies", + "**/User Data/Profile */Network/Cookies", + ], + mime_patterns=[], + magic_patterns=[], + ) + + # First pattern should match + file_enriched_1 = create_file_enriched( + object_id="test-object-id-1", + path="C:/Users/Alice/AppData/Local/Google/Chrome/User Data/Default/Network/Cookies", + mime_type="application/vnd.sqlite3; charset=binary", + magic_type="SQLite 3.x database", + ) + assert engine._matches_trigger(file_enriched_1, trigger) is True + + # Second pattern should match + file_enriched_2 = create_file_enriched( + object_id="test-object-id-2", + path="C:/Users/Alice/AppData/Local/Google/Chrome/User Data/Profile 1/Network/Cookies", + mime_type="application/vnd.sqlite3; charset=binary", + magic_type="SQLite 3.x database", + ) + assert engine._matches_trigger(file_enriched_2, trigger) is True + + def test_matches_trigger_multiple_mime_patterns(self, engine): + """Test matching with multiple MIME patterns.""" + trigger = Trigger( + file_patterns=["**/*.db"], + mime_patterns=[ + "application/vnd.sqlite3; charset=binary", + "application/x-sqlite3", + ], + magic_patterns=[], + ) + + # First MIME type should match + file_enriched_1 = create_file_enriched( + object_id="test-object-id-1", + path="/home/user/data/app.db", + mime_type="application/vnd.sqlite3; charset=binary", + magic_type="SQLite 3.x database", + ) + assert engine._matches_trigger(file_enriched_1, trigger) is True + + # Second MIME type should match + file_enriched_2 = create_file_enriched( + object_id="test-object-id-2", + path="/home/user/data/app.db", + mime_type="application/x-sqlite3", + magic_type="SQLite 3.x database", + ) + assert engine._matches_trigger(file_enriched_2, trigger) is True + + def test_matches_trigger_empty_trigger_lists(self, engine): + """Test behavior with empty trigger lists (should match any file).""" + trigger = Trigger( + file_patterns=[], + mime_patterns=[], + magic_patterns=[], + ) + + file_enriched = create_file_enriched( + object_id="test-object-id", + path="/any/path/file.txt", + mime_type="text/plain", + magic_type="ASCII text", + ) + + # Empty file_patterns should match any file + assert engine._matches_trigger(file_enriched, trigger) is True + + def test_matches_trigger_case_sensitive_pattern(self, engine): + """Test that file pattern matching is case-sensitive (via fnmatch).""" + trigger = Trigger( + file_patterns=["**/Cookies"], + mime_patterns=[], + magic_patterns=[], + ) + + # Exact case match + file_enriched_match = create_file_enriched( + object_id="test-object-id-1", + path="/path/to/Cookies", + mime_type="application/octet-stream", + magic_type="data", + ) + assert engine._matches_trigger(file_enriched_match, trigger) is True + + # Different case + file_enriched_no_match = create_file_enriched( + object_id="test-object-id-2", + path="/path/to/cookies", + mime_type="application/octet-stream", + magic_type="data", + ) + assert engine._matches_trigger(file_enriched_no_match, trigger) is False + + def test_matches_trigger_posix_path_pattern(self, engine): + """Test matching with POSIX-style paths.""" + trigger = Trigger( + file_patterns=["**/config/*.json"], + mime_patterns=[], + magic_patterns=[], + ) + + file_enriched = create_file_enriched( + object_id="test-object-id", + path="/home/user/.config/app/config/settings.json", + mime_type="application/json", + magic_type="JSON data", + ) + + assert engine._matches_trigger(file_enriched, trigger) is True + + +class TestChromiumCookiesLinking: + """Tests for Chromium cookies linking to Local State file.""" + + @pytest.fixture + def engine(self, tmp_path, mock_asyncpg_pool): + """Create a FileLinkingEngine with the actual chromium cookies rule.""" + # Use the real rules directory to load the cookies.yaml rule + import os + + rules_dir = os.path.join(os.path.dirname(os.path.dirname(__file__)), "file_linking", "rules") + return FileLinkingEngine(connection_pool=mock_asyncpg_pool, rules_dir=rules_dir) + + def test_chromium_cookies_links_to_local_state_windows(self, engine): + """Test that Chromium Cookies file creates a link to Local State on Windows paths.""" + cookies_path = "C:/Users/Alice/AppData/Local/Google/Chrome/User Data/Default/Network/Cookies" + expected_local_state_path = "C:/Users/Alice/AppData/Local/Google/Chrome/User Data/Local State" + + file_cookies = create_file_enriched( + object_id="test-cookies-001", + path=cookies_path, + mime_type="application/vnd.sqlite3; charset=binary", + magic_type="SQLite 3.x database", + ) + + # Find the chromium_cookies rule + rule = next((r for r in engine.rules if r.name == "chromium_cookies"), None) + assert rule is not None, "chromium_cookies rule should be loaded" + assert rule.enabled is True, "chromium_cookies rule should be enabled" + + # Verify the rule triggers + trigger_matched = False + for trigger in rule.triggers: + if engine._matches_trigger(file_cookies, trigger): + trigger_matched = True + break + assert trigger_matched is True, "Cookies file should match the rule trigger" + + # Verify the linked file configuration + assert len(rule.linked_files) == 1, "Should have exactly one linked file" + linked_file = rule.linked_files[0] + assert linked_file.name == "local_state", "Linked file should be named 'local_state'" + assert linked_file.priority == "high", "Priority should be high" + assert "master key" in linked_file.collection_reason.lower(), "Should mention master key in reason" + + # Verify path template expansion + assert len(linked_file.path_templates) == 1, "Should have exactly one path template" + template = linked_file.path_templates[0] + expanded_path = engine._expand_path_template(template, cookies_path) + assert expanded_path == expected_local_state_path, f"Expected {expected_local_state_path}, got {expanded_path}" + + def test_chromium_cookies_wrong_mime_type_no_match(self, engine): + """Test that a file with the right path but wrong MIME type doesn't trigger the rule.""" + cookies_path = "C:/Users/Alice/AppData/Local/Google/Chrome/User Data/Default/Network/Cookies" + + file_wrong_mime = create_file_enriched( + object_id="test-cookies-004", + path=cookies_path, + mime_type="text/plain", # Wrong MIME type + magic_type="ASCII text", + ) + + # Find the chromium_cookies rule + rule = next((r for r in engine.rules if r.name == "chromium_cookies"), None) + assert rule is not None, "chromium_cookies rule should be loaded" + + # Verify the rule does NOT trigger + trigger_matched = False + for trigger in rule.triggers: + if engine._matches_trigger(file_wrong_mime, trigger): + trigger_matched = True + break + assert trigger_matched is False, "File with wrong MIME type should not match" + + def test_chromium_cookies_wrong_path_no_match(self, engine): + """Test that a SQLite file with the wrong path doesn't trigger the rule.""" + history_path = "C:/Users/Alice/AppData/Local/Google/Chrome/User Data/Default/History" + + file_history = create_file_enriched( + object_id="test-history-001", + path=history_path, + mime_type="application/vnd.sqlite3; charset=binary", + magic_type="SQLite 3.x database", + ) + + # Find the chromium_cookies rule + rule = next((r for r in engine.rules if r.name == "chromium_cookies"), None) + assert rule is not None, "chromium_cookies rule should be loaded" + + # Verify the rule does NOT trigger + trigger_matched = False + for trigger in rule.triggers: + if engine._matches_trigger(file_history, trigger): + trigger_matched = True + break + assert trigger_matched is False, "History file should not match cookies rule" + + +class TestChromiumLocalStateLinking: + """Tests for Chromium Local State linking to Login Data and Cookies files.""" + + @pytest.fixture + def engine(self, tmp_path, mock_asyncpg_pool): + """Create a FileLinkingEngine with the actual chromium local_state rule.""" + import os + + rules_dir = os.path.join(os.path.dirname(os.path.dirname(__file__)), "file_linking", "rules") + return FileLinkingEngine(connection_pool=mock_asyncpg_pool, rules_dir=rules_dir) + + def test_chromium_local_state_links_to_login_data_and_cookies_windows(self, engine): + """Test that Chromium Local State file creates links to Login Data and Cookies on Windows paths.""" + local_state_path = "C:/Users/Alice/AppData/Local/Google/Chrome/User Data/Local State" + expected_login_data_path = "C:/Users/Alice/AppData/Local/Google/Chrome/User Data/Default/Login Data" + expected_cookies_path = "C:/Users/Alice/AppData/Local/Google/Chrome/User Data/Default/Network/Cookies" + + file_local_state = create_file_enriched( + object_id="test-local-state-001", + path=local_state_path, + mime_type="application/json", + magic_type="JSON data", + ) + + # Find the chromium_local_state rule + rule = next((r for r in engine.rules if r.name == "chromium_local_state"), None) + assert rule is not None, "chromium_local_state rule should be loaded" + assert rule.enabled is True, "chromium_local_state rule should be enabled" + + # Verify the rule triggers + trigger_matched = False + for trigger in rule.triggers: + if engine._matches_trigger(file_local_state, trigger): + trigger_matched = True + break + assert trigger_matched is True, "Local State file should match the rule trigger" + + # Verify the linked files configuration + assert len(rule.linked_files) == 2, "Should have exactly two linked files" + + # Check login_data linked file + login_data_file = next((lf for lf in rule.linked_files if lf.name == "login_data"), None) + assert login_data_file is not None, "Should have login_data linked file" + assert login_data_file.priority == "high", "login_data priority should be high" + assert "login credentials" in login_data_file.collection_reason.lower(), "Should mention login credentials" + + # Check cookies linked file + cookies_file = next((lf for lf in rule.linked_files if lf.name == "cookies"), None) + assert cookies_file is not None, "Should have cookies linked file" + assert cookies_file.priority == "high", "cookies priority should be high" + assert "cookie" in cookies_file.collection_reason.lower(), "Should mention cookies" + + # Verify path template expansion for login_data + assert len(login_data_file.path_templates) == 1, "login_data should have exactly one path template" + login_template = login_data_file.path_templates[0] + expanded_login_path = engine._expand_path_template(login_template, local_state_path) + assert expanded_login_path == expected_login_data_path, f"Expected {expected_login_data_path}, got {expanded_login_path}" + + # Verify path template expansion for cookies + assert len(cookies_file.path_templates) == 1, "cookies should have exactly one path template" + cookies_template = cookies_file.path_templates[0] + expanded_cookies_path = engine._expand_path_template(cookies_template, local_state_path) + assert expanded_cookies_path == expected_cookies_path, f"Expected {expected_cookies_path}, got {expanded_cookies_path}" + + def test_chromium_local_state_links_opera_browser(self, engine): + """Test that Opera browser Local State file creates correct links.""" + local_state_path = "C:/Users/Bob/AppData/Roaming/Opera Software/Opera Stable/Local State" + expected_login_data_path = "C:/Users/Bob/AppData/Roaming/Opera Software/Opera Stable/Default/Login Data" + expected_cookies_path = "C:/Users/Bob/AppData/Roaming/Opera Software/Opera Stable/Default/Network/Cookies" + + file_local_state = create_file_enriched( + object_id="test-local-state-opera-001", + path=local_state_path, + mime_type="application/json", + magic_type="JSON data", + ) + + # Find the chromium_local_state rule + rule = next((r for r in engine.rules if r.name == "chromium_local_state"), None) + assert rule is not None, "chromium_local_state rule should be loaded" + + # Verify the rule triggers for Opera paths + trigger_matched = False + for trigger in rule.triggers: + if engine._matches_trigger(file_local_state, trigger): + trigger_matched = True + break + assert trigger_matched is True, "Opera Local State file should match the rule trigger" + + # Verify path template expansion + login_data_file = next((lf for lf in rule.linked_files if lf.name == "login_data"), None) + cookies_file = next((lf for lf in rule.linked_files if lf.name == "cookies"), None) + + expanded_login_path = engine._expand_path_template(login_data_file.path_templates[0], local_state_path) + expanded_cookies_path = engine._expand_path_template(cookies_file.path_templates[0], local_state_path) + + assert expanded_login_path == expected_login_data_path, f"Expected {expected_login_data_path}, got {expanded_login_path}" + assert expanded_cookies_path == expected_cookies_path, f"Expected {expected_cookies_path}, got {expanded_cookies_path}" + + def test_chromium_local_state_links_posix_paths(self, engine): + """Test that Local State file works with POSIX-style paths (e.g., from Linux collection).""" + local_state_path = "/C/Users/Alice/AppData/Local/Google/Chrome/User Data/Local State" + expected_login_data_path = "/C/Users/Alice/AppData/Local/Google/Chrome/User Data/Default/Login Data" + expected_cookies_path = "/C/Users/Alice/AppData/Local/Google/Chrome/User Data/Default/Network/Cookies" + + file_local_state = create_file_enriched( + object_id="test-local-state-posix-001", + path=local_state_path, + mime_type="application/json", + magic_type="JSON data", + ) + + # Find the chromium_local_state rule + rule = next((r for r in engine.rules if r.name == "chromium_local_state"), None) + assert rule is not None, "chromium_local_state rule should be loaded" + + # Verify the rule triggers + trigger_matched = False + for trigger in rule.triggers: + if engine._matches_trigger(file_local_state, trigger): + trigger_matched = True + break + assert trigger_matched is True, "POSIX-style Local State file should match the rule trigger" + + # Verify path template expansion + login_data_file = next((lf for lf in rule.linked_files if lf.name == "login_data"), None) + cookies_file = next((lf for lf in rule.linked_files if lf.name == "cookies"), None) + + expanded_login_path = engine._expand_path_template(login_data_file.path_templates[0], local_state_path) + expanded_cookies_path = engine._expand_path_template(cookies_file.path_templates[0], local_state_path) + + assert expanded_login_path == expected_login_data_path, f"Expected {expected_login_data_path}, got {expanded_login_path}" + assert expanded_cookies_path == expected_cookies_path, f"Expected {expected_cookies_path}, got {expanded_cookies_path}" + + def test_chromium_local_state_wrong_mime_type_no_match(self, engine): + """Test that a file with the right path but wrong MIME type doesn't trigger the rule.""" + local_state_path = "C:/Users/Alice/AppData/Local/Google/Chrome/User Data/Local State" + + file_wrong_mime = create_file_enriched( + object_id="test-local-state-004", + path=local_state_path, + mime_type="text/plain", # Wrong MIME type + magic_type="ASCII text", + ) + + # Find the chromium_local_state rule + rule = next((r for r in engine.rules if r.name == "chromium_local_state"), None) + assert rule is not None, "chromium_local_state rule should be loaded" + + # Verify the rule does NOT trigger + trigger_matched = False + for trigger in rule.triggers: + if engine._matches_trigger(file_wrong_mime, trigger): + trigger_matched = True + break + assert trigger_matched is False, "File with wrong MIME type should not match" + + def test_chromium_local_state_wrong_path_no_match(self, engine): + """Test that a JSON file with the wrong path doesn't trigger the rule.""" + wrong_path = "C:/Users/Alice/AppData/Local/Google/Chrome/User Data/Default/Preferences" + + file_wrong_path = create_file_enriched( + object_id="test-preferences-001", + path=wrong_path, + mime_type="application/json", + magic_type="JSON data", + ) + + # Find the chromium_local_state rule + rule = next((r for r in engine.rules if r.name == "chromium_local_state"), None) + assert rule is not None, "chromium_local_state rule should be loaded" + + # Verify the rule does NOT trigger + trigger_matched = False + for trigger in rule.triggers: + if engine._matches_trigger(file_wrong_path, trigger): + trigger_matched = True + break + assert trigger_matched is False, "Preferences file should not match local_state rule" + + +@pytest.mark.asyncio +class TestPlaceholderResolutionIntegration: + """Integration tests for placeholder resolution in file linking.""" + + @pytest.fixture + def engine(self, tmp_path, mock_asyncpg_pool): + """Create a FileLinkingEngine with test database.""" + rules_dir = tmp_path / "rules" + rules_dir.mkdir() + + # Using a mock pool since we're testing logic, not actual DB + engine = FileLinkingEngine(connection_pool=mock_asyncpg_pool, rules_dir=str(rules_dir)) + + # Mock the database service methods (now async) + engine.db_service.add_file_listing = AsyncMock(return_value=True) + engine.db_service.add_file_linking = AsyncMock(return_value=True) + + return engine + + async def test_forward_resolution_placeholder_first_real_file_later(self, engine): + """Test forward resolution: placeholder exists in DB, real file arrives.""" + from unittest.mock import AsyncMock + + # Setup: Placeholder entry already exists + placeholder_path = "/C:/Users//AppData/Roaming/file.txt" + engine.db_service.get_placeholder_entries = AsyncMock( + return_value=[{"table_name": "file_listings", "path": placeholder_path}] + ) + engine.db_service.update_file_listing_path = AsyncMock(return_value=True) + + # Real file arrives + real_file = create_file_enriched( + object_id="test-file-001", + path="/C:/Users/john.doe/AppData/Roaming/file.txt", + mime_type="application/octet-stream", + magic_type="data", + ) + real_file.source = "test-agent" + + # Process the file (which triggers forward resolution) + await engine.apply_linking_rules(real_file) + + # Verify forward resolution was called and placeholder was updated + # Called twice: once for file_listings, once for file_linkings + assert engine.db_service.get_placeholder_entries.call_count == 2 + engine.db_service.update_file_listing_path.assert_called_once() + call_args = engine.db_service.update_file_listing_path.call_args + assert call_args[0][1] == placeholder_path # old path + assert call_args[0][2] == "/C:/Users/john.doe/AppData/Roaming/file.txt" # new path + + async def test_backward_resolution_real_file_first_placeholder_later(self, engine): + """Test backward resolution: real file exists, placeholder path created.""" + from pathlib import Path + from unittest.mock import AsyncMock + + # Setup: Real file already collected + real_path = "/C:/Users/john.doe/AppData/Roaming/Microsoft/Protect/S-1-5-21-123-456-789-1000/abc123" + engine.db_service.get_collected_files = AsyncMock(return_value=[real_path]) + engine.db_service.get_placeholder_entries = AsyncMock(return_value=[]) + + # Create a rule that generates a placeholder path + + rule_content = """ +name: "test_placeholder_rule" +description: "Test rule that creates placeholder paths" +category: "test" +enabled: true + +triggers: + - file_patterns: + - "**/Local State" + mime_patterns: + - "application/json" + +linked_files: + - name: "masterkey" + description: "User masterkey" + path_templates: + - "{parent_dir}/../../../Roaming/Microsoft/Protect//abc123" + priority: "high" + collection_reason: "Test placeholder" +""" + rule_file = Path(engine.rules_dir) / "test.yaml" + with open(rule_file, "w") as f: + f.write(rule_content) + + # Reload rules + engine._load_rules() + + # Trigger file that creates placeholder path + trigger_file = create_file_enriched( + object_id="test-trigger-001", + path="/C:/Users/john.doe/AppData/Local/Google/Chrome/User Data/Local State", + mime_type="application/json", + magic_type="JSON data", + ) + trigger_file.source = "test-agent" + + # Process the trigger file + await engine.apply_linking_rules(trigger_file) + + # Verify backward resolution was attempted + engine.db_service.get_collected_files.assert_called() + + async def test_chromium_masterkey_resolution_full_path(self, engine): + """Test resolution of Chromium masterkey with full paths.""" + from unittest.mock import AsyncMock + + placeholder_path = ( + "/C:/Users//AppData/Roaming/Microsoft/Protect/" + "/abc-123-def-456" + ) + real_path = ( + "/C:/Users/john.doe/AppData/Roaming/Microsoft/Protect/" + "S-1-5-21-1234567890-1234567890-1234567890-1000/abc-123-def-456" + ) + + engine.db_service.get_placeholder_entries = AsyncMock( + return_value=[{"table_name": "file_listings", "path": placeholder_path}] + ) + engine.db_service.update_file_listing_path = AsyncMock(return_value=True) + + # Real masterkey file arrives + masterkey_file = create_file_enriched( + object_id="test-masterkey-001", + path=real_path, + mime_type="application/octet-stream", + magic_type="data", + ) + masterkey_file.source = "test-agent" + + await engine.apply_linking_rules(masterkey_file) + + # Verify resolution occurred + engine.db_service.update_file_listing_path.assert_called_once() + call_args = engine.db_service.update_file_listing_path.call_args + assert "john.doe" in call_args[0][2] + assert "S-1-5-21-" in call_args[0][2] + + async def test_username_placeholder_resolution(self, engine): + """Test USERNAME placeholder resolution.""" + from unittest.mock import AsyncMock + + placeholder_path = "/C:/Users//Documents/file.txt" + real_path = "/C:/Users/alice.smith/Documents/file.txt" + + engine.db_service.get_placeholder_entries = AsyncMock( + return_value=[{"table_name": "file_listings", "path": placeholder_path}] + ) + engine.db_service.update_file_listing_path = AsyncMock(return_value=True) + + file_enriched = create_file_enriched( + object_id="test-001", path=real_path, mime_type="text/plain", magic_type="ASCII text" + ) + file_enriched.source = "test-agent" + + await engine.apply_linking_rules(file_enriched) + + engine.db_service.update_file_listing_path.assert_called_once() + call_args = engine.db_service.update_file_listing_path.call_args + assert call_args[0][2] == real_path + + async def test_sid_placeholder_resolution(self, engine): + """Test SID placeholder resolution.""" + from unittest.mock import AsyncMock + + placeholder_path = "/C:/Windows/System32/Config/systemprofile/AppData/Local//file.dat" + real_path = "/C:/Windows/System32/Config/systemprofile/AppData/Local/S-1-5-18/file.dat" + + engine.db_service.get_placeholder_entries = AsyncMock( + return_value=[{"table_name": "file_listings", "path": placeholder_path}] + ) + engine.db_service.update_file_listing_path = AsyncMock(return_value=True) + + file_enriched = create_file_enriched( + object_id="test-001", path=real_path, mime_type="application/octet-stream", magic_type="data" + ) + file_enriched.source = "test-agent" + + await engine.apply_linking_rules(file_enriched) + + engine.db_service.update_file_listing_path.assert_called_once() + call_args = engine.db_service.update_file_listing_path.call_args + assert "S-1-5-18" in call_args[0][2] + + async def test_both_placeholders_same_path(self, engine): + """Test multiple placeholders in the same path.""" + from unittest.mock import AsyncMock + + placeholder_path = ( + "/C:/Users//AppData/Roaming/Microsoft/Protect/" + "/masterkey" + ) + real_path = ( + "/C:/Users/bob.jones/AppData/Roaming/Microsoft/Protect/" + "S-1-5-21-9876543210-9876543210-9876543210-5000/masterkey" + ) + + engine.db_service.get_placeholder_entries = AsyncMock( + return_value=[{"table_name": "file_listings", "path": placeholder_path}] + ) + engine.db_service.update_file_listing_path = AsyncMock(return_value=True) + + file_enriched = create_file_enriched( + object_id="test-001", path=real_path, mime_type="application/octet-stream", magic_type="data" + ) + file_enriched.source = "test-agent" + + await engine.apply_linking_rules(file_enriched) + + engine.db_service.update_file_listing_path.assert_called_once() + call_args = engine.db_service.update_file_listing_path.call_args + resolved_path = call_args[0][2] + assert "bob.jones" in resolved_path + assert "S-1-5-21-" in resolved_path + + async def test_no_resolution_when_no_match(self, engine): + """Test that placeholder stays when no matching file exists.""" + from unittest.mock import AsyncMock + + # Placeholder for different path + placeholder_path = "/C:/Users//AppData/different.txt" + + engine.db_service.get_placeholder_entries = AsyncMock( + return_value=[{"table_name": "file_listings", "path": placeholder_path}] + ) + engine.db_service.update_file_listing_path = AsyncMock(return_value=True) + + # Real file with non-matching path + file_enriched = create_file_enriched( + object_id="test-001", + path="/C:/Users/john.doe/Documents/other.txt", + mime_type="text/plain", + magic_type="ASCII text", + ) + file_enriched.source = "test-agent" + + await engine.apply_linking_rules(file_enriched) + + # No resolution should occur + engine.db_service.update_file_listing_path.assert_not_called() + + async def test_case_insensitive_windows_paths(self, engine): + """Test that resolution works with different case variations.""" + from unittest.mock import AsyncMock + + placeholder_path = "/C:/Users//AppData/file.txt" + real_path_different_case = "/c:/users/john.doe/appdata/file.txt" + + engine.db_service.get_placeholder_entries = AsyncMock( + return_value=[{"table_name": "file_listings", "path": placeholder_path}] + ) + engine.db_service.update_file_listing_path = AsyncMock(return_value=True) + + file_enriched = create_file_enriched( + object_id="test-001", path=real_path_different_case, mime_type="text/plain", magic_type="ASCII text" + ) + file_enriched.source = "test-agent" + + await engine.apply_linking_rules(file_enriched) + + # Should resolve despite case differences + engine.db_service.update_file_listing_path.assert_called_once() + + async def test_source_isolation(self, engine): + """Test that placeholders from different sources don't cross-resolve.""" + from unittest.mock import AsyncMock + + # Placeholder from source-1 + placeholder_path = "/C:/Users//AppData/file.txt" + engine.db_service.get_placeholder_entries = AsyncMock( + return_value=[] # No placeholders for source-2 + ) + + # Real file from source-2 (different source) + file_enriched = create_file_enriched( + object_id="test-001", + path="/C:/Users/john.doe/AppData/file.txt", + mime_type="text/plain", + magic_type="ASCII text", + ) + file_enriched.source = "source-2" + + await engine.apply_linking_rules(file_enriched) + + # Verify query was called with correct source + engine.db_service.get_placeholder_entries.assert_called_with("source-2") diff --git a/libs/nemesis_dpapi/.vscode/settings.json b/libs/nemesis_dpapi/.vscode/settings.json new file mode 100644 index 0000000..a2e1e3a --- /dev/null +++ b/libs/nemesis_dpapi/.vscode/settings.json @@ -0,0 +1,56 @@ +{ + "[javascript]": { + "editor.formatOnSave": false + }, + "[html]": { + "editor.formatOnSave": false + }, + "[python]": { + "editor.codeActionsOnSave": { + "source.fixAll.ruff": "explicit", + "source.organizeImports.ruff": "explicit" + }, + "editor.defaultFormatter": "charliermarsh.ruff", + "editor.formatOnSave": true + }, + "autoDocstring.docstringFormat": "google", + "files.exclude": { + "**/.DS_Store": true, + "**/.git": true, + "**/.hg": true, + "**/.mypy_cache": true, + "**/.pytest_cache": true, + "**/.svn": true, + "**/.venv": true, + "**/__pycache__": true, + "**/Thumbs.db": true + }, + "files.trimTrailingWhitespace": true, + "files.watcherExclude": { + "**/__pycache__/**": true, + "**/.git/objects/**": true, + "**/.git/subtree-cache/**": true, + "**/.hg/store/**": true, + "**/.ipynb_checkpoints/**": true, + "**/.mypy_cache/**": true, + "**/.pytest_cache/**": true, + "**/.venv/**": true, + "**/*.egg-info/**": true, + "**/build/**": true, + "**/dist/**": true, + "**/node_modules/*/**": true + }, + "python.analysis.diagnosticSeverityOverrides": { + "reportMissingImports": "none", + "reportMissingModuleSource": "none" + }, + "python.analysis.useLibraryCodeForTypes": true, // Pyright + "python.languageServer": "Pylance", + "python.testing.pytestArgs": [ + "tests" + ], + "python.testing.unittestEnabled": false, + "python.testing.pytestEnabled": true, + "python.analysis.typeCheckingMode": "basic", + "debugpy.debugJustMyCode": false +} \ No newline at end of file diff --git a/libs/nemesis_dpapi/README.md b/libs/nemesis_dpapi/README.md new file mode 100644 index 0000000..41dec1f --- /dev/null +++ b/libs/nemesis_dpapi/README.md @@ -0,0 +1,18 @@ +# Tests +Run all unit tests: +```bash +poetry run pytest +``` + + + +# Benchmarks +Run only benchmark tests: +```bash +poetry run pytest --benchmark-only -v +``` + +Run benchmarks in a file: +```bash +poetry run pytest tests/benchmarks/bench_backupkey_decryption.py::TestMasterkeyDecryptionBenchmarks::test_single_masterkey_decryption --benchmark-only -v +``` \ No newline at end of file diff --git a/libs/nemesis_dpapi/examples/example.py b/libs/nemesis_dpapi/examples/example.py new file mode 100644 index 0000000..47e79ca --- /dev/null +++ b/libs/nemesis_dpapi/examples/example.py @@ -0,0 +1,154 @@ +"""Example showing DPAPI usage with eventing. + +To run this example: poetry run python /home/itadmin/code/Nemesis/libs/nemesis_dpapi/examples/example.py +This example demonstrates the complete DPAPI workflow including: + +1. Event monitoring setup + - Creates a custom DpapiObserver to monitor library events + - Subscribes to events for encrypted masterkeys, domain backup keys, and plaintext masterkeys + - Shows how to track DPAPI operations in real-time + +2. Masterkey management + - Loads masterkey files and domain backup key + - Adds multiple encrypted masterkeys to the DpapiManager + - Demonstrates both fake and real backup key scenarios + +3. Domain backup key operations + - First adds a fake/invalid backup key (shows failed decryption) + - Then adds the domain backup key + - Shows automatic decryption of masterkeys when valid backup key is added + +4. DPAPI blob decryption + - Loads an encrypted DPAPI blob + - Parses the blob to extract its masterkey GUID + - Decrypts the blob using the previously loaded masterkeys + - Displays the decrypted plaintext content +""" + +import asyncio +import base64 +import json +from pathlib import Path +from uuid import UUID + +from nemesis_dpapi import Blob, DomainBackupKey, DpapiManager, EncryptionFilter, MasterKey, MasterKeyFile +from nemesis_dpapi.eventing import ( + DpapiEvent, + DpapiObserver, + NewDomainBackupKeyEvent, + NewEncryptedMasterKeyEvent, + NewPlaintextMasterKeyEvent, +) + + +class MyDpapiEventMonitor(DpapiObserver): + """Class that observes DPAPI events.""" + + async def update(self, event: DpapiEvent) -> None: + """Handler for different types of DPAPI events.""" + + name = type(self).__name__ + + if isinstance(event, NewEncryptedMasterKeyEvent): + print(f"{name}: New encrypted masterkey added: {event.masterkey_guid}") + elif isinstance(event, NewDomainBackupKeyEvent): + print(f"{name}: New domain backup key added: {event.backup_key_guid}") + elif isinstance(event, NewPlaintextMasterKeyEvent): + print(f"{name}: New plaintext masterkey added: {event.masterkey_guid}") + else: + print(f"{name}: Event received: {type(event).__name__}") + + +async def main() -> None: + """Demonstrate DPAPI library usage with eventing system.""" + + # Real/valid DPAPI test data + fixtures_path = Path(__file__).parent.parent / "tests" / "fixtures" + backup_key_path = fixtures_path / "backupkey.json" + masterkey_file_path = fixtures_path / "masterkey_domain.bin" + blob_path = fixtures_path / "blob_without_entropy.bin" + + # Load and add real masterkey + with open(backup_key_path) as f: + backup_key_data = json.load(f) + + masterkey_file = MasterKeyFile.from_file(masterkey_file_path) + + if not masterkey_file or not masterkey_file.master_key or not masterkey_file.domain_backup_key: + raise ValueError("❗Invalid masterkey file") + + print("\n=== Adding Real DPAPI Data ===") + + print("=== DPAPI Library Usage with Events ===") + async with DpapiManager(storage_backend="memory") as manager: + # Register custom observer + monitor = MyDpapiEventMonitor() + await manager.subscribe(monitor) + + # Add the real masterkey from the test fixture + await manager.upsert_masterkey( + MasterKey( + guid=masterkey_file.masterkey_guid, + encrypted_key_usercred=masterkey_file.master_key, + encrypted_key_backup=masterkey_file.domain_backup_key.raw_bytes, + masterkey_type=masterkey_file.masterkey_type, + ) + ) + + encrypted_mks = await manager.get_masterkeys(encryption_filter=EncryptionFilter.ENCRYPTED_ONLY) + if len(encrypted_mks) == 1: + print("[✅] Added 1 masterkey:") + else: + raise ValueError("❗Failed to add encrypted masterkey") + + print(f"- MasterKey GUID : {masterkey_file.masterkey_guid}") + print(f"- Backup Key GUID : {masterkey_file.domain_backup_key.guid_key}") + + real_backup_key = DomainBackupKey( + guid=UUID(backup_key_data["backup_key_guid"]), + key_data=base64.b64decode(backup_key_data["key"]), + domain_controller=backup_key_data["dc"], + ) + await manager.upsert_domain_backup_key(real_backup_key) + + print(f"[✅] Added domain backup key: {real_backup_key.guid}") + + # Give auto-decryption time to work + await asyncio.sleep(1) + + # Check final results + all_keys_final = await manager.get_masterkeys() + decrypted_keys_final = await manager.get_masterkeys(encryption_filter=EncryptionFilter.DECRYPTED_ONLY) + + if len(decrypted_keys_final) == 0: + raise ValueError("❗ No masterkeys were auto-decrypted. This is unexpected and something is broken!") + else: + print( + f"[✅] Auto-decryption success! Total masterkeys: {len(all_keys_final)}, Decrypted: {len(decrypted_keys_final)}" + ) + + # Print the decrypted masterkeys in the form of {GUID}:SHA1 + for key in decrypted_keys_final: + print(f"{key.guid}:{key.plaintext_key_sha1.hex()}") # type: ignore + + # Demonstrate blob decryption with the blob_without_entropy.bin fixture + print("\n=== Decrypting DPAPI Blob ===") + + # Load and parse the blob + with open(blob_path, "rb") as f: + blob_data = f.read() + + # Parse blob to get its structure and masterkey GUID + blob = Blob.from_bytes(blob_data) + print(f"[*] Blob masterkey GUID: {blob.masterkey_guid}") + + # Decrypt the blob using the DPAPI manager + decrypted_blob_data = await manager.decrypt_blob(blob) + print(f"[*] Decrypted blob data: {decrypted_blob_data.decode('utf-8')}") + + if decrypted_blob_data == b"test": + print("[✅] Blob decrypted successfully and matches expected plaintext") + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/libs/nemesis_dpapi/examples/masterkey_auto_decrypt.py b/libs/nemesis_dpapi/examples/masterkey_auto_decrypt.py new file mode 100644 index 0000000..f03917d --- /dev/null +++ b/libs/nemesis_dpapi/examples/masterkey_auto_decrypt.py @@ -0,0 +1,293 @@ +"""Demo showing built-in auto-decryption functionality using test data. + +This example demonstrates the automatic decryption of DPAPI masterkeys when +domain backup keys are added to the DpapiManager. It runs three scenarios: + +1. Add encrypted masterkeys first, then add domain backup key + - Loads encrypted masterkeys into the manager + - Adds domain backup key which triggers automatic decryption of existing keys + - Shows how auto-decryption works on previously stored encrypted keys + +2. Add domain backup key first, then add masterkeys + - Adds domain backup key to the manager first + - Loads encrypted masterkeys which are automatically decrypted upon insertion + - Demonstrates auto-decryption of newly added keys + +3. Auto-decryption disabled + - Shows the same operations with auto_decrypt=False + - Proves that no automatic decryption occurs when the feature is disabled + - Validates that the auto-decryption is controllable + +""" + +import asyncio +import base64 +import json +import struct +from pathlib import Path +from uuid import UUID + +from Crypto.PublicKey import RSA +from nemesis_dpapi import DomainBackupKey, DpapiManager, EncryptionFilter, MasterKey, MasterKeyFile + + +def create_incorrect_backup_key(correct_key_data: bytes) -> bytes: + """Create a valid but incorrect domain backup key by generating a new RSA key. + + This creates a properly formatted PVK file with a different RSA key pair, + ensuring the backup key will pass all validation checks but fail to decrypt. + """ + # Parse the original PVK header to get the structure + magic, version, key_spec, encrypt_type, encrypt_data_size, pvk_size = struct.unpack("<6I", correct_key_data[:24]) + + # Generate a new RSA key with the same size (2048 bits is typical for DPAPI) + # We'll use a smaller size for faster generation in the example + new_rsa_key = RSA.generate(2048) + + # Export as DER format (PKCS#1 private key) + # private_key_der = new_rsa_key.export_key(format="DER", pkcs=1) + + # Convert DER to Microsoft's PRIVATE_KEY_BLOB format + # The PRIVATE_KEY_BLOB format is: + # PUBLICKEYSTRUC (8 bytes) + RSAPUBKEY (variable) + # For simplicity, we'll use the impacket structure from a generated key + + # Create a PRIVATE_KEY_BLOB from our RSA key + # Structure: magic (4) + bitlen (4) + pubexp (4) + modulus + prime1 + prime2 + exp1 + exp2 + coef + privexp + n = new_rsa_key.n + e = new_rsa_key.e + d = new_rsa_key.d + p = new_rsa_key.p + q = new_rsa_key.q + + # Calculate additional RSA-CRT parameters + dmp1 = d % (p - 1) + dmq1 = d % (q - 1) + iqmp = pow(q, -1, p) + + # Get bit length + bitlen = new_rsa_key.size_in_bits() + bytelen = bitlen // 8 + halflen = bytelen // 2 + + # Build the PRIVATEKEYBLOB structure + # BLOBHEADER + blob = struct.pack(" bytes: + return num.to_bytes(length, byteorder="little") + + blob += to_bytes_le(n, bytelen) # modulus + blob += to_bytes_le(p, halflen) # prime1 + blob += to_bytes_le(q, halflen) # prime2 + blob += to_bytes_le(dmp1, halflen) # exponent1 + blob += to_bytes_le(dmq1, halflen) # exponent2 + blob += to_bytes_le(iqmp, halflen) # coefficient + blob += to_bytes_le(d, bytelen) # privateExponent + + new_pvk_size = len(blob) + + # Build new PVK file with same header structure but new key + new_pvk = struct.pack("<6I", magic, version, key_spec, encrypt_type, encrypt_data_size, new_pvk_size) + + # Add encrypted data if present (we'll use empty since encrypt_type should be 0) + if encrypt_data_size > 0: + new_pvk += b"\x00" * encrypt_data_size + + # Add the new private key blob + new_pvk += blob + + return new_pvk + + +async def masterkeys_first_then_backup_key(mk_domain: MasterKeyFile, backup_key_data: dict[str, str]) -> None: + print("\n📋 Scenario 1: masterkeys added first, then backup key") + async with DpapiManager(storage_backend="memory") as dpapi: + # Add encrypted masterkeys first + print("\n1. Adding encrypted masterkeys...") + + # Domain masterkey (ed93694f-5a6d-46e2-b821-219f2c0ecd4d) + if mk_domain.master_key and mk_domain.domain_backup_key: + await dpapi.upsert_masterkey( + MasterKey( + guid=mk_domain.masterkey_guid, + masterkey_type=mk_domain.masterkey_type, + encrypted_key_usercred=mk_domain.master_key, + encrypted_key_backup=mk_domain.domain_backup_key.raw_bytes if mk_domain.domain_backup_key else None, + ) + ) + + # Check initial state + all_keys = await dpapi.get_masterkeys() + decrypted_keys = await dpapi.get_masterkeys(encryption_filter=EncryptionFilter.DECRYPTED_ONLY) + print(f"Initial state: {len(all_keys)} total, {len(decrypted_keys)} decrypted") + + # Add the domain backup key - this should trigger automatic decryption + print("\n2. Adding domain backup key (should decrypt domain masterkey)...") + # Use the correct backup key to demonstrate successful auto-decryption + backup_key = DomainBackupKey( + guid=UUID(backup_key_data["backup_key_guid"]), + key_data=base64.b64decode(backup_key_data["key"]), + domain_controller=backup_key_data["dc"], + ) + await dpapi.upsert_domain_backup_key(backup_key) + + # Give the background auto-decryption task a moment to complete + await asyncio.sleep(0.1) + + # Check final state - should show more decrypted keys if auto-decryption worked + all_keys_final = await dpapi.get_masterkeys() + decrypted_keys_final = await dpapi.get_masterkeys(encryption_filter=EncryptionFilter.DECRYPTED_ONLY) + print(f"After backup key: {len(all_keys_final)} total, {len(decrypted_keys_final)} decrypted") + + if len(decrypted_keys_final) > len(decrypted_keys): + print("✅ Auto-decryption successfully decrypted existing masterkeys!") + for mk in decrypted_keys_final: + print(f" Decrypted: {mk.guid} ({len(mk.plaintext_key or b'')} bytes)") + else: + print("❗ No masterkeys were auto-decrypted. This is unexpected and something is broken!") + + +async def backup_key_first_then_masterkeys( + mk_domain: MasterKeyFile, mk_local: MasterKeyFile, backup_key_data: dict[str, str] +) -> None: + print("\n📋 Scenario 2: backup key added first, then masterkeys") + async with DpapiManager(storage_backend="memory") as dpapi2: + # Add domain backup key first + print("\n3. Adding domain backup key first...") + # Use the correct backup key to demonstrate successful auto-decryption + backup_key = DomainBackupKey( + guid=UUID(backup_key_data["backup_key_guid"]), + key_data=base64.b64decode(backup_key_data["key"]), + domain_controller=backup_key_data["dc"], + ) + await dpapi2.upsert_domain_backup_key(backup_key) + + # Check initial state (should have backup key but no masterkeys) + backup_keys = await dpapi2.get_backup_keys() + all_keys_before = await dpapi2.get_masterkeys() + print(f"Initial state: {len(backup_keys)} backup keys, {len(all_keys_before)} masterkeys") + + # Add masterkeys - these should be auto-decrypted using existing backup key + print("\n4. Adding masterkeys (should be auto-decrypted with existing backup key)...") + + # Domain masterkey + if mk_domain.master_key and mk_domain.domain_backup_key: + await dpapi2.upsert_masterkey( + MasterKey( + guid=mk_domain.masterkey_guid, + masterkey_type=mk_domain.masterkey_type, + encrypted_key_usercred=mk_domain.master_key, + encrypted_key_backup=mk_domain.domain_backup_key.raw_bytes, + ) + ) + + # Local masterkey (won't be decrypted by domain backup key) + if mk_local.master_key and mk_local.backup_key: + await dpapi2.upsert_masterkey( + MasterKey( + guid=mk_local.masterkey_guid, + masterkey_type=mk_local.masterkey_type, + encrypted_key_usercred=mk_local.master_key, + encrypted_key_backup=mk_local.backup_key, + ) + ) + + # Give the background auto-decryption task a moment to complete + await asyncio.sleep(0.1) + + # Check final state + all_keys_after = await dpapi2.get_masterkeys() + decrypted_keys_after = await dpapi2.get_masterkeys(encryption_filter=EncryptionFilter.DECRYPTED_ONLY) + print(f"After adding masterkeys: {len(all_keys_after)} total, {len(decrypted_keys_after)} decrypted") + + if len(decrypted_keys_after) > 0: + print("✅ Auto-decryption successfully decrypted new masterkeys!") + for mk in decrypted_keys_after: + print(f" Decrypted: {mk.guid} ({len(mk.plaintext_key or b'')} bytes)") + else: + print("❗ No masterkeys were auto-decrypted. This is unexpected and something is broken!") + + +async def auto_decryption_disabled(mk_domain: MasterKeyFile, backup_key_data: dict[str, str]) -> None: + print("\n=== Scenario 3: Auto-decryption Disabled ===") + + # Now demonstrate with auto-decryption disabled using data + async with DpapiManager(storage_backend="memory", auto_decrypt=False) as dpapi_no_auto: + print("\n5. Adding masterkeys with auto-decryption disabled...") + + if mk_domain.master_key and mk_domain.domain_backup_key: + await dpapi_no_auto.upsert_masterkey( + MasterKey( + guid=mk_domain.masterkey_guid, + masterkey_type=mk_domain.masterkey_type, + encrypted_key_usercred=mk_domain.master_key, + encrypted_key_backup=mk_domain.domain_backup_key.raw_bytes, + ) + ) + + # Check state before backup key + all_keys_before = await dpapi_no_auto.get_masterkeys() + decrypted_keys_before = await dpapi_no_auto.get_masterkeys(encryption_filter=EncryptionFilter.DECRYPTED_ONLY) + print(f"Before backup key: {len(all_keys_before)} total, {len(decrypted_keys_before)} decrypted") + + # Add backup key - should NOT trigger auto-decryption + # Create a valid but incorrect backup key with a different RSA key + correct_key_data = base64.b64decode(backup_key_data["key"]) + incorrect_key_data = create_incorrect_backup_key(correct_key_data) + backup_key_disabled = DomainBackupKey( + guid=UUID(backup_key_data["backup_key_guid"]), + key_data=incorrect_key_data, + domain_controller=backup_key_data["dc"], + ) + await dpapi_no_auto.upsert_domain_backup_key(backup_key_disabled) + await asyncio.sleep(0.1) + + # Check state after backup key + all_keys_after = await dpapi_no_auto.get_masterkeys() + decrypted_keys_after = await dpapi_no_auto.get_masterkeys(encryption_filter=EncryptionFilter.DECRYPTED_ONLY) + print(f"After backup key: {len(all_keys_after)} total, {len(decrypted_keys_after)} decrypted") + + if len(decrypted_keys_after) == len(decrypted_keys_before): + print("✅ Auto-decryption correctly disabled - no automatic decryption occurred") + else: + print( + "❗ Some masterkeys were decrypted despite auto-decryption being disabled! This is unexpected and something is broken!" + ) + + +async def main() -> None: + """Demonstrate built-in auto-decryption feature with test data.""" + print("=== Built-in Auto-Decryption Demo (Real Test Data) ===") + + # Load test data fixtures + fixtures_path = Path(__file__).parent.parent / "tests" / "fixtures" + + # Load backup key from fixtures + with open(fixtures_path / "backupkey.json") as f: + backup_key_data = json.load(f) + + # Load masterkey files + mk_domain_file = fixtures_path / "masterkey_domain.bin" + mk_local_file = fixtures_path / "masterkey_local.bin" + + mk_domain = MasterKeyFile.from_file(mk_domain_file) + mk_local = MasterKeyFile.from_file(mk_local_file) + + # Run all scenarios + await masterkeys_first_then_backup_key(mk_domain, backup_key_data) + await backup_key_first_then_masterkeys(mk_domain, mk_local, backup_key_data) + await auto_decryption_disabled(mk_domain, backup_key_data) + + print("DONE!") + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/libs/nemesis_dpapi/kaitai/domain_backup_key.ksy b/libs/nemesis_dpapi/kaitai/domain_backup_key.ksy new file mode 100644 index 0000000..02f5180 --- /dev/null +++ b/libs/nemesis_dpapi/kaitai/domain_backup_key.ksy @@ -0,0 +1,226 @@ +meta: + id: pvk_file + title: Private Key (PVK) File Format + file-extension: pvk + endian: le + +doc: | + PVK (Private Key) file format used for storing cryptographic private keys. + This format can store keys with optional password-based encryption using + RC2-CBC or RC4 algorithms. + +seq: + - id: header + type: file_hdr + - id: pvk_data + type: pvk_blob + size: header.cb_pvk + doc: Private key data (may be encrypted depending on header.encrypt_type) + +types: + file_hdr: + doc: FILE_HDR structure describing the PVK file format + seq: + - id: magic + type: u4 + doc: Magic number identifying PVK file format (0xb0b5f11e) + - id: version + type: u4 + doc: File version (should be 0 for PVK_FILE_VERSION_0) + - id: key_spec + type: u4 + enum: key_spec_enum + doc: Key specification (AT_KEYEXCHANGE or AT_SIGNATURE) + - id: encrypt_type + type: u4 + enum: encrypt_type_enum + doc: Encryption type used for the private key data + - id: cb_encrypt_data + type: u4 + doc: Size of encrypted data (should be max 4096 bytes) + - id: cb_pvk + type: u4 + doc: Size of private key data (should be 1-4096 bytes) + + pvk_blob: + doc: PVK data containing blob header and key data + seq: + - id: blob_header + type: publickeystruc + - id: rsa_pubkey + type: rsapubkey + if: blob_header.ai_key_alg == alg_id_enum::calg_rsa_keyx or blob_header.ai_key_alg == alg_id_enum::calg_rsa_sign + - id: rsa_key_data + type: rsa_key_components + if: blob_header.ai_key_alg == alg_id_enum::calg_rsa_keyx or blob_header.ai_key_alg == alg_id_enum::calg_rsa_sign + - id: key_data_rem + size-eos: true + doc: Remaining key data for non-RSA keys + + publickeystruc: + doc: BLOBHEADER / PUBLICKEYSTRUC structure + seq: + - id: b_type + type: u1 + enum: blob_type_enum + doc: Blob type (e.g., PUBLICKEYBLOB, PRIVATEKEYBLOB) + - id: b_version + type: u1 + doc: Version (should be 0x02 for CUR_BLOB_VERSION) + - id: reserved + type: u2 + doc: Reserved, should be 0 + - id: ai_key_alg + type: u4 + enum: alg_id_enum + doc: Algorithm ID for the key + + rsapubkey: + doc: RSAPUBKEY structure for RSA keys + seq: + - id: magic + type: u4 + doc: Magic number (RSA1 for public, RSA2 for private) + - id: bitlen + type: u4 + doc: Number of bits in the modulus + - id: pubexp + type: u4 + doc: Public exponent + instances: + is_private_key: + value: magic == 0x32415352 + doc: True if magic is "RSA2" (private key) + is_public_key: + value: magic == 0x31415352 + doc: True if magic is "RSA1" (public key) + modulus_bytes: + value: bitlen / 8 + doc: Size of modulus in bytes + half_modulus_bytes: + value: bitlen / 16 + doc: Size of each prime in bytes + + rsa_key_components: + doc: RSA key components following RSAPUBKEY + seq: + - id: modulus + size: _parent.rsa_pubkey.modulus_bytes + doc: RSA modulus (n = p * q) + - id: prime1 + size: _parent.rsa_pubkey.half_modulus_bytes + if: _parent.blob_header.b_type == blob_type_enum::privatekeyblob + doc: First prime factor (p) + - id: prime2 + size: _parent.rsa_pubkey.half_modulus_bytes + if: _parent.blob_header.b_type == blob_type_enum::privatekeyblob + doc: Second prime factor (q) + - id: exponent1 + size: _parent.rsa_pubkey.half_modulus_bytes + if: _parent.blob_header.b_type == blob_type_enum::privatekeyblob + doc: d mod (p-1) + - id: exponent2 + size: _parent.rsa_pubkey.half_modulus_bytes + if: _parent.blob_header.b_type == blob_type_enum::privatekeyblob + doc: d mod (q-1) + - id: coefficient + size: _parent.rsa_pubkey.half_modulus_bytes + if: _parent.blob_header.b_type == blob_type_enum::privatekeyblob + doc: (inverse of q) mod p + - id: private_exponent + size: _parent.rsa_pubkey.modulus_bytes + if: _parent.blob_header.b_type == blob_type_enum::privatekeyblob + doc: Private exponent (d) + +enums: + key_spec_enum: + 1: at_keyexchange + 2: at_signature + + encrypt_type_enum: + 0: no_encrypt + 1: rc4_password_encrypt + 2: rc2_cbc_password_encrypt + + blob_type_enum: + 0x01: simpleblob + 0x06: publickeyblob + 0x07: privatekeyblob + 0x08: plaintextkeyblob + 0x09: opaquekeyblob + 0x0a: publickeyblobex + 0x0b: symmetricwrapkeyblob + 0x0c: keystateblob + + alg_id_enum: + 0x6601: calg_des + 0x6602: calg_rc2 + 0x6603: calg_3des + 0x6604: calg_desx + 0x6609: calg_3des_112 + 0x660a: calg_skipjack + 0x660b: calg_tek + 0x660c: calg_cylink_mek + 0x660d: calg_rc5 + 0x660e: calg_aes_128 + 0x660f: calg_aes_192 + 0x6610: calg_aes_256 + 0x6611: calg_aes + 0x6801: calg_rc4 + 0x6802: calg_seal + 0x8001: calg_md2 + 0x8002: calg_md4 + 0x8003: calg_md5 + 0x8004: calg_sha + 0x8005: calg_mac + 0x8008: calg_ssl3_shamd5 + 0x8009: calg_hmac + 0x800a: calg_tls1prf + 0x800b: calg_hash_replace_owf + 0x800c: calg_sha_256 + 0x800d: calg_sha_384 + 0x800e: calg_sha_512 + 0x2000: calg_no_sign + 0x2200: calg_dss_sign + 0x2203: calg_ecdsa + 0x2400: calg_rsa_sign + 0xa001: calg_ecmqv + 0xa003: calg_hughes_md5 + 0xa400: calg_rsa_keyx + 0xaa01: calg_dh_sf + 0xaa02: calg_dh_ephem + 0xaa03: calg_agreedkey_any + 0xaa04: calg_kea_keyx + 0xaa05: calg_ecdh + 0xae06: calg_ecdh_ephem + 0x4c01: calg_ssl3_master + 0x4c02: calg_schannel_master_hash + 0x4c03: calg_schannel_mac_key + 0x4c04: calg_pct1_master + 0x4c05: calg_ssl2_master + 0x4c06: calg_tls1_master + 0x4c07: calg_schannel_enc_key + 0xfffffffc: calg_oid_info_pq_t + 0xfffffffd: calg_oid_info_pq + 0xfffffffe: calg_oid_info_parameters + 0xffffffff: calg_oid_info_cng_only + +instances: + is_valid_magic: + value: header.magic == 0xb0b5f11e + is_valid_version: + value: header.version == 0 + is_encrypted: + value: header.encrypt_type != encrypt_type_enum::no_encrypt + expected_file_size: + value: 24 + header.cb_pvk + doc: Expected total file size (24-byte header + pvk_data) + actual_file_size: + value: _io.size + doc: Actual file size in bytes + pvk_data_ends_at_eof: + value: expected_file_size == actual_file_size + doc: True if pvk_data extends exactly to end of file + extra_bytes: + value: actual_file_size - expected_file_size + doc: Number of extra bytes after pvk_data (negative if file is truncated) \ No newline at end of file diff --git a/libs/nemesis_dpapi/kaitai/masterkey.ksy b/libs/nemesis_dpapi/kaitai/masterkey.ksy new file mode 100644 index 0000000..dcdc9af --- /dev/null +++ b/libs/nemesis_dpapi/kaitai/masterkey.ksy @@ -0,0 +1,134 @@ +meta: + id: dpapi_masterkey + title: DPAPI Master Key File + endian: le + ks-version: 0.9 +doc: | + Parser for Windows DPAPI master key files, based on the 010 Editor + template "DPAPI-Masterkey.bt" by Jean-Michel Picod. Modified made + Lee Chagolla-Christensen based on more recent analysis. +seq: + - id: header + type: masterkey_header + # Master key blob (encrypted) + - id: mkey + type: mkey_blob + if: header.cb_master_key > 0 + doc: > + Total size of the file header, in bytes. + # Backup key blob (encrypted) + - id: backup_key + type: backup_key_blob + if: header.cb_backup_key > 0 + # Credential history reference + - id: credhist + type: credhist + if: header.cb_credhist > 0 + # Domain key structure + - id: domain_key + type: domain_key + if: header.cb_domain_key > 0 +types: + # ---------------------------- + # Common GUID (little-endian layout) + # ---------------------------- + guid: + seq: + - id: data1 # 32-bit, little-endian + type: u4le + - id: data2 # 16-bit, little-endian + type: u2le + - id: data3 # 16-bit, little-endian + type: u2le + - id: data4 # 8 bytes, as-is + size: 8 + # ---------------------------- + # MkeyHeader (32 bytes) + # ---------------------------- + mkey_header: + seq: + - id: dw_revision + type: u4 + - id: pb_iv + size: 16 + - id: dw_rounds + type: u4 + - id: id_hash + type: u4 + - id: id_cipher + type: u4 + # ---------------------------- + # Variable-length encrypted blob for master key + # ---------------------------- + mkey_blob: + seq: + - id: hdr + type: mkey_header + - id: cipher + size: _root.header.cb_master_key - 32 # mkey_header is 32 bytes + # ---------------------------- + # Variable-length encrypted blob for backup key + # ---------------------------- + backup_key_blob: + seq: + - id: hdr + type: mkey_header + - id: cipher + size: _root.header.cb_backup_key - 32 # mkey_header is 32 bytes + # ---------------------------- + # DomainKey (BACKUPKEY_RECOVERY_BLOB) + # ---------------------------- + domain_key: + seq: + - id: dw_version + type: u4 + doc: version of structure (BACKUPKEY_RECOVERY_BLOB_VERSION) + - id: cb_encrypted_master_key + type: u4 + doc: quantity of encrypted master key data following structure + - id: cb_encrypted_payload + type: u4 + doc: quantity of encrypted payload + - id: guid_key + type: guid + doc: guid identifying backup key used + - id: encrypted_master_key + size: cb_encrypted_master_key + doc: encrypted master key data + - id: encrypted_payload + size: cb_encrypted_payload + doc: encrypted payload data + # ---------------------------- + # Credhist (reference – compact version: revision + GUID) + # ---------------------------- + credhist: + seq: + - id: dw_revision + type: u4 + - id: g_cred + type: guid + # ---------------------------- + # MasterkeyHeader (MASTERKEY_STORED_ON_DISK) + # ---------------------------- + masterkey_header: + seq: + - id: dw_revision + type: u4 + - id: f_modified + type: u4 + - id: sz_file_path + type: u4 + - id: wsz_guid_master_key + type: str + size: 80 + encoding: UTF-16LE + - id: dw_policy + type: u4 + - id: cb_master_key + type: u8 + - id: cb_backup_key + type: u8 + - id: cb_credhist + type: u8 + - id: cb_domain_key + type: u8 \ No newline at end of file diff --git a/libs/nemesis_dpapi/nemesis_dpapi/__init__.py b/libs/nemesis_dpapi/nemesis_dpapi/__init__.py new file mode 100644 index 0000000..58770b2 --- /dev/null +++ b/libs/nemesis_dpapi/nemesis_dpapi/__init__.py @@ -0,0 +1,52 @@ +"""DPAPI utility library for Windows Data Protection API operations.""" + +from .core import Blob, MasterKey, MasterKeyFile, MasterKeyPolicy, MasterKeyType +from .exceptions import ( + BlobDecryptionError, + DpapiError, + MasterKeyDecryptionError, + MasterKeyNotDecryptedError, + MasterKeyNotFoundError, + StorageError, +) +from .keys import ( + CredKey, + CredKeyHashType, + DomainBackupKey, + DpapiSystemCredential, + MasterKeyEncryptionKey, + NtlmHash, + Password, + Pbkdf2Hash, + Sha1Hash, +) +from .manager import DpapiManager +from .repositories import EncryptionFilter + +__all__ = [ + # Main classes + "Blob", + "DpapiManager", + "MasterKey", + "MasterKeyDecryptionError", + "MasterKeyPolicy", + "MasterKeyFile", + "EncryptionFilter", + "MasterKeyType", + # Keys + "CredKey", + "CredKeyHashType", + "DomainBackupKey", + "DpapiSystemCredential", + "MasterKeyEncryptionKey", + "NtlmHash", + "Password", + "Pbkdf2Hash", + "Sha1Hash", + # Exceptions + "BlobDecryptionError", + "DpapiError", + "MasterKeyNotFoundError", + "MasterKeyNotDecryptedError", + "StorageError", +] diff --git a/libs/nemesis_dpapi/nemesis_dpapi/auto_decrypt.py b/libs/nemesis_dpapi/nemesis_dpapi/auto_decrypt.py new file mode 100644 index 0000000..97e0bd5 --- /dev/null +++ b/libs/nemesis_dpapi/nemesis_dpapi/auto_decrypt.py @@ -0,0 +1,314 @@ +"""Auto-decryption observer for DPAPI manager.""" + +import asyncio +import time +from logging import getLogger +from typing import TYPE_CHECKING +from uuid import UUID + +from nemesis_dpapi.exceptions import MasterKeyDecryptionError +from nemesis_dpapi.keys import MasterKeyEncryptionKey +from nemesis_dpapi.repositories import EncryptionFilter + +from .core import BackupKeyRecoveryBlob, MasterKey, MasterKeyFile, MasterKeyPolicy, MasterKeyType +from .eventing import ( + DpapiEvent, + DpapiObserver, + NewDomainBackupKeyEvent, + NewDpapiSystemCredentialEvent, + NewEncryptedMasterKeyEvent, +) +from .keys import DpapiSystemCredential + +if TYPE_CHECKING: + from .manager import DpapiManager + + +logger = getLogger(__name__) + + +class AutoDecryptionObserver(DpapiObserver): + """Automatically decrypts encrypted masterkeys with available decryption keys. + + This class monitors DPAPI events and attempts automatic decryption of masterkeys: + - When new domain backup keys are added, attempts to decrypt existing encrypted masterkeys + - When new DPAPI_SYSTEM credentials are added, attempts to decrypt existing encrypted masterkeys + - When new encrypted masterkeys are added, attempts to decrypt them using all available + domain backup keys and DPAPI_SYSTEM credentials + """ + + def __init__(self, dpapi_manager: "DpapiManager"): + """Initialize the observer with a reference to the DPAPI manager.""" + self.dpapi_manager = dpapi_manager + self._background_tasks: set[asyncio.Task] = set() + + async def update(self, event: DpapiEvent) -> None: + """Handle DPAPI events, specifically new domain backup keys, encrypted masterkeys, and new credentials.""" + if isinstance(event, NewDomainBackupKeyEvent): + self._create_task(self._handle_new_backup_key(event)) + elif isinstance(event, NewEncryptedMasterKeyEvent): + await self._handle_new_encrypted_masterkey(event) + elif isinstance(event, NewDpapiSystemCredentialEvent): + self._create_task(await self._handle_new_sytem_credential(event)) + + def _create_task(self, coroutine) -> asyncio.Task: + """Creates a background task and maintains a reference until its completion + + The purpose of this is to maintain reference to the task so that the garbage collector + does not prematurely collect and destroy it. + """ + task = asyncio.create_task(coroutine) + self._background_tasks.add(task) + task.add_done_callback(self._background_tasks.discard) + + return task + + async def _handle_new_backup_key(self, event: NewDomainBackupKeyEvent) -> None: + """Handle a new domain backup key by decrypting existing encrypted masterkeys.""" + + logger.debug(f"New domain backup key added: {event.backup_key_guid}, attempting decryption...") + + start_time = time.perf_counter() + await self._attempt_masterkey_decryption_with_backup_key(event.backup_key_guid) + end_time = time.perf_counter() + + logger.debug(f"_attempt_masterkey_decryption_with_backup_key took {end_time - start_time:.4f} seconds") + + async def _handle_new_encrypted_masterkey(self, event: NewEncryptedMasterKeyEvent) -> None: + """Attempt to decrypt a new masterkey using existing domain backup keys.""" + + masterkeys = await self.dpapi_manager.get_masterkeys(guid=event.masterkey_guid) + + if not masterkeys: + raise ValueError(f"New masterkey {event.masterkey_guid} not found in the DB!") + + masterkey = masterkeys[0] + if masterkey.is_decrypted: + return # Already decrypted + + # We have an encrypted masterkey, try and decrypt with: + # - Available domain backup keys + # - Available DPAPI_SYSTEM credentials + # - (TODO) Available user credentials + + tasks = [ + self._create_task(self._decrypt_with_backup_keys(masterkey)), + self._create_task(self._decrypt_with_system_credentials(masterkey)), + # self._create_task(self._decrypt_with_user_credentials(masterkey)), # TODO + ] + + result = await asyncio.gather(*tasks, return_exceptions=True) + + async def _handle_new_sytem_credential(self, event: NewDpapiSystemCredentialEvent) -> None: + """Handle a new DPAPI_SYSTEM credential by attempting to decrypt existing encrypted masterkeys.""" + + logger.debug("New DPAPI_SYSTEM credential added. Attempting decryption...") + + start_time = time.perf_counter() + await self._attempt_masterkey_decryption_with_system_credential(event.credential) + end_time = time.perf_counter() + + logger.debug(f"_attempt_masterkey_decryption_with_system_credential took {end_time - start_time:.4f} seconds") + + async def _attempt_masterkey_decryption_with_system_credential( + self, credential: DpapiSystemCredential, encrypted_masterkeys: list[MasterKey] | None = None + ) -> None: + """Attempt to decrypt all encrypted masterkeys using the new DPAPI system credentials.""" + start_time = time.perf_counter() + + logger.debug("Attempting to decrypt masterkeys with new DPAPI_SYSTEM credential") + + if encrypted_masterkeys is None: + # Filter out User masterkeys + encrypted_masterkeys = await self.dpapi_manager.get_masterkeys( + encryption_filter=EncryptionFilter.ENCRYPTED_ONLY, + masterkey_type=[MasterKeyType.SYSTEM, MasterKeyType.SYSTEM_USER, MasterKeyType.UNKNOWN], + ) + + if not encrypted_masterkeys: + return + + decrypted_count = 0 + for encrypted_mk in encrypted_masterkeys: + try: + if not encrypted_mk.encrypted_key_usercred: + continue + + # Try the machine key first, then the user key + for i in range(2): + if i == 0: + mk_key = MasterKeyEncryptionKey.from_dpapi_system_cred(credential.machine_key) + else: + mk_key = MasterKeyEncryptionKey.from_dpapi_system_cred(credential.user_key) + + try: + plaintext_mk = encrypted_mk.decrypt(mk_key) + decrypted_count += 1 + except MasterKeyDecryptionError: + continue + + print(f"Successfully decrypted masterkey {encrypted_mk.guid} with DPAPI_SYSTEM credential") + await self.dpapi_manager.upsert_masterkey(plaintext_mk) + break # Decrypted successfully, no need to try other key + except Exception as e: + logger.error( + f"Error decrypting masterkey with DPAPI_SYSTEM credential. MasterKey UUID: {encrypted_mk.guid}: {e}" + ) + continue + + end_time = time.perf_counter() + logger.debug(f"_attempt_masterkey_decryption_with_system_credential took {end_time - start_time:.4f} seconds") + + async def _attempt_masterkey_decryption_with_backup_key( + self, backup_key_guid: UUID, encrypted_masterkeys: list[MasterKey] | None = None + ) -> None: + """Attempt to decrypt masterkeys using a backup key.""" + + try: + if encrypted_masterkeys is None: + # Filter out SYSTEM masterkeys + encrypted_masterkeys = await self.dpapi_manager.get_masterkeys( + encryption_filter=EncryptionFilter.ENCRYPTED_ONLY, + masterkey_type=[MasterKeyType.USER, MasterKeyType.UNKNOWN], + ) + + if not encrypted_masterkeys: + return + + backup_keys = await self.dpapi_manager.get_backup_keys(guid=backup_key_guid) + + if not backup_keys: + return + + new_backup_key = backup_keys[0] + + # Try to decrypt each encrypted masterkey with the new backup key + for enc_masterkey in encrypted_masterkeys: + if enc_masterkey.is_decrypted: + continue + + if enc_masterkey.encrypted_key_backup is None: + continue + + if enc_masterkey.masterkey_type not in (MasterKeyType.USER, MasterKeyType.UNKNOWN): + continue + + # Parse the encrypted backup key bytes into a BackupKeyRecoveryBlob + try: + backup_key_blob = BackupKeyRecoveryBlob.from_bytes(enc_masterkey.encrypted_key_backup) + except Exception: + # Skip if we can't parse the backup key blob + continue + + if backup_key_blob.guid_key != new_backup_key.guid: + continue # This backup key does not match the masterkey's backup key GUID + + masterkey_file = MasterKeyFile( + version=0, + modified=False, + file_path=None, + masterkey_guid=enc_masterkey.guid, + policy=MasterKeyPolicy.NONE, + masterkey_type=enc_masterkey.masterkey_type, + domain_backup_key=backup_key_blob, + raw_bytes=b"", # Not needed for decryption + ) + + try: + result = masterkey_file.decrypt(new_backup_key) + except (MasterKeyDecryptionError, ValueError): + # Skip masterkeys that can't be decrypted (wrong key, local backup key, etc.) + continue + + if result: + print( + f"Successfully decrypted masterkey {enc_masterkey.guid} with new backup key {new_backup_key.guid}" + ) + new_mk = MasterKey( + guid=enc_masterkey.guid, + masterkey_type=enc_masterkey.masterkey_type, + encrypted_key_usercred=enc_masterkey.encrypted_key_usercred, + encrypted_key_backup=enc_masterkey.encrypted_key_backup, + plaintext_key=result.plaintext_key, + plaintext_key_sha1=result.plaintext_key_sha1, + backup_key_guid=result.backup_key_guid, + ) + + await self.dpapi_manager.upsert_masterkey(new_mk) + + except Exception as e: + logger.error(f"Auto-decrypt _attempt_masterkey_decryption_with_backup_key error: {e}") + + async def _decrypt_with_backup_keys(self, masterkey: MasterKey) -> None: + """Attempt to decrypt a masterkey using all available backup keys.""" + start_time = time.perf_counter() + if masterkey.encrypted_key_backup is None: + return # Cannot decrypt if there's no backup key data + + backup_keys = await self.dpapi_manager.get_backup_keys() + if not backup_keys: + return + + # Try to decrypt the masterkey with each backup key + for backup_key in backup_keys: + try: + # Parse the encrypted backup key bytes into a BackupKeyRecoveryBlob + try: + backup_key_blob = BackupKeyRecoveryBlob.from_bytes(masterkey.encrypted_key_backup) + except Exception: + # Skip if we can't parse the backup key blob + continue + + masterkey_file = MasterKeyFile( + version=0, + modified=False, + file_path=None, + masterkey_guid=masterkey.guid, + policy=MasterKeyPolicy.NONE, + masterkey_type=masterkey.masterkey_type, + domain_backup_key=backup_key_blob, + raw_bytes=b"", # Not needed for decryption + ) + + result = masterkey_file.decrypt(backup_key) + except Exception: + logger.debug(f"Failed to decrypt masterkey {masterkey.guid} with backup key {backup_key.guid}") + continue + + if result: + new_mk = MasterKey( + guid=masterkey.guid, + masterkey_type=masterkey.masterkey_type, + encrypted_key_usercred=masterkey.encrypted_key_usercred, + encrypted_key_backup=masterkey.encrypted_key_backup, + plaintext_key=result.plaintext_key, + plaintext_key_sha1=result.plaintext_key_sha1, + backup_key_guid=result.backup_key_guid, + ) + print(f"Successfully decrypted masterkey {masterkey.guid} with backup key {backup_key.guid}") + await self.dpapi_manager.upsert_masterkey(new_mk) + break + + end_time = time.perf_counter() + logger.debug(f"_attempt_masterkey_decryption took {end_time - start_time:.4f} seconds") + + async def _decrypt_with_system_credentials(self, masterkey: MasterKey) -> None: + """Attempt to decrypt a masterkey using all available DPAPI_SYSTEM credentials.""" + + if masterkey.masterkey_type == MasterKeyType.USER: + return # Skip user masterkeys + + start_time = time.perf_counter() + if masterkey.encrypted_key_usercred is None: + return # Cannot decrypt if there's no user credential data + + system_credentials = await self.dpapi_manager.get_system_credentials() + if not system_credentials: + return + + # Try to decrypt the masterkey with each system credential + for credential in system_credentials: + await self._attempt_masterkey_decryption_with_system_credential(credential, [masterkey]) + + end_time = time.perf_counter() + logger.debug(f"_decrypt_with_system_credentials took {end_time - start_time:.4f} seconds") diff --git a/libs/nemesis_dpapi/nemesis_dpapi/core.py b/libs/nemesis_dpapi/nemesis_dpapi/core.py new file mode 100644 index 0000000..45695a5 --- /dev/null +++ b/libs/nemesis_dpapi/nemesis_dpapi/core.py @@ -0,0 +1,759 @@ +"""Core data types used in the DPAPI library""" + +from __future__ import annotations + +import json +import re +import struct +from enum import Enum, IntFlag +from pathlib import Path +from typing import TYPE_CHECKING, Self +from uuid import UUID + +from Cryptodome.Cipher import PKCS1_v1_5 +from Cryptodome.Hash import SHA1 +from dpapick3 import blob as dpapick3_blob +from impacket.dpapi import ( + DPAPI_BLOB, + DPAPI_DOMAIN_RSA_MASTER_KEY, + PRIVATE_KEY_BLOB, + PVK_FILE_HDR, + privatekeyblob_to_pkcs1, +) +from impacket.dpapi import MasterKey as ImpacketMasterKey +from pydantic import BaseModel as PydanticBaseModel +from pydantic import ConfigDict, model_validator + +from .exceptions import BlobDecryptionError, BlobParsingError, InvalidBackupKeyError, MasterKeyDecryptionError + +if TYPE_CHECKING: + from .keys import DomainBackupKey, MasterKeyEncryptionKey + +DEFAULT_BLOB_PROVIDER_GUID = UUID("DF9D8CD0-1501-11D1-8C7A-00C04FC297EB") + +# Pre-compiled regex patterns for MasterKeyType path matching +_PATTERN_SYSTEM_USER = re.compile(r"/windows/system32/microsoft/protect/s-1-5-18/user/", re.IGNORECASE) +_PATTERN_SYSTEM = re.compile(r"/windows/system32/microsoft/protect/s-1-5-18/", re.IGNORECASE) +_PATTERN_SERVICE_PROFILES = re.compile(r"/windows/serviceprofiles/(localservice|networkservice)/", re.IGNORECASE) +_PATTERN_USER_FULL = re.compile(r"/users/.+/appdata/roaming/microsoft/protect/s-1-5-21-[\d-]+/[0-9a-f-]+", re.IGNORECASE) +_PATTERN_USER_FALLBACK = re.compile(r"/users/.+/appdata/roaming/microsoft/protect/", re.IGNORECASE) + + +class BaseModel(PydanticBaseModel): + model_config = ConfigDict( + frozen=True, + extra="forbid", + ) + + def to_json(self, **kwargs) -> str: + """Serialize model to JSON string.""" + return self.model_dump_json(**kwargs) + + @classmethod + def from_json(cls, data: str, **kwargs): + """ + Deserialize a JSON string into a model instance. + kwargs are passed to `json.loads`. + """ + parsed = json.loads(data, **kwargs) + return cls.model_validate(parsed) + + +class FlagMixin: + def has_any(self: Self, flags: Self) -> bool: + """True if *any* bit in `flags` is set in `self`.""" + return bool(self & flags) + + def has_all(self: Self, flags: Self) -> bool: + """True if *all* bits in `flags` are set in `self`.""" + return (self & flags) == flags + + def enable(self: Self, flags: Self) -> Self: + """Return self | flags.""" + return self | flags + + def disable(self: Self, flags: Self) -> Self: + """Return self with `flags` cleared.""" + return self & ~flags + + +class MasterKeyPolicy(FlagMixin, IntFlag): + """Policy bits for DPAPI masterkey.""" + + NONE = 0x0 # No special policy + LOCAL_BACKUP = 0x1 # Policy bit for local only (no DC) backup + NO_BACKUP = 0x2 # Policy bit for NO backup (Win95) + DPAPI_OWF = 0x4 # Use the DPAPI One way function of the password (SHA_1(pw)) + + +class MasterKeyType(str, Enum): + """Type of DPAPI masterkey, which determines the decryption method. + + This classification is based on the account type that generated the masterkey + and determines which credentials or keys are needed for decryption. + """ + + UNKNOWN = "unknown" # Masterkey type could not be determined from the file path + USER = "user" # User-level masterkey (domain or local user account) - decrypted with user password and/or domain backup key + SYSTEM = "system" # System-level masterkey (SYSTEM, LocalService, or NetworkService) - decrypted with DPAPI_SYSTEM machine key + SYSTEM_USER = "system_user" # Machine's DPAPI SYSTEM user masterkey - decrypted with DPAPI_SYSTEM user key + + @classmethod + def from_path(cls, path: str | None) -> MasterKeyType: + """Determine the user account type from a masterkey file path. + + Args: + path: The file path to the masterkey file + + Returns: + MasterKeyType based on the path pattern + + Examples: + - /C:/Users/username/AppData/Roaming/Microsoft/Protect/S-1-5-21-.../{GUID} -> USER + - /C:/Windows/System32/Microsoft/Protect/S-1-5-18/... -> SYSTEM + - /C:/Windows/System32/Microsoft/Protect/S-1-5-18/User/... -> SYSTEM_USER + - /C:/Windows/ServiceProfiles/LocalService/... -> SYSTEM + - /C:/Windows/ServiceProfiles/NetworkService/... -> SYSTEM + """ + if not path: + return cls.UNKNOWN + + # Check for SYSTEM account with User subdirectory (SYSTEM_USER) + # Pattern: .../Windows/System32/Microsoft/Protect/S-1-5-18/User/... + if _PATTERN_SYSTEM_USER.search(path): + return cls.SYSTEM_USER + + # Check for SYSTEM account patterns (SYSTEM, LocalService, NetworkService) + # Pattern: .../Windows/System32/Microsoft/Protect/S-1-5-18/... + if _PATTERN_SYSTEM.search(path): + return cls.SYSTEM + + # Check for LocalService or NetworkService in ServiceProfiles + # Pattern: .../Windows/ServiceProfiles/(LocalService|NetworkService)/... + if _PATTERN_SERVICE_PROFILES.search(path): + return cls.SYSTEM + + # Check for user profiles with DPAPI protect directory + # Pattern: .../Users/.../AppData/Roaming/Microsoft/Protect/S-1-5-21-.../... + if _PATTERN_USER_FULL.search(path): + return cls.USER + + # Fallback: Check for any user profile with Microsoft Protect + if _PATTERN_USER_FALLBACK.search(path): + return cls.USER + + # Default to UNKNOWN if we can't determine the type + return cls.UNKNOWN + + +class MasterKey(BaseModel): + """Represents a DPAPI masterkey. + + Attributes: + guid: Unique identifier for this masterkey. + encrypted_key_user: Masterkey encrypted with the user's password-derived key. + encrypted_key_backup: Masterkey encrypted with the domain backup key. + backup_key_guid: GUID of the domain backup key used to encrypt this masterkey. + plaintext_key: Decrypted masterkey data. + plaintext_key_sha1: SHA1 hash of the plaintext masterkey. AKA the Master Key (MK) Encryption Key. + masterkey_type: Type of user account this masterkey belongs to. + """ + + guid: UUID + masterkey_type: MasterKeyType + encrypted_key_usercred: bytes | None = None + encrypted_key_backup: bytes | None = None + plaintext_key: bytes | None = None + plaintext_key_sha1: bytes | None = None + backup_key_guid: UUID | None = None + + @model_validator(mode="before") + @classmethod + def compute_plaintext_key_sha1(cls, data: dict) -> dict: + """Auto-calculate plaintext_key_sha1 from plaintext_key if not provided, or validate if both are provided.""" + # Handle both dict and model instance inputs + if isinstance(data, dict): + plaintext_key = data.get("plaintext_key") + plaintext_key_sha1 = data.get("plaintext_key_sha1") + + if plaintext_key is not None: + expected_sha1 = SHA1.new(plaintext_key).digest() + + if plaintext_key_sha1 is None: + # Auto-calculate if not provided + data["plaintext_key_sha1"] = expected_sha1 + elif plaintext_key_sha1 != expected_sha1: + # Validate if both are provided + raise ValueError( + f"plaintext_key_sha1 does not match the SHA1 hash of plaintext_key. " + f"Expected: {expected_sha1.hex()}, Got: {plaintext_key_sha1.hex()}" + ) + + return data + + @property + def is_decrypted(self) -> bool: + """Check if masterkey has been decrypted.""" + + # We only need to check if the sha1 is there because there's a constraint + # that if plaintext_key is set, plaintext_key_sha1 must also be set + return self.plaintext_key_sha1 is not None + + def __str__(self) -> str: + """Return a string representation of the MasterKey with all properties.""" + lines = [ + f"MasterKey({self.guid})", + f" guid: {self.guid}", + f" encrypted_key_usercred: {self.encrypted_key_usercred.hex() if self.encrypted_key_usercred else None}", + f" encrypted_key_backup: {self.encrypted_key_backup.hex() if self.encrypted_key_backup else None}", + f" plaintext_key: {self.plaintext_key.hex() if self.plaintext_key else None}", + f" plaintext_key_sha1: {self.plaintext_key_sha1.hex() if self.plaintext_key_sha1 else None}", + f" backup_key_guid: {self.backup_key_guid}", + f" masterkey_type: {self.masterkey_type.value}", + ] + return "\r\n".join(lines) + + def decrypt(self, master_key_encryption_key: MasterKeyEncryptionKey) -> MasterKey: + """Decrypt the master key using the provided master key encryption key. + + Args: + master_key_encryption_key: The 20-byte SHA1 hash used to decrypt the master key + + Returns: + A new MasterKey instance with decrypted plaintext_key and plaintext_key_sha1 + + Raises: + ValueError: If encrypted_key_usercred is None + MasterKeyDecryptionError: If decryption fails + """ + if self.encrypted_key_usercred is None: + raise ValueError("No encrypted user credential key available for decryption") + + mk = ImpacketMasterKey(self.encrypted_key_usercred) + plaintext_mk = mk.decrypt(master_key_encryption_key.key.value) + + if not plaintext_mk: + raise MasterKeyDecryptionError("Decryption failed") + + plaintext_key_sha1 = SHA1.new(plaintext_mk).digest() + + return self.model_copy( + update={ + "plaintext_key": plaintext_mk, + "plaintext_key_sha1": plaintext_key_sha1, + } + ) + + +class Blob(BaseModel): + """Represents a DPAPI encrypted blob. + + Structure representation comes from parsing in SPCryptProtect and SPCryptUnprotect in crypt32p.cpp. + """ + + model_config = {"frozen": True} + + outerVersion: int + provider_guid: UUID + + version: int + masterkey_guid: UUID + prompt_flags: int + description: str + encryption_algorithm_id: int + encryption_algorithm_key_size: int + encryption_key: bytes + encryption_salt: bytes + mac_algorithm_id: int + mac_algorithm_key_size: int + mac_key: bytes + encrypted_data: bytes + mac: bytes # MAC signature. Includes all data from the beginning of the structure through encrypted_data + + # Raw bytes of the entire blob + raw_bytes: bytes + + @classmethod + def from_file(cls, file_path: str | Path) -> Blob: + """Parse a DPAPI blob from a file path. + + Args: + file_path: Path to the blob file + + Returns: + Blob instance with parsed data + + Raises: + ValueError: If blob format is invalid + FileNotFoundError: If file doesn't exist + """ + file_path = Path(file_path) + + if not file_path.exists(): + raise FileNotFoundError(f"Blob file not found: {file_path}") + + with open(file_path, "rb") as f: + data = f.read() + + return cls.from_bytes(data) + + @classmethod + def from_bytes(cls, data: bytes) -> Blob: + """Parse a DPAPI blob from raw bytes. + + Args: + data: Raw blob bytes + + Returns: + Blob instance with parsed data + + Raises: + ValueError: If blob format is invalid + """ + + def _parse_guid(data: bytes) -> UUID: + # '<' = little-endian for first 3 fields, '>' = big-endian for last field + data1, data2, data3 = struct.unpack(" bytes: + """Decrypt the blob using the provided master key. + + Args: + masterkey: The decrypted MasterKey instance to use for decryption + entropy: Optional entropy data used during encryption/decryption + + Returns: + Decrypted blob data as bytes + + Raises: + ValueError: If masterkey is not decrypted or decryption fails + """ + if not masterkey.is_decrypted: + raise ValueError("Master key must be decrypted before use") + + blob_dpapick = dpapick3_blob.DPAPIBlob(self.raw_bytes) + + if not blob_dpapick.decrypt(masterkey.plaintext_key_sha1, entropy): + raise BlobDecryptionError(f"Failed to decrypt blob with provided master key: {masterkey.guid}") + + if not blob_dpapick.cleartext: + raise Exception("Decryption succeeded but no cleartext available") + + return blob_dpapick.cleartext + + +class BackupKeyRecoveryBlob(BaseModel): + """Represents a BACKUPKEY_RECOVERY_BLOB structure. + + Used for domain backup key recovery operations in DPAPI. + """ + + model_config = {"frozen": True} + + raw_bytes: bytes + + version: int + cb_encrypted_master_key: int + cb_encrypted_payload: int + guid_key: UUID + encrypted_master_key: bytes + encrypted_payload: bytes + + def __str__(self) -> str: + """Return a string representation of the BackupKeyRecoveryBlob with all properties.""" + lines = [ + "BackupKeyRecoveryBlob()", + f" version: {self.version}", + f" cb_encrypted_master_key: {self.cb_encrypted_master_key}", + f" cb_encrypted_payload: {self.cb_encrypted_payload}", + f" guid_key: {self.guid_key}", + f" encrypted_master_key: {self.encrypted_master_key.hex()}", + f" encrypted_payload: {self.encrypted_payload.hex()}", + ] + return "\n".join(lines) + + @classmethod + def from_file(cls, file_path: str | Path) -> BackupKeyRecoveryBlob: + """Parse a BACKUPKEY_RECOVERY_BLOB from a file path. + + Args: + file_path: Path to the BACKUPKEY_RECOVERY_BLOB file + + Returns: + BackupKeyRecoveryBlob instance with parsed data + + Raises: + ValueError: If format is invalid + FileNotFoundError: If file doesn't exist + """ + file_path = Path(file_path) + + if not file_path.exists(): + raise FileNotFoundError(f"Blob file not found: {file_path}") + + with open(file_path, "rb") as f: + data = f.read() + + return cls.from_bytes(data) + + @classmethod + def from_bytes(cls, data: bytes) -> BackupKeyRecoveryBlob: + """Parse a BACKUPKEY_RECOVERY_BLOB from bytes. + + Args: + data: Raw bytes containing the BACKUPKEY_RECOVERY_BLOB structure + + Returns: + BackupKeyRecoveryBlob instance with parsed data + + Raises: + ValueError: If the data format is not a valid BACKUPKEY_RECOVERY_BLOB structure + """ + if len(data) < 28: # Minimum size: 4 + 4 + 4 + 16 + raise ValueError(f"Data too short for BACKUPKEY_RECOVERY_BLOB: {len(data)} bytes") + + # Parse header: DWORD version, DWORD cbEncryptedMasterKey, DWORD cbEncryptedPayload + blob_header = struct.unpack(" len(data) or cb_encrypted_payload > len(data): + raise ValueError( + f"Invalid sizes: cb_encrypted_master_key={cb_encrypted_master_key}, " + f"cb_encrypted_payload={cb_encrypted_payload}, data_len={len(data)}" + ) + + # Validate total size matches expected size + expected_size = 28 + cb_encrypted_master_key + cb_encrypted_payload + if len(data) < expected_size: + raise ValueError(f"Data too short: expected {expected_size} bytes, got {len(data)} bytes") + + # Parse GUID (16 bytes starting at offset 12) + guid_bytes = data[12:28] + data1, data2, data3 = struct.unpack(" str: + """Return a string representation of the MasterKeyFile with all properties.""" + # Interpret policy flags + policy_str = str(self.policy.name) if self.policy else "NONE" + if self.policy and self.policy != MasterKeyPolicy.NONE: + flags = [] + if self.policy.has_any(MasterKeyPolicy.LOCAL_BACKUP): + flags.append("LOCAL_BACKUP") + if self.policy.has_any(MasterKeyPolicy.NO_BACKUP): + flags.append("NO_BACKUP") + if self.policy.has_any(MasterKeyPolicy.DPAPI_OWF): + flags.append("DPAPI_OWF") + policy_str = " | ".join(flags) if flags else "NONE" + + lines = [ + "MasterKeyFile()", + f" version: {self.version}", + f" modified: {self.modified}", + f" file_path: {self.file_path}", + f" masterkey_guid: {self.masterkey_guid}", + f" policy: {self.policy} ({policy_str})\n", + f" masterkey_type: {self.masterkey_type.value}\n", + f"master_key: {self.master_key.hex() if self.master_key else None}\n", + f"local_key: {self.local_key.hex() if self.local_key else None}\n", + f"backup_key: {self.backup_key.hex() if self.backup_key else None}\n", + f"domain_backup_key: {self.domain_backup_key}", + ] + return "\n".join(lines) + + @classmethod + def from_file(cls, file_path: str | Path, masterkey_type: MasterKeyType = MasterKeyType.UNKNOWN) -> MasterKeyFile: + """Parse a masterkey file from disk. + + Args: + file_path: Path to the masterkey file + masterkey_type: Type of user account this masterkey belongs to (default: UNKNOWN) + + Returns: + MasterKeyFile instance with parsed data + + Raises: + ValueError: If file format is invalid + FileNotFoundError: If file doesn't exist + """ + file_path = Path(file_path) + + if not file_path.exists(): + raise FileNotFoundError(f"Masterkey file not found: {file_path}") + + with open(file_path, "rb") as f: + data = f.read() + + return cls.from_bytes(data, masterkey_type=masterkey_type) + + @classmethod + def from_bytes(cls, data: bytes, masterkey_type: MasterKeyType = MasterKeyType.UNKNOWN) -> MasterKeyFile: + """Parse a masterkey from raw bytes. + + Args: + data: Raw masterkey file bytes + masterkey_type: Type of user account this masterkey belongs to (default: UNKNOWN) + + Returns: + MasterKeyFile instance with parsed data + + Raises: + ValueError: If file format is invalid + """ + if len(data) < 44: # Minimum size for MASTERKEY_STORED_ON_DISK header + raise ValueError("File too small to contain valid masterkey data") + + # Parse the on-disk structure + # struct format: DWORD dwVersion, BOOL fModified, DWORD szFilePath, + # WCHAR wszguidMasterKey[40], DWORD dwPolicy, + # DWORD cbMK, DWORD pbMK, DWORD cbLK, DWORD pbLK, + # DWORD cbBK, DWORD pbBK, DWORD cbBBK, DWORD pbBBK + + header_format = " 0: + master_key = data[offset : offset + cb_mk] + offset += cb_mk + + local_key = None + if cb_lk > 0: + local_key = data[offset : offset + cb_lk] + offset += cb_lk + + backup_key = None + if cb_bk > 0: + backup_key = data[offset : offset + cb_bk] + offset += cb_bk + + backup_dc_key = None + if cb_bbk > 0: + backup_dc_key_bytes = data[offset : offset + cb_bbk] + backup_dc_key = BackupKeyRecoveryBlob.from_bytes(backup_dc_key_bytes) + + return cls( + version=version, + modified=modified, + file_path=None, # Invalid on disk, set to None + masterkey_guid=guid, + policy=MasterKeyPolicy(policy), + masterkey_type=masterkey_type, + master_key=master_key, + local_key=local_key, + backup_key=backup_key, + domain_backup_key=backup_dc_key, + raw_bytes=data, + ) + + def decrypt(self, backup_key: DomainBackupKey) -> MasterKey: + """Decrypt this masterkey file using a domain backup key. + + Args: + backup_key: The domain backup key to use for decryption + + Returns: + MasterKey instance with decrypted key data + + Raises: + ValueError: If masterkey file has no domain backup key + InvalidBackupKeyError: If domain backup key is invalid or malformed + MasterKeyDecryptionError: If decryption fails + """ + if not self.domain_backup_key: + raise ValueError("Masterkey file contains no domain backup key data") + + try: + # Extract the private key from the backup key data + key = PRIVATE_KEY_BLOB(backup_key.key_data[len(PVK_FILE_HDR()) :]) + private = privatekeyblob_to_pkcs1(key) + cipher = PKCS1_v1_5.new(private) + except Exception as e: + raise InvalidBackupKeyError(f"Invalid domain backup key: {e}") from e + + # Decrypt the masterkey. Encrypted masterkey is in reverse byte order (per Impacket implementation) + decrypted_key = cipher.decrypt(self.domain_backup_key.encrypted_master_key[::-1], None) + + if not decrypted_key: + raise MasterKeyDecryptionError("Failed to decrypt masterkey with backup key") + + domain_master_key = DPAPI_DOMAIN_RSA_MASTER_KEY(decrypted_key) + buffer = domain_master_key["buffer"] + + # If it's a version 3 masterkey, skip the first 8 bytes (structure is different) + if len(decrypted_key) == 128: + key_offset = 8 + elif len(decrypted_key) == 104: + key_offset = 0 + else: + raise MasterKeyDecryptionError( + f"Unexpected decrypted key length: {len(decrypted_key)}. Decrypted key: {decrypted_key.hex()}" + ) + + plaintext_key = buffer[key_offset : key_offset + domain_master_key["cbMasterKey"]] + plaintext_key_sha1 = SHA1.new(plaintext_key).digest() + + return MasterKey( + guid=self.masterkey_guid, + masterkey_type=self.masterkey_type, + encrypted_key_usercred=self.master_key, + encrypted_key_backup=self.domain_backup_key.raw_bytes, + plaintext_key=plaintext_key, + plaintext_key_sha1=plaintext_key_sha1, + backup_key_guid=backup_key.guid, + ) diff --git a/libs/nemesis_dpapi/nemesis_dpapi/eventing.py b/libs/nemesis_dpapi/nemesis_dpapi/eventing.py new file mode 100644 index 0000000..5ae9f19 --- /dev/null +++ b/libs/nemesis_dpapi/nemesis_dpapi/eventing.py @@ -0,0 +1,219 @@ +"""Main DPAPI manager class.""" + +import asyncio +import json +from abc import abstractmethod +from datetime import UTC, datetime +from logging import getLogger +from typing import get_args +from uuid import UUID + +from dapr.clients import DaprClient +from dapr.clients.grpc._response import TopicEventResponse, TopicEventResponseStatus +from dapr.clients.grpc.subscription import SubscriptionMessage +from pydantic import Field, field_validator, model_validator + +from nemesis_dpapi.core import BaseModel +from nemesis_dpapi.keys import DpapiSystemCredential, NtlmHash, Password, Pbkdf2Hash, Sha1Hash +from nemesis_dpapi.types import Sid + +logger = getLogger(__name__) + + +class NewEncryptedMasterKeyEvent(BaseModel): + """Event emitted when a new encrypted master key is added""" + + masterkey_guid: UUID + timestamp: datetime = Field(default_factory=lambda: datetime.now(UTC)) + + +class NewPlaintextMasterKeyEvent(BaseModel): + """Event emitted when a new plaintext master key is added""" + + masterkey_guid: UUID + timestamp: datetime = Field(default_factory=lambda: datetime.now(UTC)) + + +class NewDomainBackupKeyEvent(BaseModel): + """Event emitted when a new domain backup key is added""" + + backup_key_guid: UUID + timestamp: datetime = Field(default_factory=lambda: datetime.now(UTC)) + + +class NewDpapiSystemCredentialEvent(BaseModel): + """Event emitted when a new DPAPI system credential is added""" + + credential: DpapiSystemCredential + timestamp: datetime = Field(default_factory=lambda: datetime.now(UTC)) + + +class NewPasswordDerivedCredentialEvent(BaseModel): + """Event emitted when a new password-derived credential is added. This includes + Password, NTLM hash, SHA1 hash, and PBKDF2 hash credentials.""" + + type: str + credential: Password | NtlmHash | Sha1Hash | Pbkdf2Hash + user_sid: Sid | None = None + timestamp: datetime = Field(default_factory=lambda: datetime.now(UTC)) + + @field_validator("type") + @classmethod + def validate_type_matches_credential(cls, v, info): + """Validate that the type field matches the credential class name.""" + if "credential" in info.data: + credential = info.data["credential"] + expected_type = credential.__class__.__name__ + if v != expected_type: + raise ValueError(f"Type '{v}' does not match credential class name '{expected_type}'") + return v + + +type DpapiEvent = ( + NewEncryptedMasterKeyEvent + | NewPlaintextMasterKeyEvent + | NewDomainBackupKeyEvent + | NewDpapiSystemCredentialEvent + | NewPasswordDerivedCredentialEvent +) + + +DAPR_PUBSUB_NAME = "broadcast" +DAPR_DPAPI_EVENT_TOPIC = "dpapi_events" + +DPAPI_EVENT_CLASSES = {cls.__name__: cls for cls in get_args(DpapiEvent.__value__)} + +subscription_started = False + + +class TypedDpapiEvent(BaseModel): + """Wrapper class for DpapiEvent with type information for deserialization.""" + + type_name: str + evnt: DpapiEvent + + @model_validator(mode="before") + @classmethod + def deserialize_event(cls, data: dict) -> dict: + if isinstance(data, dict) and "type_name" in data and "evnt" in data: + event_class = DPAPI_EVENT_CLASSES.get(data["type_name"]) + if event_class and isinstance(data["evnt"], dict): + data["evnt"] = event_class(**data["evnt"]) + return data + + +class DpapiObserver: + """Base class for DPAPI event observers.""" + + @abstractmethod + async def update(self, event: DpapiEvent) -> None: + """Called when an observed event occurs.""" + pass + + +class DpapiEventPublisher: + """Abstract base class for DPAPI event publishers.""" + + @abstractmethod + async def register_subscriber(self, observer: DpapiObserver) -> None: + """Attach an observer to this publisher.""" + pass + + @abstractmethod + async def publish_event(self, event: DpapiEvent) -> None: + """Publish an event to all subscribed observers.""" + pass + + +class InMemoryPublisher(DpapiEventPublisher): + """In-memory publisher using the observer pattern.""" + + def __init__(self): + self._observers: list[DpapiObserver] = [] + + async def register_subscriber(self, observer: DpapiObserver) -> None: + """Attach an observer to this publisher.""" + if observer not in self._observers: + self._observers.append(observer) + + async def publish_event(self, event: DpapiEvent) -> None: + """Notify all observers of an event.""" + for observer in self._observers: + await observer.update(event) + + +class DaprDpapiEventPublisher(DpapiEventPublisher): + """DPAPI event publisher using Dapr pub/sub.""" + + def __init__(self, dapr_client: DaprClient, loop: asyncio.AbstractEventLoop | None = None): + self._dapr_client = dapr_client + self._observers: list[DpapiObserver] = [] + self._background_task = None + self._loop = loop if loop else asyncio.get_running_loop() + + async def register_subscriber(self, observer: DpapiObserver) -> None: + """Attach an observer and start the Dapr subscription if not already started.""" + + logger.debug(f"Subscribing Dapr observer: {observer.__class__.__name__}") + self._observers.append(observer) + + global subscription_started + if not subscription_started: + subscription_started = True + self._background_task = asyncio.create_task(self._start_subscription()) + + async def publish_event(self, event: DpapiEvent) -> None: + """Publish an event to all subscribed observers via Dapr pub/sub.""" + + event_type = event.__class__.__name__ + new_event = TypedDpapiEvent(type_name=event_type, evnt=event) + + logger.debug(f"Publishing event of type {event_type} to Dapr") + self._dapr_client.publish_event( + pubsub_name=DAPR_PUBSUB_NAME, + topic_name=DAPR_DPAPI_EVENT_TOPIC, + data=new_event.model_dump_json(), + data_content_type="application/json", + ) + + def process_message(self, evnt: SubscriptionMessage) -> TopicEventResponse: + """Process incoming Dapr pub/sub messages.""" + + logger.debug(f"Processing event of type {evnt.type()}. JSON: {json.dumps(evnt.data())}") + + # type_name = evnt.type() + typed_dpapi_event_dict = evnt.data() + if not isinstance(typed_dpapi_event_dict, dict): + logger.error(f"Received event data is not a dictionary: {typed_dpapi_event_dict}") + return TopicEventResponse(TopicEventResponseStatus.drop) + + typed_dpapi_event = TypedDpapiEvent(**typed_dpapi_event_dict) + + # convert the dict to the appropriate event class using DPAPI_EVENT_CLASSES + event_class = DPAPI_EVENT_CLASSES.get(typed_dpapi_event.type_name) + if event_class: + dpapi_event = event_class(**typed_dpapi_event_dict["evnt"]) + else: + logger.error(f"Unknown event type received: {typed_dpapi_event.type_name}") + return TopicEventResponse(TopicEventResponseStatus.drop) + + for observer in self._observers: + asyncio.run_coroutine_threadsafe(observer.update(dpapi_event), self._loop) + + return TopicEventResponse(TopicEventResponseStatus.success) + + async def _start_subscription(self) -> None: + """Start the Dapr client (if needed).""" + + logger.info("Starting Dapr subscriber handler...") + close_fn = self._dapr_client.subscribe_with_handler( + pubsub_name=DAPR_PUBSUB_NAME, + topic=DAPR_DPAPI_EVENT_TOPIC, + handler_fn=self.process_message, + # dead_letter_topic="TOPIC_A_DEAD", + ) + + # wait indefinitely + await asyncio.Event().wait() + + close_fn() diff --git a/libs/nemesis_dpapi/nemesis_dpapi/exceptions.py b/libs/nemesis_dpapi/nemesis_dpapi/exceptions.py new file mode 100644 index 0000000..5333f8a --- /dev/null +++ b/libs/nemesis_dpapi/nemesis_dpapi/exceptions.py @@ -0,0 +1,88 @@ +"""Custom exceptions for DPAPI operations.""" + +from uuid import UUID + + +class DpapiError(Exception): + """Base exception for DPAPI operations.""" + + pass + + +class MasterKeyNotFoundError(DpapiError): + """Raised when required masterkey is not present.""" + + def __init__(self, masterkey_guid: UUID) -> None: + super().__init__(f"Masterkey {masterkey_guid} not found") + self.masterkey_guid = masterkey_guid + + +class MasterKeyNotDecryptedError(DpapiError): + """Raised when masterkey exists but plaintext not available.""" + + def __init__(self, masterkey_guid: UUID) -> None: + super().__init__(f"Masterkey {masterkey_guid} found but not decrypted") + self.masterkey_guid = masterkey_guid + + +class StorageError(DpapiError): + """Raised when storage backend operations fail.""" + + pass + + +class DpapiCryptoError(DpapiError): + """Base exception for DPAPI cryptographic operations.""" + + pass + + +class InvalidBackupKeyError(DpapiCryptoError): + """Raised when domain backup key is invalid or malformed.""" + + pass + + +class MasterKeyDecryptionError(DpapiCryptoError): + """Raised when masterkey decryption fails.""" + + pass + + +class BlobParsingError(DpapiError): + """Raised when DPAPI blob data is invalid or malformed.""" + + pass + + +class BlobDecryptionError(DpapiError): + """Raised when DPAPI blob decryption fails.""" + + pass + + +class WriteOnceViolationError(StorageError): + """Raised when attempting to modify a field that already has a value (write-once semantics). + + This exception is raised when an upsert operation tries to change a field that already + contains a non-NULL value. Write-once semantics ensure that once a field is set to a + non-NULL value, it cannot be changed to a different value. + """ + + def __init__(self, entity_type: str, entity_id: str, fields: list[str]) -> None: + """Initialize WriteOnceViolationError. + + Args: + entity_type: The type of entity (e.g., "masterkey", "backup_key") + entity_id: The identifier of the entity (e.g., GUID) + fields: List of field names that have write-once violations + """ + self.entity_type = entity_type + self.entity_id = entity_id + self.fields = fields + + fields_str = ", ".join(f"'{f}'" for f in fields) + super().__init__( + f"Write-once violation for {entity_type} {entity_id}: " + f"field(s) {fields_str} already have values and cannot be modified" + ) diff --git a/libs/nemesis_dpapi/nemesis_dpapi/keys.py b/libs/nemesis_dpapi/nemesis_dpapi/keys.py new file mode 100644 index 0000000..a3e9773 --- /dev/null +++ b/libs/nemesis_dpapi/nemesis_dpapi/keys.py @@ -0,0 +1,504 @@ +"""DPAPI cryptographic operations.""" + +from __future__ import annotations + +import struct +from enum import Enum +from typing import TYPE_CHECKING +from uuid import UUID # noqa: TC003 - need for pydantic + +from Crypto.Hash import HMAC, MD4, SHA1, SHA256 +from Crypto.Protocol.KDF import PBKDF2 +from impacket.dpapi import PRIVATE_KEY_BLOB +from pydantic import BaseModel, ConfigDict, field_serializer, field_validator + +if TYPE_CHECKING: + from .types import Sid + + +# PVK file format constants +PVK_MAGIC = 0xB0B5F11E +PVK_FILE_VERSION_0 = 0 +PVK_NO_ENCRYPT = 0 +MAX_PVK_FILE_LEN = 4096 + + +class PvkFileHeader(BaseModel): + """PVK file header structure. + + Based on the Microsoft PVK file format: + typedef struct _FILE_HDR { + DWORD dwMagic; + DWORD dwVersion; + DWORD dwKeySpec; + DWORD dwEncryptType; + DWORD cbEncryptData; + DWORD cbPvk; + } FILE_HDR, *PFILE_HDR; + """ + + model_config = ConfigDict(frozen=True) + + magic: int # Should be PVK_MAGIC (0xb0b5f11e) + version: int # Should be PVK_FILE_VERSION_0 (0) + key_spec: int # Key specification + encrypt_type: int # Encryption type (PVK_NO_ENCRYPT = 0) + encrypt_data_size: int # Size of encrypted data + pvk_size: int # Size of private key data + + encrypted_data: bytes + private_key: bytes + + @classmethod + def parse(cls, data: bytes) -> PvkFileHeader: + """Parse PVK file header from bytes. + + Args: + data: Raw bytes containing the PVK file header and key data + + Returns: + Parsed PvkFileHeader instance + + Raises: + ValueError: If data is too short or header is invalid + """ + if len(data) < 24: + raise ValueError(f"Data too short for PVK header: {len(data)} bytes, need at least 24") + + magic, version, key_spec, encrypt_type, encrypt_data_size, pvk_size = struct.unpack("<6I", data[:24]) + + if magic != PVK_MAGIC: + raise ValueError(f"Invalid PVK magic: 0x{magic:08x}, expected 0x{PVK_MAGIC:08x}") + + if version != PVK_FILE_VERSION_0: + raise ValueError(f"Invalid PVK version: {version}, expected {PVK_FILE_VERSION_0}") + + if encrypt_data_size > MAX_PVK_FILE_LEN: + raise ValueError(f"Encrypted data size too large: {encrypt_data_size}, max {MAX_PVK_FILE_LEN}") + + if pvk_size == 0 or pvk_size > MAX_PVK_FILE_LEN: + raise ValueError(f"Invalid PVK size: {pvk_size}, must be 1-{MAX_PVK_FILE_LEN}") + + # Parse encrypted data (if present) and private key + offset = 24 + + # Extract encrypted data if present + if encrypt_data_size > 0: + if len(data) < offset + encrypt_data_size: + raise ValueError(f"Data too short for encrypted data: need {offset + encrypt_data_size} bytes") + encrypted_data = data[offset : offset + encrypt_data_size] + offset += encrypt_data_size + else: + encrypted_data = b"" + + # Extract private key data + if len(data) < offset + pvk_size: + raise ValueError(f"Data too short for private key: need {offset + pvk_size} bytes") + private_key = data[offset : offset + pvk_size] + + return cls( + magic=magic, + version=version, + key_spec=key_spec, + encrypt_type=encrypt_type, + encrypt_data_size=encrypt_data_size, + pvk_size=pvk_size, + encrypted_data=encrypted_data, + private_key=private_key, + ) + + +class Password(BaseModel): + """Password credential.""" + + value: str + + @field_validator("value") + @classmethod + def validate_value(cls, v: str) -> str: + if not v: + raise ValueError("Password value cannot be empty") + return v + + +class NtlmHash(BaseModel): + """NTLM hash credential.""" + + value: bytes + + @field_validator("value") + @classmethod + def validate_value(cls, v: bytes) -> bytes: + if not v: + raise ValueError("NTLM hash value cannot be empty") + if len(v) != 16: + raise ValueError("NTLM hash must be exactly 16 bytes") + return v + + @classmethod + def from_hexstring(cls, hex_string: str) -> NtlmHash: + """Create NtlmHash from hex string.""" + try: + return cls(value=bytes.fromhex(hex_string)) + except ValueError as e: + raise ValueError(f"Invalid hex string: {e}") from e + + +class Sha1Hash(BaseModel): + """SHA1 hash credential.""" + + value: bytes + + @field_validator("value") + @classmethod + def validate_value(cls, v: bytes) -> bytes: + if not v: + raise ValueError("SHA1 hash value cannot be empty") + if len(v) != 20: + raise ValueError("SHA1 hash must be exactly 20 bytes") + return v + + @classmethod + def from_hex(cls, hex_string: str) -> Sha1Hash: + """Create Sha1Hash from hex string.""" + try: + return cls(value=bytes.fromhex(hex_string)) + except ValueError as e: + raise ValueError(f"Invalid hex string: {e}") from e + + +class Pbkdf2Hash(BaseModel): + """PBKDF2 hash credential.""" + + value: bytes + + @field_validator("value") + @classmethod + def validate_value(cls, v: bytes) -> bytes: + if not v: + raise ValueError("PBKDF2 hash value cannot be empty") + if len(v) != 16: + raise ValueError("PBKDF2 hash must be exactly 16 bytes") + return v + + @classmethod + def from_hex(cls, hex_string: str) -> Pbkdf2Hash: + """Create Pbkdf2Hash from hex string.""" + try: + return cls(value=bytes.fromhex(hex_string)) + except ValueError as e: + raise ValueError(f"Invalid hex string: {e}") from e + + +def _derive_secure_cred_key(ntlm_hash: bytes, user_sid_bytes: bytes) -> bytes: + """Compute PBKDF2 hash using two-step derivation process.""" + derived_key = PBKDF2(ntlm_hash, user_sid_bytes, dkLen=32, count=10000, hmac_hash_module=SHA256) # type: ignore + derived_key = PBKDF2(derived_key, user_sid_bytes, dkLen=16, count=1, hmac_hash_module=SHA256) # type: ignore + return derived_key + + +class CredKeyHashType(Enum): + """Type of one-way function (OWF) hashes used in credential key derivation.""" + + MD4 = "md4" # MD4 hash (16 bytes) + NTLM = "md4" # Alias for MD4 + SHA1 = "sha1" # SHA1 hash (20 bytes) + PBKDF2 = "pbkdf2" # "Secure" Cred Key, 16 bytes derived from PBKDF2 + SECURE_CRED_KEY = "pbkdf2" # Alias for PBKDF2 + + +class CredKey(BaseModel): + """Credential key derived from OWF hash.""" + + key: NtlmHash | Sha1Hash | Pbkdf2Hash + + @property + def owf(self) -> CredKeyHashType: + """Get the OWF type inferred from the hash type.""" + if isinstance(self.key, NtlmHash): + return CredKeyHashType.NTLM + elif isinstance(self.key, Sha1Hash): + return CredKeyHashType.SHA1 + elif isinstance(self.key, Pbkdf2Hash): + return CredKeyHashType.PBKDF2 + else: + raise ValueError(f"Cannot infer OWF type from key type: {type(self.key)}") + + @classmethod + def from_password(cls, password: str, hash_type: CredKeyHashType, user_sid: Sid | None = None) -> CredKey: + """Create CredKey from password by calculating the specified hash type. + + Args: + password: The user's password + hash_type: The type of hash to compute (NTLM, SHA1, PBKDF2) + user_sid: (Optional) The user's SID. Only required for PBKDF2 derivation. + + Returns: + CredKey object with the computed hash + + Raises: + ValueError: If parameters are invalid or unsupported hash type + + Note: + Derived from SharpDPAPI's CalculateKeys function: https://github.com/GhostPack/SharpDPAPI/blob/master/SharpDPAPI/lib/Dpapi.cs#L1755 + """ + + if hash_type in (CredKeyHashType.MD4, CredKeyHashType.NTLM): + ntlm_hash = MD4.new(password.encode("utf-16le")).digest() + return cls(key=NtlmHash(value=ntlm_hash)) + elif hash_type == CredKeyHashType.SHA1: + sha1_hash = SHA1.new(password.encode("utf-16le")).digest() + return cls(key=Sha1Hash(value=sha1_hash)) + elif hash_type == CredKeyHashType.PBKDF2: + if user_sid is None: + raise ValueError("user_sid parameter is required when using PBKDF2") + + user_sid_bytes = user_sid.encode("utf-16le") + ntlm_hash = MD4.new(password.encode("utf-16le")).digest() + + derived_key = _derive_secure_cred_key(ntlm_hash, user_sid_bytes) + + return cls(key=Pbkdf2Hash(value=derived_key)) + else: + raise ValueError(f"Unsupported hash type: {hash_type}") + + @classmethod + def from_ntlm(cls, ntlm_hash: bytes, hash_type: CredKeyHashType, user_sid: Sid | None = None) -> CredKey: + """Create CredKey from NTLM hash.""" + + if hash_type in (CredKeyHashType.MD4, CredKeyHashType.NTLM): + return cls(key=NtlmHash(value=ntlm_hash)) + elif hash_type == CredKeyHashType.PBKDF2: + if user_sid is None: + raise ValueError("user_sid parameter is required when using PBKDF2") + + user_sid_bytes = user_sid.encode("utf-16le") + derived_key = _derive_secure_cred_key(ntlm_hash, user_sid_bytes) + + return cls(key=Pbkdf2Hash(value=derived_key)) + else: + raise ValueError(f"Cannot derive {hash_type} from NTLM hash") + + @classmethod + def from_sha1(cls, sha1_hash: bytes) -> CredKey: + """Create CredKey from SHA1 hash.""" + return cls(key=Sha1Hash(value=sha1_hash)) + + @classmethod + def from_pbkdf2(cls, pbkdf2_hash: bytes) -> CredKey: + """Create CredKey from PBKDF2 hash.""" + return cls(key=Pbkdf2Hash(value=pbkdf2_hash)) + + +class MasterKeyEncryptionKey(BaseModel): + """Symmetric encryption key derived from credential key.""" + + key: Sha1Hash + + @staticmethod + def _derive_mk_key(pwdhash: bytes, user_sid: Sid, digest: str = "sha1") -> bytes: + """Internal use. Computes the DPAPI symmetric key from a hash derived from a user's password.""" + # Map digest names to pycryptodome hash modules + digest_map = { + "sha1": SHA1, + "sha256": SHA256, + "md4": MD4, + } + + if digest not in digest_map: + raise ValueError(f"Unsupported digest algorithm: {digest}") + + user_sid_bytes = user_sid.encode("utf-16le") + b"\0\0" + + return HMAC.new(pwdhash, user_sid_bytes, digestmod=digest_map[digest]).digest() + + @classmethod + def from_cred_key(cls, cred_key: CredKey, user_sid: Sid) -> MasterKeyEncryptionKey: + """Generate symmetric key from credential key using derivation algorithm. + + Args: + cred_key: The credential key containing hash + user_sid: The user SID (used in the key derivation) + """ + if cred_key.owf in (CredKeyHashType.MD4, CredKeyHashType.NTLM): + if not isinstance(cred_key.key, NtlmHash): + raise ValueError("Expected NtlmHash for MD4/NTLM key type") + key = cls._derive_mk_key(cred_key.key.value, user_sid, digest="sha1") + elif cred_key.owf == CredKeyHashType.SHA1: + if not isinstance(cred_key.key, Sha1Hash): + raise ValueError("Expected Sha1Hash for SHA1 key type") + key = cls._derive_mk_key(cred_key.key.value, user_sid, digest="sha1") + elif cred_key.owf == CredKeyHashType.PBKDF2: + if not isinstance(cred_key.key, Pbkdf2Hash): + raise ValueError("Expected Pbkdf2Hash for PBKDF2 key type") + + key = cls._derive_mk_key(cred_key.key.value, user_sid, digest="sha1") + else: + raise ValueError(f"Invalid hash_type: {cred_key.owf}") + + return cls(key=Sha1Hash(value=key)) + + @classmethod + def from_dpapi_system_cred(cls, dpapi_system_key: bytes) -> MasterKeyEncryptionKey: + """Generate symmetric key from DPAPI_SYSTEM credential. + + Args: + dpapi_system_key: The DPAPI_SYSTEM key bytes + """ + return cls(key=Sha1Hash(value=dpapi_system_key)) + + +class DomainBackupKey(BaseModel): + """Represents a domain backup key for decrypting masterkeys.""" + + model_config = {"frozen": True} + + guid: UUID + key_data: bytes + domain_controller: str | None = None + + @field_validator("key_data") + @classmethod + def validate_key_data(cls, v: bytes) -> bytes: + """Validate that key_data contains a correctly formatted domain backup key. + + A valid domain backup key should: + 1. Be at least large enough to contain a PVK file header + 2. Have a valid PVK file header structure + 3. Have a valid PRIVATE_KEY_BLOB structure following the header + + Args: + v: The key_data bytes to validate + + Returns: + The validated key_data bytes + + Raises: + ValueError: If the key_data is not a valid domain backup key + """ + if not isinstance(v, bytes): + raise ValueError("key_data must be bytes") + + # Check minimum size - PVK header is 24 bytes + pvk_header_size = 24 + if len(v) < pvk_header_size: + raise ValueError( + f"key_data too short: {len(v)} bytes, minimum {pvk_header_size} bytes required for PVK header" + ) + + try: + # Validate PVK header and full structure can be parsed + # This validates magic number, version, sizes, and private key blob + header = PvkFileHeader.parse(v) + except Exception as e: + raise ValueError(f"Invalid PVK file header: {e}") from e + + try: + # Validate PRIVATE_KEY_BLOB can be parsed from the private key data + PRIVATE_KEY_BLOB(header.private_key) + except Exception as e: + raise ValueError(f"Invalid private key blob: {e}") from e + + return v + + +class DpapiSystemCredential(BaseModel): + """Represents the DPAPI_SYSTEM LSA secret key for decrypting machine-protected masterkeys.""" + + model_config = ConfigDict(frozen=True) + + user_key: bytes + machine_key: bytes + + @field_validator("user_key", "machine_key", mode="before") + @classmethod + def deserialize_hex_to_bytes(cls, v: bytes | str) -> bytes: + """Deserialize hex strings back to bytes.""" + if isinstance(v, str): + return bytes.fromhex(v) + return v + + @field_serializer("user_key", "machine_key") + def serialize_bytes_as_hex(self, value: bytes) -> str: + """Serialize bytes fields as hex strings for JSON serialization.""" + return value.hex() + + def model_dump(self, **kwargs): + """Override to serialize bytes as hex strings.""" + data = super().model_dump(**kwargs) + data["user_key"] = self.user_key.hex() + data["machine_key"] = self.machine_key.hex() + return data + + @classmethod + def from_bytes(cls, dpapi_system_data: bytes | str) -> DpapiSystemCredential: + """Create a DpapiSystemCredential from bytes. + + Args: + dpapi_system_data (bytes | str): 40-byte DPAPI_SYSTEM LSA secret + (as raw bytes or hex string). + + Returns: + DpapiSystemKey: A new instance created from the given secret. + + Raises: + ValueError: If dpapi_system_data is not exactly 40 bytes or + 80 hex characters. + + Note: + For creating a DpapiSystemCredential from the bytes of the + DPAPI_SYSTEM LSA secret, use the from_lsa_secret method instead. + """ + + if isinstance(dpapi_system_data, str): + try: + dpapi_system_bytes = bytes.fromhex(dpapi_system_data) + except ValueError as e: + raise ValueError(f"Invalid hex string: {e}") from e + else: + dpapi_system_bytes = dpapi_system_data + + if len(dpapi_system_bytes) != 40: + raise ValueError(f"DPAPI_SYSTEM must be exactly 40 bytes, got {len(dpapi_system_bytes)}") + + # Split into machine (first 20 bytes) and user (last 20 bytes) components + machine_key_bytes = dpapi_system_bytes[:20] + user_key_bytes = dpapi_system_bytes[20:] + + return cls(user_key=user_key_bytes, machine_key=machine_key_bytes) + + @classmethod + def from_lsa_secret(cls, lsa_secret_bytes: bytes | str) -> DpapiSystemCredential: + """Create DpapiSystemSecret from the DPAPI_SYSTEM LSA secret. + + Args: + lsa_secret_bytes: LSA secret structure containing version and keys (as bytes or hex string) + + Returns: + DpapiSystemSecret instance + + Raises: + ValueError: If structure is invalid or missing required data + """ + # Convert hex string to bytes if needed + if isinstance(lsa_secret_bytes, str): + try: + lsa_secret_data = bytes.fromhex(lsa_secret_bytes) + except ValueError as e: + raise ValueError(f"Invalid hex string: {e}") from e + else: + lsa_secret_data = lsa_secret_bytes + + if len(lsa_secret_data) != 44: # 4 + 20 + 20 = minimum structure size + raise ValueError(f"Incorrect LSA secret size, expected at least 44 bytes, got {len(lsa_secret_data)}") + + try: + # Parse structure: Version (4 bytes), MachineKey (20 bytes), UserKey (20 bytes) + version, machine_key, user_key = struct.unpack(" None: + """Initialize DPAPI manager with specified storage backend. + + Args: + storage_backend: Either "memory" for in-memory storage or an asyncpg.Pool object + for PostgreSQL database storage. + auto_decrypt: Enable automatic masterkey decryption as new domain backup keys are added. + """ + super().__init__() + self._storage_backend = storage_backend + self._initialized = False + self._auto_decrypt = auto_decrypt + + # Storage-related fields + self._masterkey_repo: MasterKeyRepository + self._backup_key_repo: DomainBackupKeyRepository + self._dpapi_system_cred_repo: DpapiSystemCredentialRepository + self._pg_pool: asyncpg.Pool | None = None + + if publisher is None: + self._publisher = InMemoryPublisher() + else: + self._publisher = publisher + + # Auto-decryption observer will be set up during async initialization + self._auto_decrypt_observer: AutoDecryptionObserver | None = None + + async def _initialize_storage(self) -> None: + """Initialize storage repositories based on backend type.""" + if self._storage_backend == "memory": + self._masterkey_repo = InMemoryMasterKeyRepository() + self._backup_key_repo = InMemoryDomainBackupKeyRepository() + self._dpapi_system_cred_repo = InMemoryDpapiSystemCredentialRepository() + elif isinstance(self._storage_backend, asyncpg.Pool): + # Use provided PostgreSQL connection pool + self._pg_pool = self._storage_backend + + self._masterkey_repo = PostgresMasterKeyRepository(self._pg_pool) + self._backup_key_repo = PostgresDomainBackupKeyRepository(self._pg_pool) + self._dpapi_system_cred_repo = PostgresDpapiSystemCredentialRepository(self._pg_pool) + else: + raise ValueError(f"Unsupported storage backend: {self._storage_backend}. Must be 'memory' or asyncpg.Pool") + + # Set up auto-decryption observer after storage is initialized + if self._auto_decrypt and self._auto_decrypt_observer is None: + self._auto_decrypt_observer = AutoDecryptionObserver(self) + await self._publisher.register_subscriber(self._auto_decrypt_observer) + + self._initialized = True + + async def __aenter__(self) -> Self: + """Async context manager entry.""" + await self._initialize_storage() + return self + + async def __aexit__(self, exc_type, exc_val, exc_tb) -> None: + """Async context manager exit.""" + pass + + async def subscribe(self, observer: DpapiObserver) -> None: + """Subscribe an observer to DPAPI events. + + Args: + observer: Observer implementing the update(event) method + """ + await self._publisher.register_subscriber(observer) + + async def upsert_masterkey( + self, + masterkey: MasterKey, + ) -> None: + """Add or update a masterkey (encrypted or plaintext). + + This method enforces write-once semantics: once a field is set to a non-NULL value, + it cannot be changed to a different value. Fields can only be written once. + + Args: + masterkey: MasterKey object to add or update + + Raises: + ValueError: If plaintext_key and plaintext_key_sha1 don't match + WriteOnceViolationError: If attempting to modify fields that already have values + """ + if not self._initialized: + await self._initialize_storage() + + # Validate and calculate SHA1 hash + # This ensures integrity: if plaintext_key is provided, SHA1 is calculated and verified + validated_sha1 = validate_and_calculate_sha1( + masterkey.plaintext_key, + masterkey.plaintext_key_sha1, + ) + + # Create updated masterkey with validated SHA1 if it changed + if validated_sha1 != masterkey.plaintext_key_sha1: + new_masterkey = masterkey.model_copy( + update={"plaintext_key_sha1": validated_sha1}, + ) + else: + new_masterkey = masterkey + + # Check write-once constraints against existing record + existing_list = await self._masterkey_repo.get_masterkeys(guid=new_masterkey.guid) + if existing_list: + existing = existing_list[0] + conflicts = check_write_once_conflicts( + existing, + new_masterkey, + fields=[ + "encrypted_key_usercred", + "encrypted_key_backup", + "plaintext_key", + "plaintext_key_sha1", + "backup_key_guid", + "masterkey_type", + ], + ) + if conflicts: + raise WriteOnceViolationError("masterkey", str(new_masterkey.guid), conflicts) + + # Call repository to perform the upsert (repository layer also enforces write-once at SQL level) + await self._masterkey_repo.upsert_masterkey(new_masterkey) + + # Publish appropriate event based on what was added + if new_masterkey.plaintext_key or new_masterkey.plaintext_key_sha1: + await self._publisher.publish_event(NewPlaintextMasterKeyEvent(masterkey_guid=new_masterkey.guid)) + elif new_masterkey.encrypted_key_usercred or new_masterkey.encrypted_key_backup: + await self._publisher.publish_event(NewEncryptedMasterKeyEvent(masterkey_guid=new_masterkey.guid)) + + async def get_masterkeys( + self, + guid: UUID | None = None, + encryption_filter: EncryptionFilter = EncryptionFilter.ALL, + backup_key_guid: UUID | None = None, + masterkey_type: list[MasterKeyType] | None = None, + ) -> list[MasterKey]: + """Retrieve masterkey(s) with optional filtering. + + Args: + guid: Optional specific masterkey GUID to retrieve. If provided, returns a list with one MasterKey or empty list. + encryption_filter: Filter by decryption status (default: ALL). Ignored if guid is provided. + backup_key_guid: Filter by backup key GUID (default: None for all). Ignored if guid is provided. + masterkey_type: Filter by user account types (default: None for all). Ignored if guid is provided. + + Returns: + A list of MasterKeys (empty list if no matches) + """ + if not self._initialized: + await self._initialize_storage() + return await self._masterkey_repo.get_masterkeys(guid, encryption_filter, backup_key_guid, masterkey_type) + + async def upsert_domain_backup_key(self, backup_key: DomainBackupKey) -> int: + """Add or update a domain backup key and decrypt all compatible masterkeys. + + This method enforces write-once semantics: once a field is set to a non-NULL value, + it cannot be changed to a different value. Fields can only be written once. + + Args: + backup_key: Domain backup key to add or update + + Returns: + The ID of the inserted or updated backup key + + Raises: + ValueError: If domain_controller is an empty string + WriteOnceViolationError: If attempting to modify fields that already have values + """ + + if not self._initialized: + await self._initialize_storage() + + # Validate that domain_controller is not an empty string (NULL or non-empty only) + validate_no_empty_string(backup_key.domain_controller, "domain_controller") + + # Check write-once constraints against existing record + existing_list = await self._backup_key_repo.get_backup_keys(guid=backup_key.guid) + if existing_list: + existing = existing_list[0] + conflicts = check_write_once_conflicts( + existing, + backup_key, + fields=["key_data", "domain_controller"], + ) + if conflicts: + raise WriteOnceViolationError("backup_key", str(backup_key.guid), conflicts) + + # Call repository to perform the upsert (repository layer also enforces write-once at SQL level) + backup_key_id = await self._backup_key_repo.upsert_backup_key(backup_key) + + await self._publisher.publish_event(NewDomainBackupKeyEvent(backup_key_guid=backup_key.guid)) + + return backup_key_id + + async def get_backup_keys(self, guid: UUID | None = None) -> list[DomainBackupKey]: + """Retrieve domain backup key(s). + + Args: + guid: Optional specific backup key GUID to retrieve. If provided, returns a list with one key or empty list. + + Returns: + A list of DomainBackupKey objects (empty list if no matches) + """ + if not self._initialized: + await self._initialize_storage() + + return await self._backup_key_repo.get_backup_keys(guid) + + async def upsert_system_credential(self, cred: DpapiSystemCredential) -> None: + """Add or update a DPAPI system credential. + + Args: + cred: DPAPI system credential to add or update + """ + if not self._initialized: + await self._initialize_storage() + + await self._dpapi_system_cred_repo.upsert_credential(cred) + await self._publisher.publish_event(NewDpapiSystemCredentialEvent(credential=cred)) + + async def get_system_credentials(self, guid: UUID | None = None) -> list[DpapiSystemCredential]: + """Retrieve DPAPI system credential(s). + + Args: + guid: Optional specific credential GUID to retrieve. If provided, returns a list with one credential or empty list. + + Returns: + A list of DpapiSystemCredential objects (empty list if no matches) + """ + if not self._initialized: + await self._initialize_storage() + + if guid is not None: + credential = await self._dpapi_system_cred_repo.get_credential(guid) + return [credential] if credential else [] + + return await self._dpapi_system_cred_repo.get_all_credentials() + + async def decrypt_blob(self, blob: Blob, entropy: bytes | None = None) -> bytes: + """Decrypt a DPAPI blob using available masterkeys. + + Args: + blob: DPAPI blob to decrypt + + Returns: + Decrypted blob data + + Raises: + MasterKeyNotFoundError: If required masterkey is not available + MasterKeyNotDecryptedError: If masterkey exists but is not decrypted + DPAPIBlobDecryptionError: If blob decryption fails + """ + if not self._initialized: + await self._initialize_storage() + + # Find the required masterkey + masterkeys = await self._masterkey_repo.get_masterkeys(guid=blob.masterkey_guid) + if not masterkeys: + raise MasterKeyNotFoundError(blob.masterkey_guid) + + masterkey = masterkeys[0] + if not masterkey.is_decrypted: + raise MasterKeyNotDecryptedError(blob.masterkey_guid) + + return blob.decrypt(masterkey, entropy) diff --git a/libs/nemesis_dpapi/nemesis_dpapi/masterkey_decryptor.py b/libs/nemesis_dpapi/nemesis_dpapi/masterkey_decryptor.py new file mode 100644 index 0000000..7be1eef --- /dev/null +++ b/libs/nemesis_dpapi/nemesis_dpapi/masterkey_decryptor.py @@ -0,0 +1,125 @@ +import asyncio +from time import perf_counter + +from common.logger import get_logger + +from .core import MasterKeyType +from .exceptions import MasterKeyDecryptionError +from .keys import CredKey, CredKeyHashType, MasterKeyEncryptionKey, NtlmHash, Password, Pbkdf2Hash, Sha1Hash +from .manager import DpapiManager, EncryptionFilter +from .types import Sid + +logger = get_logger(__name__) + + +class MasterKeyDecryptorService: + """Handles DPAPI master key decryption with background task processing.""" + + def __init__(self, dpapi_manager: DpapiManager): + self.dpapi_manager = dpapi_manager + self._background_tasks = set() + + async def process_password_based_credential( + self, + credential: Password | NtlmHash | Sha1Hash | Pbkdf2Hash, + account_sid: Sid, + ) -> dict: + """Handle password, NTLM hash, and cred key credential submissions.""" + + mk_keys_to_try = self._generate_mk_encryption_keys(credential, account_sid) + + task = asyncio.create_task(self._decrypt_masterkeys_background(mk_keys_to_try, type(credential))) + self._background_tasks.add(task) + task.add_done_callback(self._background_tasks.discard) + + return { + "status": "success", + "type": type(credential).__name__, + "message": "Decryption task started", + } + + async def _decrypt_masterkeys_background( + self, mk_keys_to_try: list[MasterKeyEncryptionKey], credential_type: type + ) -> None: + """Perform master key decryption attempts in background.""" + start_time = perf_counter() + try: + logger.info(f"Starting background decryption for credential type: {credential_type.__name__}") + + encrypted_masterkeys = await self.dpapi_manager.get_masterkeys( + encryption_filter=EncryptionFilter.ENCRYPTED_ONLY, + masterkey_type=[MasterKeyType.USER, MasterKeyType.UNKNOWN], + ) + + decrypted_count = 0 + + logger.info(f"Attempting to decrypt {len(encrypted_masterkeys)} encrypted master keys") + for masterkey in encrypted_masterkeys: + if not masterkey.encrypted_key_usercred: + continue + + for mk_key in mk_keys_to_try: + try: + plaintext_mk = masterkey.decrypt(mk_key) + await self.dpapi_manager.upsert_masterkey(plaintext_mk) + decrypted_count += 1 + logger.info( + f"Successfully decrypted master key {masterkey.guid} with {credential_type.__name__}" + ) + break # We decrypted it, no need to try other keys + except MasterKeyDecryptionError as e: + logger.debug(f"Failed to decrypt master key: {e}") + continue + + # TODO: Notify the user that new master keys have been decrypted + elapsed_time = perf_counter() - start_time + logger.info( + f"Background decryption completed. Decrypted {decrypted_count}/({len(encrypted_masterkeys)}) master keys in {elapsed_time:.2f} seconds" + ) + + except Exception as e: + elapsed_time = perf_counter() - start_time + logger.error( + f"Error in background masterkey decryption task. Cred type: {credential_type.__name__}. Error: {e}. Elapsed time: {elapsed_time:.2f} seconds" + ) + + def _generate_mk_encryption_keys( + self, + cred: Password | NtlmHash | Sha1Hash | Pbkdf2Hash, + account_sid: Sid, + ) -> list[MasterKeyEncryptionKey]: + """Generate MasterKeyEncryptionKey objects based on the credential type.""" + cred_keys = [] + + if isinstance(cred, Password): + cred_keys = [ + CredKey.from_password(cred.value, CredKeyHashType.PBKDF2, account_sid), + CredKey.from_password(cred.value, CredKeyHashType.SHA1), + CredKey.from_password(cred.value, CredKeyHashType.NTLM), + ] + elif isinstance(cred, NtlmHash): + cred_keys = [ + CredKey.from_ntlm(cred.value, CredKeyHashType.PBKDF2, account_sid), + CredKey.from_ntlm(cred.value, CredKeyHashType.NTLM), + ] + elif isinstance(cred, Sha1Hash): + cred_keys = [ + CredKey.from_sha1(cred.value), + ] + elif isinstance(cred, Pbkdf2Hash): + cred_keys = [ + CredKey.from_pbkdf2(cred.value), + ] + else: + raise ValueError(f"Unsupported credential type: {cred.type}") + + return [MasterKeyEncryptionKey.from_cred_key(cred, account_sid) for cred in cred_keys] + + async def shutdown(self): + """Cancel all background tasks on shutdown.""" + if self._background_tasks: + logger.info(f"Cancelling {len(self._background_tasks)} background tasks") + for task in self._background_tasks: + task.cancel() + await asyncio.gather(*self._background_tasks, return_exceptions=True) + self._background_tasks.clear() diff --git a/libs/nemesis_dpapi/nemesis_dpapi/null_manager.py b/libs/nemesis_dpapi/nemesis_dpapi/null_manager.py new file mode 100644 index 0000000..4a97332 --- /dev/null +++ b/libs/nemesis_dpapi/nemesis_dpapi/null_manager.py @@ -0,0 +1,56 @@ +"""Null DPAPI manager implementation.""" + +from typing import Any, Self +from uuid import UUID + +from .core import Blob, MasterKey, MasterKeyType +from .exceptions import MasterKeyNotFoundError +from .keys import DomainBackupKey, DpapiSystemCredential +from .protocols import DpapiManagerProtocol +from .repositories import EncryptionFilter + + +class NullDpapiManager(DpapiManagerProtocol): + """Null object implementation of DPAPI manager that does nothing when methods are invoked.""" + + async def __aenter__(self) -> Self: + """Async context manager entry.""" + return self + + async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None: + """Async context manager exit.""" + pass + + async def upsert_masterkey(self, masterkey: MasterKey) -> None: + """Add or update a masterkey (does nothing).""" + pass + + async def upsert_domain_backup_key(self, backup_key: DomainBackupKey) -> int: + """Add or update a domain backup key (does nothing).""" + return 0 + + async def upsert_system_credential(self, cred: DpapiSystemCredential) -> None: + """Add or update a DPAPI system credential (does nothing).""" + pass + + async def decrypt_blob(self, blob: Blob) -> bytes: + """Decrypt a DPAPI blob (always fails).""" + raise MasterKeyNotFoundError(blob.masterkey_guid) + + async def get_masterkeys( + self, + guid: UUID | None = None, + encryption_filter: EncryptionFilter = EncryptionFilter.ALL, + backup_key_guid: UUID | None = None, + masterkey_type: list[MasterKeyType] | None = None, + ) -> list[MasterKey]: + """Retrieve masterkey(s) with optional filtering (always returns empty list).""" + return [] + + async def get_system_credentials(self, guid: UUID | None = None) -> list[DpapiSystemCredential]: + """Retrieve DPAPI system credential(s) (always returns empty list).""" + return [] + + async def get_backup_keys(self, guid: UUID | None = None) -> list[DomainBackupKey]: + """Retrieve domain backup key(s) (always returns empty list).""" + return [] diff --git a/libs/nemesis_dpapi/nemesis_dpapi/protocols.py b/libs/nemesis_dpapi/nemesis_dpapi/protocols.py new file mode 100644 index 0000000..81bda82 --- /dev/null +++ b/libs/nemesis_dpapi/nemesis_dpapi/protocols.py @@ -0,0 +1,65 @@ +"""Protocol definitions for DPAPI components.""" + +from typing import Protocol, Self, runtime_checkable +from uuid import UUID + +from .core import Blob, MasterKey, MasterKeyType +from .keys import DomainBackupKey, DpapiSystemCredential +from .repositories import EncryptionFilter + + +@runtime_checkable +class DpapiManagerProtocol(Protocol): + """Protocol defining the interface for DPAPI managers.""" + + async def __aenter__(self) -> Self: + """Async context manager entry.""" + ... + + async def __aexit__(self, exc_type, exc_val, exc_tb) -> None: + """Async context manager exit.""" + ... + + async def upsert_masterkey(self, masterkey: MasterKey) -> None: + """Add or update a masterkey (encrypted or plaintext).""" + ... + + async def upsert_domain_backup_key(self, backup_key: DomainBackupKey) -> int: + """Add or update a domain backup key.""" + ... + + async def upsert_system_credential(self, cred: DpapiSystemCredential) -> None: + """Add or update a DPAPI system credential.""" + ... + + async def decrypt_blob(self, blob: Blob) -> bytes: + """Decrypt a DPAPI blob using available masterkeys.""" + ... + + async def get_masterkeys( + self, + guid: UUID | None = None, + encryption_filter: EncryptionFilter = EncryptionFilter.ALL, + backup_key_guid: UUID | None = None, + masterkey_type: list[MasterKeyType] | None = None, + ) -> list[MasterKey]: + """Retrieve masterkey(s) with optional filtering. + + Args: + guid: Optional specific masterkey GUID to retrieve. If provided, returns a list with one MasterKey or empty list. + encryption_filter: Filter by decryption status (default: ALL). Ignored if guid is provided. + backup_key_guid: Filter by backup key GUID (default: None for all). Ignored if guid is provided. + masterkey_type: Filter by user account types (default: None for all). Ignored if guid is provided. + + Returns: + A list of MasterKeys (empty list if no matches) + """ + ... + + async def get_system_credentials(self, guid: UUID | None = None) -> list[DpapiSystemCredential]: + """Retrieve DPAPI system credential(s).""" + ... + + async def get_backup_keys(self, guid: UUID | None = None) -> list[DomainBackupKey]: + """Retrieve domain backup key(s).""" + ... diff --git a/libs/nemesis_dpapi/nemesis_dpapi/repositories.py b/libs/nemesis_dpapi/nemesis_dpapi/repositories.py new file mode 100644 index 0000000..c792ff9 --- /dev/null +++ b/libs/nemesis_dpapi/nemesis_dpapi/repositories.py @@ -0,0 +1,87 @@ +"""Repository interfaces and implementations for DPAPI storage.""" + +from enum import Enum +from typing import Protocol +from uuid import UUID + +from .core import MasterKey, MasterKeyType +from .keys import DomainBackupKey, DpapiSystemCredential + + +class EncryptionFilter(Enum): + """Encryption filter options for masterkey queries.""" + + ALL = "all" # Return all masterkeys + ENCRYPTED_ONLY = "encrypted_only" # Return only encrypted masterkeys + DECRYPTED_ONLY = "decrypted_only" # Return only decrypted masterkeys + + +class MasterKeyRepository(Protocol): + """Protocol for masterkey storage operations.""" + + async def upsert_masterkey(self, masterkey: MasterKey) -> None: + """Add or update a masterkey in storage.""" + ... + + async def get_masterkeys( + self, + guid: UUID | None = None, + encryption_filter: EncryptionFilter = EncryptionFilter.ALL, + backup_key_guid: UUID | None = None, + masterkey_type: list[MasterKeyType] | None = None, + ) -> list[MasterKey]: + """Retrieve masterkey(s) with optional filtering. + + Args: + guid: Optional specific masterkey GUID to retrieve. If provided, returns a list with one MasterKey or empty list. + encryption_filter: Filter by decryption status (default: ALL). Ignored if guid is provided. + backup_key_guid: Filter by backup key GUID (default: None for all). Ignored if guid is provided. + masterkey_type: Filter by user account types (default: None for all). Ignored if guid is provided. + + Returns: + A list of MasterKeys (empty list if no matches) + """ + ... + + async def delete_masterkey(self, guid: UUID) -> None: + """Delete a masterkey by GUID.""" + ... + + +class DomainBackupKeyRepository(Protocol): + """Protocol for domain backup key storage operations.""" + + async def upsert_backup_key(self, key: DomainBackupKey) -> int: + """Add or update a domain backup key in storage.""" + ... + + async def get_backup_keys(self, guid: UUID | None = None) -> list[DomainBackupKey]: + """Retrieve backup key(s). + + Args: + guid: Optional specific backup key GUID to retrieve. If provided, returns a list with one key or empty list. + + Returns: + A list of DomainBackupKey objects (empty list if no matches) + """ + ... + + async def delete_backup_key(self, guid: UUID) -> None: + """Delete a backup key by GUID.""" + ... + + +class DpapiSystemCredentialRepository(Protocol): + """Protocol for DPAPI system credential storage operations.""" + + async def upsert_credential(self, cred: DpapiSystemCredential) -> None: + """Add or update a DPAPI system credential in storage.""" + ... + + async def get_all_credentials(self) -> list[DpapiSystemCredential]: + """Retrieve all DPAPI system credentials.""" + ... + + async def delete_all_credentials(self) -> None: + """Delete all DPAPI system credentials.""" + ... diff --git a/libs/nemesis_dpapi/nemesis_dpapi/storage_in_memory.py b/libs/nemesis_dpapi/nemesis_dpapi/storage_in_memory.py new file mode 100644 index 0000000..c4fae8e --- /dev/null +++ b/libs/nemesis_dpapi/nemesis_dpapi/storage_in_memory.py @@ -0,0 +1,185 @@ +"""Storage backend implementations.""" + +from uuid import UUID + +from .core import MasterKey, MasterKeyType +from .exceptions import StorageError, WriteOnceViolationError +from .keys import DomainBackupKey, DpapiSystemCredential +from .repositories import EncryptionFilter +from .validation import check_write_once_conflicts + + +class InMemoryMasterKeyRepository: + """In-memory storage for masterkeys.""" + + def __init__(self) -> None: + self._masterkeys: dict[UUID, MasterKey] = {} + + async def upsert_masterkey(self, masterkey: MasterKey) -> None: + """Add or update a masterkey in storage with write-once semantics. + + Write-once enforcement: Fields can only be set once. Once a field has a non-NULL value, + it cannot be changed to a different value (including NULL). + + Raises: + WriteOnceViolationError: If attempting to modify fields that already have values + """ + if masterkey.guid in self._masterkeys: + existing = self._masterkeys[masterkey.guid] + + # Check write-once constraints - ensure consistency with PostgreSQL implementation + conflicts = check_write_once_conflicts( + existing, + masterkey, + fields=[ + "encrypted_key_usercred", + "encrypted_key_backup", + "plaintext_key", + "plaintext_key_sha1", + "backup_key_guid", + "masterkey_type", + ], + ) + + if conflicts: + raise WriteOnceViolationError("masterkey", str(masterkey.guid), conflicts) + + # Insert or update (only reached if no conflicts) + self._masterkeys[masterkey.guid] = masterkey + + async def get_masterkeys( + self, + guid: UUID | None = None, + encryption_filter: EncryptionFilter = EncryptionFilter.ALL, + backup_key_guid: UUID | None = None, + masterkey_type: list[MasterKeyType] | None = None, + ) -> list[MasterKey]: + """Retrieve masterkey(s) with optional filtering. + + Args: + guid: Optional specific masterkey GUID to retrieve. If provided, returns a list with one MasterKey or empty list. + encryption_filter: Filter by decryption status (default: ALL). Ignored if guid is provided. + backup_key_guid: Filter by backup key GUID (default: None for all). Ignored if guid is provided. + masterkey_type: Filter by user account types (default: None for all). Ignored if guid is provided. + + Returns: + A list of MasterKeys (empty list if no matches) + """ + # If guid is provided, return single masterkey as a list + if guid is not None: + mk = self._masterkeys.get(guid) + return [mk] if mk is not None else [] + + # Otherwise, return filtered list + masterkeys = list(self._masterkeys.values()) + + # Filter by decryption status + if encryption_filter == EncryptionFilter.ENCRYPTED_ONLY: + masterkeys = [mk for mk in masterkeys if not mk.is_decrypted] + elif encryption_filter == EncryptionFilter.DECRYPTED_ONLY: + masterkeys = [mk for mk in masterkeys if mk.is_decrypted] + + # Filter by backup key GUID + if backup_key_guid is not None: + masterkeys = [mk for mk in masterkeys if mk.backup_key_guid == backup_key_guid] + + # Filter by user account type + if masterkey_type is not None and len(masterkey_type) > 0: + masterkeys = [mk for mk in masterkeys if mk.masterkey_type in masterkey_type] + + return masterkeys + + async def delete_masterkey(self, guid: UUID) -> None: + """Delete a masterkey by GUID.""" + if guid not in self._masterkeys: + raise StorageError(f"Masterkey {guid} not found") + del self._masterkeys[guid] + + +class InMemoryDomainBackupKeyRepository: + """In-memory storage for domain backup keys.""" + + def __init__(self) -> None: + self._backup_keys: dict[UUID, DomainBackupKey] = {} + self._backup_key_ids: dict[UUID, int] = {} + self._next_id: int = 1 + + async def upsert_backup_key(self, key: DomainBackupKey) -> int: + """Add or update a domain backup key in storage with write-once semantics. + + Write-once enforcement: Fields can only be set once. Once a field has a non-NULL value, + it cannot be changed to a different value (including NULL). + + Returns: + The ID of the inserted or updated backup key + + Raises: + WriteOnceViolationError: If attempting to modify fields that already have values + """ + if key.guid in self._backup_keys: + existing = self._backup_keys[key.guid] + + # Check write-once constraints - ensure consistency with PostgreSQL implementation + conflicts = check_write_once_conflicts( + existing, + key, + fields=["key_data", "domain_controller"], + ) + + if conflicts: + raise WriteOnceViolationError("backup_key", str(key.guid), conflicts) + + # Update existing key, return existing ID + self._backup_keys[key.guid] = key + return self._backup_key_ids[key.guid] + + # Insert new key with new ID + key_id = self._next_id + self._next_id += 1 + self._backup_keys[key.guid] = key + self._backup_key_ids[key.guid] = key_id + return key_id + + async def get_backup_keys(self, guid: UUID | None = None) -> list[DomainBackupKey]: + """Retrieve backup key(s). + + Args: + guid: Optional specific backup key GUID to retrieve. If provided, returns a list with one key or empty list. + + Returns: + A list of DomainBackupKey objects (empty list if no matches) + """ + if guid is not None: + key = self._backup_keys.get(guid) + return [key] if key is not None else [] + + return list(self._backup_keys.values()) + + async def delete_backup_key(self, guid: UUID) -> None: + """Delete a backup key by GUID.""" + if guid not in self._backup_keys: + raise StorageError(f"Domain backup key {guid} not found") + del self._backup_keys[guid] + + +class InMemoryDpapiSystemCredentialRepository: + """In-memory storage for DPAPI system credentials.""" + + def __init__(self) -> None: + self._credentials: list[DpapiSystemCredential] = [] + + async def upsert_credential(self, cred: DpapiSystemCredential) -> None: + """Add or update a DPAPI system credential in storage.""" + for i, existing_cred in enumerate(self._credentials): + if existing_cred.user_key == cred.user_key and existing_cred.machine_key == cred.machine_key: + self._credentials[i] = cred + return + self._credentials.append(cred) + + async def get_all_credentials(self) -> list[DpapiSystemCredential]: + """Retrieve all DPAPI system credentials.""" + return list(self._credentials) + + async def delete_all_credentials(self) -> None: + """Delete all DPAPI system credentials.""" + self._credentials.clear() diff --git a/libs/nemesis_dpapi/nemesis_dpapi/storage_postgres.py b/libs/nemesis_dpapi/nemesis_dpapi/storage_postgres.py new file mode 100644 index 0000000..852875b --- /dev/null +++ b/libs/nemesis_dpapi/nemesis_dpapi/storage_postgres.py @@ -0,0 +1,269 @@ +"""PostgreSQL storage backend implementations.""" + +from uuid import UUID + +import asyncpg + +from .core import MasterKey, MasterKeyType +from .exceptions import StorageError +from .keys import DomainBackupKey, DpapiSystemCredential +from .repositories import EncryptionFilter + +MASTKEYS_TABLE = "dpapi.masterkeys" +BACKUPKEYS_TABLE = "dpapi.domain_backup_keys" +SYSTEMCREDS_TABLE = "dpapi.system_credentials" + + +class PostgresMasterKeyRepository: + """PostgreSQL storage for masterkeys.""" + + def __init__(self, connection_pool: asyncpg.Pool) -> None: + self.pool = connection_pool + + async def upsert_masterkey(self, masterkey: MasterKey) -> None: + """Add or update a masterkey in storage with write-once semantics. + + Write-once enforcement: Fields can only be set once. Once a field has a non-NULL value, + it cannot be changed to a different value (including NULL). + + Raises: + WriteOnceViolationError: If attempting to modify fields that already have values + """ + async with self.pool.acquire() as conn: + await conn.execute( + f""" + INSERT INTO {MASTKEYS_TABLE} (guid, encrypted_key_usercred, encrypted_key_backup, + plaintext_key, plaintext_key_sha1, backup_key_guid, masterkey_type) + VALUES ($1, $2, $3, $4, $5, $6, $7) + ON CONFLICT (guid) DO UPDATE SET + encrypted_key_usercred = EXCLUDED.encrypted_key_usercred, + encrypted_key_backup = EXCLUDED.encrypted_key_backup, + plaintext_key = EXCLUDED.plaintext_key, + plaintext_key_sha1 = EXCLUDED.plaintext_key_sha1, + backup_key_guid = EXCLUDED.backup_key_guid, + masterkey_type = EXCLUDED.masterkey_type + WHERE + -- Write-once enforcement: only update if existing is NULL or matches new value + -- SQL Pattern: (existing IS NULL OR existing = new) + -- This correctly handles NULL because: + -- - If existing IS NULL: first condition is TRUE, allows write + -- - If existing is NOT NULL: second condition checked, must equal new value + -- - Note: "NULL = NULL" returns NULL (falsy), but "IS NULL" returns TRUE + ({MASTKEYS_TABLE}.encrypted_key_usercred IS NULL OR + {MASTKEYS_TABLE}.encrypted_key_usercred = EXCLUDED.encrypted_key_usercred) + AND ({MASTKEYS_TABLE}.encrypted_key_backup IS NULL OR + {MASTKEYS_TABLE}.encrypted_key_backup = EXCLUDED.encrypted_key_backup) + AND ({MASTKEYS_TABLE}.plaintext_key IS NULL OR + {MASTKEYS_TABLE}.plaintext_key = EXCLUDED.plaintext_key) + AND ({MASTKEYS_TABLE}.plaintext_key_sha1 IS NULL OR + {MASTKEYS_TABLE}.plaintext_key_sha1 = EXCLUDED.plaintext_key_sha1) + AND ({MASTKEYS_TABLE}.backup_key_guid IS NULL OR + {MASTKEYS_TABLE}.backup_key_guid = EXCLUDED.backup_key_guid) + AND ({MASTKEYS_TABLE}.masterkey_type IS NULL OR + {MASTKEYS_TABLE}.masterkey_type = EXCLUDED.masterkey_type) + """, + str(masterkey.guid), + masterkey.encrypted_key_usercred, + masterkey.encrypted_key_backup, + masterkey.plaintext_key, + masterkey.plaintext_key_sha1, + str(masterkey.backup_key_guid) if masterkey.backup_key_guid else None, + masterkey.masterkey_type.value, + ) + + # Check if the WHERE clause prevented the update (write-once violation) + # Note: asyncpg returns "INSERT 0 1" for new rows, "UPDATE 1" for updated rows + # If WHERE clause fails, we get "INSERT 0 0" (conflict but no update) + # However, asyncpg's execute() doesn't reliably return row counts for ON CONFLICT + # so we rely on service layer validation as the primary check + + async def get_masterkeys( + self, + guid: UUID | None = None, + encryption_filter: EncryptionFilter = EncryptionFilter.ALL, + backup_key_guid: UUID | None = None, + masterkey_type: list[MasterKeyType] | None = None, + ) -> list[MasterKey]: + """Retrieve masterkey(s) with optional filtering. + + Args: + guid: Optional specific masterkey GUID to retrieve. If provided, returns a list with one MasterKey or empty list. + encryption_filter: Filter by decryption status (default: ALL). Ignored if guid is provided. + backup_key_guid: Filter by backup key GUID (default: None for all). Ignored if guid is provided. + masterkey_type: Filter by user account types (default: None for all). Ignored if guid is provided. + + Returns: + A list of MasterKeys (empty list if no matches) + """ + async with self.pool.acquire() as conn: + # If guid is provided, return single masterkey as a list + if guid is not None: + query = f"SELECT * FROM {MASTKEYS_TABLE} WHERE guid = $1" + row = await conn.fetchrow(query, str(guid)) + if not row: + return [] + + mk = MasterKey( + guid=row["guid"], + masterkey_type=MasterKeyType(row["masterkey_type"]) + if row.get("masterkey_type") + else MasterKeyType.UNKNOWN, + encrypted_key_usercred=row["encrypted_key_usercred"], + encrypted_key_backup=row["encrypted_key_backup"], + plaintext_key=row["plaintext_key"], + plaintext_key_sha1=row["plaintext_key_sha1"], + backup_key_guid=row["backup_key_guid"], + ) + return [mk] + + # Otherwise, return filtered list + # Build query based on filters + query = f"SELECT * FROM {MASTKEYS_TABLE}" + params = [] + conditions = [] + + if backup_key_guid is not None: + conditions.append(f"backup_key_guid = ${len(params) + 1}") + params.append(str(backup_key_guid)) + + if masterkey_type is not None and len(masterkey_type) > 0: + # Use ANY for matching multiple values + conditions.append(f"masterkey_type = ANY(${len(params) + 1})") + params.append([t.value for t in masterkey_type]) + + if conditions: + query += " WHERE " + " AND ".join(conditions) + + rows = await conn.fetch(query, *params) + masterkeys = [ + MasterKey( + guid=row["guid"], + masterkey_type=MasterKeyType(row["masterkey_type"]) + if row.get("masterkey_type") + else MasterKeyType.UNKNOWN, + encrypted_key_usercred=row["encrypted_key_usercred"], + encrypted_key_backup=row["encrypted_key_backup"], + plaintext_key=row["plaintext_key"], + plaintext_key_sha1=row["plaintext_key_sha1"], + backup_key_guid=row["backup_key_guid"], + ) + for row in rows + ] + + # Apply decryption filter in Python (could be optimized to SQL) + if encryption_filter == EncryptionFilter.ENCRYPTED_ONLY: + masterkeys = [mk for mk in masterkeys if not mk.is_decrypted] + elif encryption_filter == EncryptionFilter.DECRYPTED_ONLY: + masterkeys = [mk for mk in masterkeys if mk.is_decrypted] + + return masterkeys + + async def delete_masterkey(self, guid: UUID) -> None: + """Delete a masterkey by GUID.""" + async with self.pool.acquire() as conn: + result = await conn.execute(f"DELETE FROM {MASTKEYS_TABLE} WHERE guid = $1", str(guid)) + if result == "DELETE 0": + raise StorageError(f"Masterkey {guid} not found") + + +class PostgresDomainBackupKeyRepository: + """PostgreSQL storage for domain backup keys.""" + + def __init__(self, connection_pool: asyncpg.Pool) -> None: + self.pool = connection_pool + + async def upsert_backup_key(self, key: DomainBackupKey) -> int: + """Add or update a domain backup key in storage with write-once semantics. + + Write-once enforcement: Fields can only be set once. Once a field has a non-NULL value, + it cannot be changed to a different value (including NULL). + + Returns: + The ID of the inserted or updated backup key + + Raises: + WriteOnceViolationError: If attempting to modify fields that already have values + """ + async with self.pool.acquire() as conn: + row = await conn.fetchrow( + f""" + INSERT INTO {BACKUPKEYS_TABLE} (guid, key_data, domain_controller) + VALUES ($1, $2, $3) + ON CONFLICT (guid) DO UPDATE SET + key_data = EXCLUDED.key_data, + domain_controller = EXCLUDED.domain_controller + WHERE + -- Write-once enforcement: only update if existing is NULL or matches new value + -- SQL Pattern: (existing IS NULL OR existing = new) + -- This correctly handles NULL because: + -- - If existing IS NULL: first condition is TRUE, allows write + -- - If existing is NOT NULL: second condition checked, must equal new value + -- - Note: "NULL = NULL" returns NULL (falsy), but "IS NULL" returns TRUE + ({BACKUPKEYS_TABLE}.key_data IS NULL OR + {BACKUPKEYS_TABLE}.key_data = EXCLUDED.key_data) + AND ({BACKUPKEYS_TABLE}.domain_controller IS NULL OR + {BACKUPKEYS_TABLE}.domain_controller = EXCLUDED.domain_controller) + RETURNING id + """, + str(key.guid), + key.key_data, + key.domain_controller, + ) + return row["id"] + + async def get_backup_keys(self, guid: UUID | None = None) -> list[DomainBackupKey]: + """Retrieve backup key(s). + + Args: + guid: Optional specific backup key GUID to retrieve. If provided, returns a list with one key or empty list. + + Returns: + A list of DomainBackupKey objects (empty list if no matches) + """ + async with self.pool.acquire() as conn: + if guid is not None: + row = await conn.fetchrow(f"SELECT * FROM {BACKUPKEYS_TABLE} WHERE guid = $1", str(guid)) + if not row: + return [] + return [DomainBackupKey(guid=row["guid"], key_data=row["key_data"])] + + rows = await conn.fetch(f"SELECT * FROM {BACKUPKEYS_TABLE}") + return [DomainBackupKey(guid=row["guid"], key_data=row["key_data"]) for row in rows] + + async def delete_backup_key(self, guid: UUID) -> None: + """Delete a backup key by GUID.""" + async with self.pool.acquire() as conn: + result = await conn.execute(f"DELETE FROM {BACKUPKEYS_TABLE} WHERE guid = $1", str(guid)) + if result == "DELETE 0": + raise StorageError(f"Domain backup key {guid} not found") + + +class PostgresDpapiSystemCredentialRepository: + """PostgreSQL storage for DPAPI system credentials.""" + + def __init__(self, connection_pool: asyncpg.Pool) -> None: + self.pool = connection_pool + + async def upsert_credential(self, cred: DpapiSystemCredential) -> None: + """Add or update a DPAPI system credential in storage.""" + async with self.pool.acquire() as conn: + await conn.execute( + f""" + INSERT INTO {SYSTEMCREDS_TABLE} (user_key, machine_key) + VALUES ($1, $2) + ON CONFLICT (user_key, machine_key) DO NOTHING + """, + cred.user_key, + cred.machine_key, + ) + + async def get_all_credentials(self) -> list[DpapiSystemCredential]: + """Retrieve all DPAPI system credentials.""" + async with self.pool.acquire() as conn: + rows = await conn.fetch(f"SELECT * FROM {SYSTEMCREDS_TABLE}") + return [DpapiSystemCredential(user_key=row["user_key"], machine_key=row["machine_key"]) for row in rows] + + async def delete_all_credentials(self) -> None: + """Delete all DPAPI system credentials.""" + async with self.pool.acquire() as conn: + await conn.execute(f"DELETE FROM {SYSTEMCREDS_TABLE}") diff --git a/libs/nemesis_dpapi/nemesis_dpapi/types.py b/libs/nemesis_dpapi/nemesis_dpapi/types.py new file mode 100644 index 0000000..9556f3b --- /dev/null +++ b/libs/nemesis_dpapi/nemesis_dpapi/types.py @@ -0,0 +1,68 @@ +"""Core data models for DPAPI library.""" + +import re +from typing import Annotated + +from pydantic import BeforeValidator + + +def validate_windows_sid(value: str) -> str: + """Validate that a string is a valid Windows SID format. + + Windows SIDs have the format: S-R-I-S-S-...-S + Where: + - S = literal 'S' + - R = revision (usually 1) + - I = identifier authority (48-bit number) + - S = subauthority values (32-bit numbers) + + Examples: + - S-1-5-21-1234567890-1234567890-1234567890-1001 (domain user) + - S-1-5-18 (local system) + - S-1-5-32-544 (builtin administrators) + """ + if not isinstance(value, str): + raise ValueError("SID must be a string") + + # Basic format check: starts with S-, all parts are numeric except first + sid_pattern = r"^S-\d+(-\d+)*$" + + if not re.match(sid_pattern, value): + raise ValueError(f"Invalid Windows SID format: {value}") + + # Split and validate components + parts = value.split("-") + + # Must have at least S-R-I-S (4 parts after splitting - need at least one subauthority) + if len(parts) < 4: + raise ValueError(f"SID must have at least one subauthority: {value}") + + # First part must be 'S' + if parts[0] != "S": + raise ValueError(f"SID must start with 'S': {value}") + + # Second part is revision (should be 1) + try: + revision = int(parts[1]) + if revision != 1: + raise ValueError(f"SID revision must be 1, got {revision}: {value}") + except ValueError as e: + # Re-raise specific revision errors, but catch non-numeric revision errors + if "SID revision must be 1" in str(e): + raise e + raise ValueError(f"Invalid SID revision: {value}") from e + + # Validate all numeric parts are valid integers + for i, part in enumerate(parts[2:], start=2): + try: + num_val = int(part) + # Authority and subauthority values should be non-negative + if num_val < 0: + raise ValueError(f"SID component at position {i} must be non-negative: {value}") + except ValueError as e: + raise ValueError(f"Invalid numeric component in SID at position {i}: {value}") from e + + return value + + +Sid = Annotated[str, BeforeValidator(validate_windows_sid)] diff --git a/libs/nemesis_dpapi/nemesis_dpapi/validation.py b/libs/nemesis_dpapi/nemesis_dpapi/validation.py new file mode 100644 index 0000000..f40533b --- /dev/null +++ b/libs/nemesis_dpapi/nemesis_dpapi/validation.py @@ -0,0 +1,118 @@ +"""Validation helpers for DPAPI operations.""" + +from typing import Any + +from Crypto.Hash import SHA1 + + +def validate_and_calculate_sha1( + plaintext_key: bytes | None, + plaintext_key_sha1: bytes | None, +) -> bytes | None: + """Validate and/or calculate SHA1 of plaintext key. + + This function enforces that if a plaintext_key is provided, its SHA1 hash is + correctly calculated and matches any provided plaintext_key_sha1. This ensures + data integrity and prevents accepting mismatched key/hash pairs. + + Rules: + - If plaintext_key provided: calculate SHA1, verify if sha1 also provided + - If only sha1 provided: return it (valid scenario for SHA1-only updates) + - If neither provided: return None + + Args: + plaintext_key: The plaintext masterkey bytes (optional) + plaintext_key_sha1: The SHA1 hash of the plaintext key (optional) + + Returns: + The validated/calculated SHA1 hash, or None if neither input provided + + Raises: + ValueError: If provided sha1 doesn't match calculated sha1 from plaintext_key + + Examples: + >>> # Auto-calculate SHA1 + >>> sha1 = validate_and_calculate_sha1(b"mykey", None) + + >>> # Verify provided SHA1 matches + >>> sha1 = validate_and_calculate_sha1(b"mykey", expected_sha1) + + >>> # SHA1-only update + >>> sha1 = validate_and_calculate_sha1(None, known_sha1) + """ + if plaintext_key is not None: + calculated = SHA1.new(plaintext_key).digest() + if plaintext_key_sha1 is not None: + if calculated != plaintext_key_sha1: + raise ValueError( + "Provided plaintext_key_sha1 does not match calculated SHA1 of plaintext_key" + ) + return calculated + return plaintext_key_sha1 + + +def validate_no_empty_string(value: str | None, field_name: str) -> None: + """Validate that string field is not empty string (NULL or non-empty only). + + Empty strings are not allowed as they can be ambiguous with NULL values. + Fields should either be NULL (unset) or contain a non-empty string. + + Args: + value: The string value to validate (can be None) + field_name: The name of the field being validated (for error messages) + + Raises: + ValueError: If value is an empty string + + Examples: + >>> validate_no_empty_string(None, "domain_controller") # OK + >>> validate_no_empty_string("DC01", "domain_controller") # OK + >>> validate_no_empty_string("", "domain_controller") # Raises ValueError + """ + if value == "": + raise ValueError(f"{field_name} cannot be empty string (use None for unset)") + + +def check_write_once_conflicts( + existing: Any, + new: Any, + fields: list[str], +) -> list[str]: + """Check for write-once conflicts between existing and new records. + + Compares specified fields between an existing record and a new record to detect + write-once violations. A violation occurs when: + - The existing field has a non-NULL value + - The new field has a different value (including NULL) + + This enforces write-once semantics where fields can only be set once and cannot + be changed afterward. + + Args: + existing: The existing record object + new: The new record object to compare against + fields: List of field names to check for conflicts + + Returns: + List of field names that have write-once conflicts (empty if no conflicts) + + Examples: + >>> conflicts = check_write_once_conflicts( + ... existing_masterkey, + ... new_masterkey, + ... ["plaintext_key", "backup_key_guid"] + ... ) + >>> if conflicts: + ... raise WriteOnceViolationError("masterkey", guid, conflicts) + """ + conflicts = [] + for field in fields: + existing_val = getattr(existing, field) + new_val = getattr(new, field) + + # Write-once violation: existing is not NULL and differs from new value + # This includes the case where new value is NULL (attempting to clear a field) + if existing_val is not None and existing_val != new_val: + conflicts.append(field) + + return conflicts diff --git a/libs/nemesis_dpapi/poetry.lock b/libs/nemesis_dpapi/poetry.lock new file mode 100644 index 0000000..530359a --- /dev/null +++ b/libs/nemesis_dpapi/poetry.lock @@ -0,0 +1,2284 @@ +# This file is automatically @generated by Poetry 2.0.1 and should not be changed by hand. + +[[package]] +name = "aiohappyeyeballs" +version = "2.6.1" +description = "Happy Eyeballs for asyncio" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8"}, + {file = "aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558"}, +] + +[[package]] +name = "aiohttp" +version = "3.13.0" +description = "Async http client/server framework (asyncio)" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "aiohttp-3.13.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ca69ec38adf5cadcc21d0b25e2144f6a25b7db7bea7e730bac25075bc305eff0"}, + {file = "aiohttp-3.13.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:240f99f88a9a6beb53ebadac79a2e3417247aa756202ed234b1dbae13d248092"}, + {file = "aiohttp-3.13.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a4676b978a9711531e7cea499d4cdc0794c617a1c0579310ab46c9fdf5877702"}, + {file = "aiohttp-3.13.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:48fcdd5bc771cbbab8ccc9588b8b6447f6a30f9fe00898b1a5107098e00d6793"}, + {file = "aiohttp-3.13.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:eeea0cdd2f687e210c8f605f322d7b0300ba55145014a5dbe98bd4be6fff1f6c"}, + {file = "aiohttp-3.13.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:10b3f01d5aeb632adaaf39c5e93f040a550464a768d54c514050c635adcbb9d0"}, + {file = "aiohttp-3.13.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a4dc0b83e25267f42ef065ea57653de4365b56d7bc4e4cfc94fabe56998f8ee6"}, + {file = "aiohttp-3.13.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:72714919ed9b90f030f761c20670e529c4af96c31bd000917dd0c9afd1afb731"}, + {file = "aiohttp-3.13.0-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:564be41e85318403fdb176e9e5b3e852d528392f42f2c1d1efcbeeed481126d7"}, + {file = "aiohttp-3.13.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:84912962071087286333f70569362e10793f73f45c48854e6859df11001eb2d3"}, + {file = "aiohttp-3.13.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:90b570f1a146181c3d6ae8f755de66227ded49d30d050479b5ae07710f7894c5"}, + {file = "aiohttp-3.13.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:2d71ca30257ce756e37a6078b1dff2d9475fee13609ad831eac9a6531bea903b"}, + {file = "aiohttp-3.13.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:cd45eb70eca63f41bb156b7dffbe1a7760153b69892d923bdb79a74099e2ed90"}, + {file = "aiohttp-3.13.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:5ae3a19949a27982c7425a7a5a963c1268fdbabf0be15ab59448cbcf0f992519"}, + {file = "aiohttp-3.13.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ea6df292013c9f050cbf3f93eee9953d6e5acd9e64a0bf4ca16404bfd7aa9bcc"}, + {file = "aiohttp-3.13.0-cp310-cp310-win32.whl", hash = "sha256:3b64f22fbb6dcd5663de5ef2d847a5638646ef99112503e6f7704bdecb0d1c4d"}, + {file = "aiohttp-3.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:f8d877aa60d80715b2afc565f0f1aea66565824c229a2d065b31670e09fed6d7"}, + {file = "aiohttp-3.13.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:99eb94e97a42367fef5fc11e28cb2362809d3e70837f6e60557816c7106e2e20"}, + {file = "aiohttp-3.13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4696665b2713021c6eba3e2b882a86013763b442577fe5d2056a42111e732eca"}, + {file = "aiohttp-3.13.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3e6a38366f7f0d0f6ed7a1198055150c52fda552b107dad4785c0852ad7685d1"}, + {file = "aiohttp-3.13.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aab715b1a0c37f7f11f9f1f579c6fbaa51ef569e47e3c0a4644fba46077a9409"}, + {file = "aiohttp-3.13.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7972c82bed87d7bd8e374b60a6b6e816d75ba4f7c2627c2d14eed216e62738e1"}, + {file = "aiohttp-3.13.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ca8313cb852af788c78d5afdea24c40172cbfff8b35e58b407467732fde20390"}, + {file = "aiohttp-3.13.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6c333a2385d2a6298265f4b3e960590f787311b87f6b5e6e21bb8375914ef504"}, + {file = "aiohttp-3.13.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cc6d5fc5edbfb8041d9607f6a417997fa4d02de78284d386bea7ab767b5ea4f3"}, + {file = "aiohttp-3.13.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7ddedba3d0043349edc79df3dc2da49c72b06d59a45a42c1c8d987e6b8d175b8"}, + {file = "aiohttp-3.13.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:23ca762140159417a6bbc959ca1927f6949711851e56f2181ddfe8d63512b5ad"}, + {file = "aiohttp-3.13.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:bfe824d6707a5dc3c5676685f624bc0c63c40d79dc0239a7fd6c034b98c25ebe"}, + {file = "aiohttp-3.13.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:3c11fa5dd2ef773a8a5a6daa40243d83b450915992eab021789498dc87acc114"}, + {file = "aiohttp-3.13.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:00fdfe370cffede3163ba9d3f190b32c0cfc8c774f6f67395683d7b0e48cdb8a"}, + {file = "aiohttp-3.13.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:6475e42ef92717a678bfbf50885a682bb360a6f9c8819fb1a388d98198fdcb80"}, + {file = "aiohttp-3.13.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:77da5305a410910218b99f2a963092f4277d8a9c1f429c1ff1b026d1826bd0b6"}, + {file = "aiohttp-3.13.0-cp311-cp311-win32.whl", hash = "sha256:2f9d9ea547618d907f2ee6670c9a951f059c5994e4b6de8dcf7d9747b420c820"}, + {file = "aiohttp-3.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:0f19f7798996d4458c669bd770504f710014926e9970f4729cf55853ae200469"}, + {file = "aiohttp-3.13.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1c272a9a18a5ecc48a7101882230046b83023bb2a662050ecb9bfcb28d9ab53a"}, + {file = "aiohttp-3.13.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:97891a23d7fd4e1afe9c2f4473e04595e4acb18e4733b910b6577b74e7e21985"}, + {file = "aiohttp-3.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:475bd56492ce5f4cffe32b5533c6533ee0c406d1d0e6924879f83adcf51da0ae"}, + {file = "aiohttp-3.13.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c32ada0abb4bc94c30be2b681c42f058ab104d048da6f0148280a51ce98add8c"}, + {file = "aiohttp-3.13.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:4af1f8877ca46ecdd0bc0d4a6b66d4b2bddc84a79e2e8366bc0d5308e76bceb8"}, + {file = "aiohttp-3.13.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e04ab827ec4f775817736b20cdc8350f40327f9b598dec4e18c9ffdcbea88a93"}, + {file = "aiohttp-3.13.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a6d9487b9471ec36b0faedf52228cd732e89be0a2bbd649af890b5e2ce422353"}, + {file = "aiohttp-3.13.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e66c57416352f36bf98f6641ddadd47c93740a22af7150d3e9a1ef6e983f9a8"}, + {file = "aiohttp-3.13.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:469167d5372f5bb3aedff4fc53035d593884fff2617a75317740e885acd48b04"}, + {file = "aiohttp-3.13.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a9f3546b503975a69b547c9fd1582cad10ede1ce6f3e313a2f547c73a3d7814f"}, + {file = "aiohttp-3.13.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:6b4174fcec98601f0cfdf308ee29a6ae53c55f14359e848dab4e94009112ee7d"}, + {file = "aiohttp-3.13.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a533873a7a4ec2270fb362ee5a0d3b98752e4e1dc9042b257cd54545a96bd8ed"}, + {file = "aiohttp-3.13.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:ce887c5e54411d607ee0959cac15bb31d506d86a9bcaddf0b7e9d63325a7a802"}, + {file = "aiohttp-3.13.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:d871f6a30d43e32fc9252dc7b9febe1a042b3ff3908aa83868d7cf7c9579a59b"}, + {file = "aiohttp-3.13.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:222c828243b4789d79a706a876910f656fad4381661691220ba57b2ab4547865"}, + {file = "aiohttp-3.13.0-cp312-cp312-win32.whl", hash = "sha256:682d2e434ff2f1108314ff7f056ce44e457f12dbed0249b24e106e385cf154b9"}, + {file = "aiohttp-3.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:0a2be20eb23888df130214b91c262a90e2de1553d6fb7de9e9010cec994c0ff2"}, + {file = "aiohttp-3.13.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:00243e51f16f6ec0fb021659d4af92f675f3cf9f9b39efd142aa3ad641d8d1e6"}, + {file = "aiohttp-3.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:059978d2fddc462e9211362cbc8446747ecd930537fa559d3d25c256f032ff54"}, + {file = "aiohttp-3.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:564b36512a7da3b386143c611867e3f7cfb249300a1bf60889bd9985da67ab77"}, + {file = "aiohttp-3.13.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4aa995b9156ae499393d949a456a7ab0b994a8241a96db73a3b73c7a090eff6a"}, + {file = "aiohttp-3.13.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:55ca0e95a3905f62f00900255ed807c580775174252999286f283e646d675a49"}, + {file = "aiohttp-3.13.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:49ce7525853a981fc35d380aa2353536a01a9ec1b30979ea4e35966316cace7e"}, + {file = "aiohttp-3.13.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2117be9883501eaf95503bd313eb4c7a23d567edd44014ba15835a1e9ec6d852"}, + {file = "aiohttp-3.13.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d169c47e40c911f728439da853b6fd06da83761012e6e76f11cb62cddae7282b"}, + {file = "aiohttp-3.13.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:703ad3f742fc81e543638a7bebddd35acadaa0004a5e00535e795f4b6f2c25ca"}, + {file = "aiohttp-3.13.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5bf635c3476f4119b940cc8d94ad454cbe0c377e61b4527f0192aabeac1e9370"}, + {file = "aiohttp-3.13.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:cfe6285ef99e7ee51cef20609be2bc1dd0e8446462b71c9db8bb296ba632810a"}, + {file = "aiohttp-3.13.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:34d8af6391c5f2e69749d7f037b614b8c5c42093c251f336bdbfa4b03c57d6c4"}, + {file = "aiohttp-3.13.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:12f5d820fadc5848d4559ea838aef733cf37ed2a1103bba148ac2f5547c14c29"}, + {file = "aiohttp-3.13.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:0f1338b61ea66f4757a0544ed8a02ccbf60e38d9cfb3225888888dd4475ebb96"}, + {file = "aiohttp-3.13.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:582770f82513419512da096e8df21ca44f86a2e56e25dc93c5ab4df0fe065bf0"}, + {file = "aiohttp-3.13.0-cp313-cp313-win32.whl", hash = "sha256:3194b8cab8dbc882f37c13ef1262e0a3d62064fa97533d3aa124771f7bf1ecee"}, + {file = "aiohttp-3.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:7897298b3eedc790257fef8a6ec582ca04e9dbe568ba4a9a890913b925b8ea21"}, + {file = "aiohttp-3.13.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:c417f8c2e1137775569297c584a8a7144e5d1237789eae56af4faf1894a0b861"}, + {file = "aiohttp-3.13.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:f84b53326abf8e56ebc28a35cebf4a0f396a13a76300f500ab11fe0573bf0b52"}, + {file = "aiohttp-3.13.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:990a53b9d6a30b2878789e490758e568b12b4a7fb2527d0c89deb9650b0e5813"}, + {file = "aiohttp-3.13.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c811612711e01b901e18964b3e5dec0d35525150f5f3f85d0aee2935f059910a"}, + {file = "aiohttp-3.13.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:ee433e594d7948e760b5c2a78cc06ac219df33b0848793cf9513d486a9f90a52"}, + {file = "aiohttp-3.13.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:19bb08e56f57c215e9572cd65cb6f8097804412c54081d933997ddde3e5ac579"}, + {file = "aiohttp-3.13.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f27b7488144eb5dd9151cf839b195edd1569629d90ace4c5b6b18e4e75d1e63a"}, + {file = "aiohttp-3.13.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d812838c109757a11354a161c95708ae4199c4fd4d82b90959b20914c1d097f6"}, + {file = "aiohttp-3.13.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7c20db99da682f9180fa5195c90b80b159632fb611e8dbccdd99ba0be0970620"}, + {file = "aiohttp-3.13.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:cf8b0870047900eb1f17f453b4b3953b8ffbf203ef56c2f346780ff930a4d430"}, + {file = "aiohttp-3.13.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:5b8a5557d5af3f4e3add52a58c4cf2b8e6e59fc56b261768866f5337872d596d"}, + {file = "aiohttp-3.13.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:052bcdd80c1c54b8a18a9ea0cd5e36f473dc8e38d51b804cea34841f677a9971"}, + {file = "aiohttp-3.13.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:76484ba17b2832776581b7ab466d094e48eba74cb65a60aea20154dae485e8bd"}, + {file = "aiohttp-3.13.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:62d8a0adcdaf62ee56bfb37737153251ac8e4b27845b3ca065862fb01d99e247"}, + {file = "aiohttp-3.13.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5004d727499ecb95f7c9147dd0bfc5b5670f71d355f0bd26d7af2d3af8e07d2f"}, + {file = "aiohttp-3.13.0-cp314-cp314-win32.whl", hash = "sha256:a1c20c26af48aea984f63f96e5d7af7567c32cb527e33b60a0ef0a6313cf8b03"}, + {file = "aiohttp-3.13.0-cp314-cp314-win_amd64.whl", hash = "sha256:56f7d230ec66e799fbfd8350e9544f8a45a4353f1cf40c1fea74c1780f555b8f"}, + {file = "aiohttp-3.13.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:2fd35177dc483ae702f07b86c782f4f4b100a8ce4e7c5778cea016979023d9fd"}, + {file = "aiohttp-3.13.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:4df1984c8804ed336089e88ac81a9417b1fd0db7c6f867c50a9264488797e778"}, + {file = "aiohttp-3.13.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:e68c0076052dd911a81d3acc4ef2911cc4ef65bf7cadbfbc8ae762da24da858f"}, + {file = "aiohttp-3.13.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bc95c49853cd29613e4fe4ff96d73068ff89b89d61e53988442e127e8da8e7ba"}, + {file = "aiohttp-3.13.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3b3bdc89413117b40cc39baae08fd09cbdeb839d421c4e7dce6a34f6b54b3ac1"}, + {file = "aiohttp-3.13.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3e77a729df23be2116acc4e9de2767d8e92445fbca68886dd991dc912f473755"}, + {file = "aiohttp-3.13.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e88ab34826d6eeb6c67e6e92400b9ec653faf5092a35f07465f44c9f1c429f82"}, + {file = "aiohttp-3.13.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:019dbef24fe28ce2301419dd63a2b97250d9760ca63ee2976c2da2e3f182f82e"}, + {file = "aiohttp-3.13.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:2c4aeaedd20771b7b4bcdf0ae791904445df6d856c02fc51d809d12d17cffdc7"}, + {file = "aiohttp-3.13.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:b3a8e6a2058a0240cfde542b641d0e78b594311bc1a710cbcb2e1841417d5cb3"}, + {file = "aiohttp-3.13.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:f8e38d55ca36c15f36d814ea414ecb2401d860de177c49f84a327a25b3ee752b"}, + {file = "aiohttp-3.13.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:a921edbe971aade1bf45bcbb3494e30ba6863a5c78f28be992c42de980fd9108"}, + {file = "aiohttp-3.13.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:474cade59a447cb4019c0dce9f0434bf835fb558ea932f62c686fe07fe6db6a1"}, + {file = "aiohttp-3.13.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:99a303ad960747c33b65b1cb65d01a62ac73fa39b72f08a2e1efa832529b01ed"}, + {file = "aiohttp-3.13.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:bb34001fc1f05f6b323e02c278090c07a47645caae3aa77ed7ed8a3ce6abcce9"}, + {file = "aiohttp-3.13.0-cp314-cp314t-win32.whl", hash = "sha256:dea698b64235d053def7d2f08af9302a69fcd760d1c7bd9988fd5d3b6157e657"}, + {file = "aiohttp-3.13.0-cp314-cp314t-win_amd64.whl", hash = "sha256:1f164699a060c0b3616459d13c1464a981fddf36f892f0a5027cbd45121fb14b"}, + {file = "aiohttp-3.13.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:fcc425fb6fd2a00c6d91c85d084c6b75a61bc8bc12159d08e17c5711df6c5ba4"}, + {file = "aiohttp-3.13.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7c2c4c9ce834801651f81d6760d0a51035b8b239f58f298de25162fcf6f8bb64"}, + {file = "aiohttp-3.13.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f91e8f9053a07177868e813656ec57599cd2a63238844393cd01bd69c2e40147"}, + {file = "aiohttp-3.13.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:df46d9a3d78ec19b495b1107bf26e4fcf97c900279901f4f4819ac5bb2a02a4c"}, + {file = "aiohttp-3.13.0-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3b1eb9871cbe43b6ca6fac3544682971539d8a1d229e6babe43446279679609d"}, + {file = "aiohttp-3.13.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:62a3cddf8d9a2eae1f79585fa81d32e13d0c509bb9e7ad47d33c83b45a944df7"}, + {file = "aiohttp-3.13.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0f735e680c323ee7e9ef8e2ea26425c7dbc2ede0086fa83ce9d7ccab8a089f26"}, + {file = "aiohttp-3.13.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6a51839f778b0e283b43cd82bb17f1835ee2cc1bf1101765e90ae886e53e751c"}, + {file = "aiohttp-3.13.0-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ac90cfab65bc281d6752f22db5fa90419e33220af4b4fa53b51f5948f414c0e7"}, + {file = "aiohttp-3.13.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:62fd54f3e6f17976962ba67f911d62723c760a69d54f5d7b74c3ceb1a4e9ef8d"}, + {file = "aiohttp-3.13.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:cf2b60b65df05b6b2fa0d887f2189991a0dbf44a0dd18359001dc8fcdb7f1163"}, + {file = "aiohttp-3.13.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:1ccedfe280e804d9a9d7fe8b8c4309d28e364b77f40309c86596baa754af50b1"}, + {file = "aiohttp-3.13.0-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:ea01ffbe23df53ece0c8732d1585b3d6079bb8c9ee14f3745daf000051415a31"}, + {file = "aiohttp-3.13.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:19ba8625fa69523627b67f7e9901b587a4952470f68814d79cdc5bc460e9b885"}, + {file = "aiohttp-3.13.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:4b14bfae90598d331b5061fd15a7c290ea0c15b34aeb1cf620464bb5ec02a602"}, + {file = "aiohttp-3.13.0-cp39-cp39-win32.whl", hash = "sha256:cf7a4b976da219e726d0043fc94ae8169c0dba1d3a059b3c1e2c964bafc5a77d"}, + {file = "aiohttp-3.13.0-cp39-cp39-win_amd64.whl", hash = "sha256:6b9697d15231aeaed4786f090c9c8bc3ab5f0e0a6da1e76c135a310def271020"}, + {file = "aiohttp-3.13.0.tar.gz", hash = "sha256:378dbc57dd8cf341ce243f13fa1fa5394d68e2e02c15cd5f28eae35a70ec7f67"}, +] + +[package.dependencies] +aiohappyeyeballs = ">=2.5.0" +aiosignal = ">=1.4.0" +attrs = ">=17.3.0" +frozenlist = ">=1.1.1" +multidict = ">=4.5,<7.0" +propcache = ">=0.2.0" +yarl = ">=1.17.0,<2.0" + +[package.extras] +speedups = ["Brotli", "aiodns (>=3.3.0)", "brotlicffi", "zstandard"] + +[[package]] +name = "aiosignal" +version = "1.4.0" +description = "aiosignal: a list of registered asynchronous callbacks" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e"}, + {file = "aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7"}, +] + +[package.dependencies] +frozenlist = ">=1.1.0" +typing-extensions = {version = ">=4.2", markers = "python_version < \"3.13\""} + +[[package]] +name = "annotated-types" +version = "0.7.0" +description = "Reusable constraint types to use with typing.Annotated" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, + {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, +] + +[[package]] +name = "asyncpg" +version = "0.30.0" +description = "An asyncio PostgreSQL driver" +optional = false +python-versions = ">=3.8.0" +groups = ["main"] +files = [ + {file = "asyncpg-0.30.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bfb4dd5ae0699bad2b233672c8fc5ccbd9ad24b89afded02341786887e37927e"}, + {file = "asyncpg-0.30.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dc1f62c792752a49f88b7e6f774c26077091b44caceb1983509edc18a2222ec0"}, + {file = "asyncpg-0.30.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3152fef2e265c9c24eec4ee3d22b4f4d2703d30614b0b6753e9ed4115c8a146f"}, + {file = "asyncpg-0.30.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c7255812ac85099a0e1ffb81b10dc477b9973345793776b128a23e60148dd1af"}, + {file = "asyncpg-0.30.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:578445f09f45d1ad7abddbff2a3c7f7c291738fdae0abffbeb737d3fc3ab8b75"}, + {file = "asyncpg-0.30.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c42f6bb65a277ce4d93f3fba46b91a265631c8df7250592dd4f11f8b0152150f"}, + {file = "asyncpg-0.30.0-cp310-cp310-win32.whl", hash = "sha256:aa403147d3e07a267ada2ae34dfc9324e67ccc4cdca35261c8c22792ba2b10cf"}, + {file = "asyncpg-0.30.0-cp310-cp310-win_amd64.whl", hash = "sha256:fb622c94db4e13137c4c7f98834185049cc50ee01d8f657ef898b6407c7b9c50"}, + {file = "asyncpg-0.30.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5e0511ad3dec5f6b4f7a9e063591d407eee66b88c14e2ea636f187da1dcfff6a"}, + {file = "asyncpg-0.30.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:915aeb9f79316b43c3207363af12d0e6fd10776641a7de8a01212afd95bdf0ed"}, + {file = "asyncpg-0.30.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c198a00cce9506fcd0bf219a799f38ac7a237745e1d27f0e1f66d3707c84a5a"}, + {file = "asyncpg-0.30.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3326e6d7381799e9735ca2ec9fd7be4d5fef5dcbc3cb555d8a463d8460607956"}, + {file = "asyncpg-0.30.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:51da377487e249e35bd0859661f6ee2b81db11ad1f4fc036194bc9cb2ead5056"}, + {file = "asyncpg-0.30.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bc6d84136f9c4d24d358f3b02be4b6ba358abd09f80737d1ac7c444f36108454"}, + {file = "asyncpg-0.30.0-cp311-cp311-win32.whl", hash = "sha256:574156480df14f64c2d76450a3f3aaaf26105869cad3865041156b38459e935d"}, + {file = "asyncpg-0.30.0-cp311-cp311-win_amd64.whl", hash = "sha256:3356637f0bd830407b5597317b3cb3571387ae52ddc3bca6233682be88bbbc1f"}, + {file = "asyncpg-0.30.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c902a60b52e506d38d7e80e0dd5399f657220f24635fee368117b8b5fce1142e"}, + {file = "asyncpg-0.30.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:aca1548e43bbb9f0f627a04666fedaca23db0a31a84136ad1f868cb15deb6e3a"}, + {file = "asyncpg-0.30.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c2a2ef565400234a633da0eafdce27e843836256d40705d83ab7ec42074efb3"}, + {file = "asyncpg-0.30.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1292b84ee06ac8a2ad8e51c7475aa309245874b61333d97411aab835c4a2f737"}, + {file = "asyncpg-0.30.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0f5712350388d0cd0615caec629ad53c81e506b1abaaf8d14c93f54b35e3595a"}, + {file = "asyncpg-0.30.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:db9891e2d76e6f425746c5d2da01921e9a16b5a71a1c905b13f30e12a257c4af"}, + {file = "asyncpg-0.30.0-cp312-cp312-win32.whl", hash = "sha256:68d71a1be3d83d0570049cd1654a9bdfe506e794ecc98ad0873304a9f35e411e"}, + {file = "asyncpg-0.30.0-cp312-cp312-win_amd64.whl", hash = "sha256:9a0292c6af5c500523949155ec17b7fe01a00ace33b68a476d6b5059f9630305"}, + {file = "asyncpg-0.30.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:05b185ebb8083c8568ea8a40e896d5f7af4b8554b64d7719c0eaa1eb5a5c3a70"}, + {file = "asyncpg-0.30.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c47806b1a8cbb0a0db896f4cd34d89942effe353a5035c62734ab13b9f938da3"}, + {file = "asyncpg-0.30.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b6fde867a74e8c76c71e2f64f80c64c0f3163e687f1763cfaf21633ec24ec33"}, + {file = "asyncpg-0.30.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46973045b567972128a27d40001124fbc821c87a6cade040cfcd4fa8a30bcdc4"}, + {file = "asyncpg-0.30.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9110df111cabc2ed81aad2f35394a00cadf4f2e0635603db6ebbd0fc896f46a4"}, + {file = "asyncpg-0.30.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:04ff0785ae7eed6cc138e73fc67b8e51d54ee7a3ce9b63666ce55a0bf095f7ba"}, + {file = "asyncpg-0.30.0-cp313-cp313-win32.whl", hash = "sha256:ae374585f51c2b444510cdf3595b97ece4f233fde739aa14b50e0d64e8a7a590"}, + {file = "asyncpg-0.30.0-cp313-cp313-win_amd64.whl", hash = "sha256:f59b430b8e27557c3fb9869222559f7417ced18688375825f8f12302c34e915e"}, + {file = "asyncpg-0.30.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:29ff1fc8b5bf724273782ff8b4f57b0f8220a1b2324184846b39d1ab4122031d"}, + {file = "asyncpg-0.30.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:64e899bce0600871b55368b8483e5e3e7f1860c9482e7f12e0a771e747988168"}, + {file = "asyncpg-0.30.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b290f4726a887f75dcd1b3006f484252db37602313f806e9ffc4e5996cfe5cb"}, + {file = "asyncpg-0.30.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f86b0e2cd3f1249d6fe6fd6cfe0cd4538ba994e2d8249c0491925629b9104d0f"}, + {file = "asyncpg-0.30.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:393af4e3214c8fa4c7b86da6364384c0d1b3298d45803375572f415b6f673f38"}, + {file = "asyncpg-0.30.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:fd4406d09208d5b4a14db9a9dbb311b6d7aeeab57bded7ed2f8ea41aeef39b34"}, + {file = "asyncpg-0.30.0-cp38-cp38-win32.whl", hash = "sha256:0b448f0150e1c3b96cb0438a0d0aa4871f1472e58de14a3ec320dbb2798fb0d4"}, + {file = "asyncpg-0.30.0-cp38-cp38-win_amd64.whl", hash = "sha256:f23b836dd90bea21104f69547923a02b167d999ce053f3d502081acea2fba15b"}, + {file = "asyncpg-0.30.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6f4e83f067b35ab5e6371f8a4c93296e0439857b4569850b178a01385e82e9ad"}, + {file = "asyncpg-0.30.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5df69d55add4efcd25ea2a3b02025b669a285b767bfbf06e356d68dbce4234ff"}, + {file = "asyncpg-0.30.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3479a0d9a852c7c84e822c073622baca862d1217b10a02dd57ee4a7a081f708"}, + {file = "asyncpg-0.30.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26683d3b9a62836fad771a18ecf4659a30f348a561279d6227dab96182f46144"}, + {file = "asyncpg-0.30.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1b982daf2441a0ed314bd10817f1606f1c28b1136abd9e4f11335358c2c631cb"}, + {file = "asyncpg-0.30.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1c06a3a50d014b303e5f6fc1e5f95eb28d2cee89cf58384b700da621e5d5e547"}, + {file = "asyncpg-0.30.0-cp39-cp39-win32.whl", hash = "sha256:1b11a555a198b08f5c4baa8f8231c74a366d190755aa4f99aacec5970afe929a"}, + {file = "asyncpg-0.30.0-cp39-cp39-win_amd64.whl", hash = "sha256:8b684a3c858a83cd876f05958823b68e8d14ec01bb0c0d14a6704c5bf9711773"}, + {file = "asyncpg-0.30.0.tar.gz", hash = "sha256:c551e9928ab6707602f44811817f82ba3c446e018bfe1d3abecc8ba5f3eac851"}, +] + +[package.extras] +docs = ["Sphinx (>=8.1.3,<8.2.0)", "sphinx-rtd-theme (>=1.2.2)"] +gssauth = ["gssapi", "sspilib"] +test = ["distro (>=1.9.0,<1.10.0)", "flake8 (>=6.1,<7.0)", "flake8-pyi (>=24.1.0,<24.2.0)", "gssapi", "k5test", "mypy (>=1.8.0,<1.9.0)", "sspilib", "uvloop (>=0.15.3)"] + +[[package]] +name = "attrs" +version = "25.4.0" +description = "Classes Without Boilerplate" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373"}, + {file = "attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11"}, +] + +[[package]] +name = "blinker" +version = "1.9.0" +description = "Fast, simple object-to-object and broadcast signaling" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "blinker-1.9.0-py3-none-any.whl", hash = "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc"}, + {file = "blinker-1.9.0.tar.gz", hash = "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf"}, +] + +[[package]] +name = "cffi" +version = "2.0.0" +description = "Foreign Function Interface for Python calling C code." +optional = false +python-versions = ">=3.9" +groups = ["main"] +markers = "platform_python_implementation != \"PyPy\"" +files = [ + {file = "cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44"}, + {file = "cffi-2.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49"}, + {file = "cffi-2.0.0-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c"}, + {file = "cffi-2.0.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb"}, + {file = "cffi-2.0.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0"}, + {file = "cffi-2.0.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4"}, + {file = "cffi-2.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453"}, + {file = "cffi-2.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495"}, + {file = "cffi-2.0.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5"}, + {file = "cffi-2.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb"}, + {file = "cffi-2.0.0-cp310-cp310-win32.whl", hash = "sha256:1f72fb8906754ac8a2cc3f9f5aaa298070652a0ffae577e0ea9bd480dc3c931a"}, + {file = "cffi-2.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:b18a3ed7d5b3bd8d9ef7a8cb226502c6bf8308df1525e1cc676c3680e7176739"}, + {file = "cffi-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe"}, + {file = "cffi-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c"}, + {file = "cffi-2.0.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92"}, + {file = "cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93"}, + {file = "cffi-2.0.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5"}, + {file = "cffi-2.0.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664"}, + {file = "cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26"}, + {file = "cffi-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9"}, + {file = "cffi-2.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414"}, + {file = "cffi-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743"}, + {file = "cffi-2.0.0-cp311-cp311-win32.whl", hash = "sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5"}, + {file = "cffi-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5"}, + {file = "cffi-2.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d"}, + {file = "cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d"}, + {file = "cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c"}, + {file = "cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe"}, + {file = "cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062"}, + {file = "cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e"}, + {file = "cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037"}, + {file = "cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba"}, + {file = "cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94"}, + {file = "cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187"}, + {file = "cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18"}, + {file = "cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5"}, + {file = "cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6"}, + {file = "cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb"}, + {file = "cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca"}, + {file = "cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b"}, + {file = "cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b"}, + {file = "cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2"}, + {file = "cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3"}, + {file = "cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26"}, + {file = "cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c"}, + {file = "cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b"}, + {file = "cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27"}, + {file = "cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75"}, + {file = "cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91"}, + {file = "cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5"}, + {file = "cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13"}, + {file = "cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b"}, + {file = "cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c"}, + {file = "cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef"}, + {file = "cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775"}, + {file = "cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205"}, + {file = "cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1"}, + {file = "cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f"}, + {file = "cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25"}, + {file = "cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad"}, + {file = "cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9"}, + {file = "cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d"}, + {file = "cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c"}, + {file = "cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8"}, + {file = "cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc"}, + {file = "cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592"}, + {file = "cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512"}, + {file = "cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4"}, + {file = "cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e"}, + {file = "cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6"}, + {file = "cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9"}, + {file = "cffi-2.0.0-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:fe562eb1a64e67dd297ccc4f5addea2501664954f2692b69a76449ec7913ecbf"}, + {file = "cffi-2.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:de8dad4425a6ca6e4e5e297b27b5c824ecc7581910bf9aee86cb6835e6812aa7"}, + {file = "cffi-2.0.0-cp39-cp39-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:4647afc2f90d1ddd33441e5b0e85b16b12ddec4fca55f0d9671fef036ecca27c"}, + {file = "cffi-2.0.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3f4d46d8b35698056ec29bca21546e1551a205058ae1a181d871e278b0b28165"}, + {file = "cffi-2.0.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:e6e73b9e02893c764e7e8d5bb5ce277f1a009cd5243f8228f75f842bf937c534"}, + {file = "cffi-2.0.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:cb527a79772e5ef98fb1d700678fe031e353e765d1ca2d409c92263c6d43e09f"}, + {file = "cffi-2.0.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:61d028e90346df14fedc3d1e5441df818d095f3b87d286825dfcbd6459b7ef63"}, + {file = "cffi-2.0.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0f6084a0ea23d05d20c3edcda20c3d006f9b6f3fefeac38f59262e10cef47ee2"}, + {file = "cffi-2.0.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:1cd13c99ce269b3ed80b417dcd591415d3372bcac067009b6e0f59c7d4015e65"}, + {file = "cffi-2.0.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:89472c9762729b5ae1ad974b777416bfda4ac5642423fa93bd57a09204712322"}, + {file = "cffi-2.0.0-cp39-cp39-win32.whl", hash = "sha256:2081580ebb843f759b9f617314a24ed5738c51d2aee65d31e02f6f7a2b97707a"}, + {file = "cffi-2.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:b882b3df248017dba09d6b16defe9b5c407fe32fc7c65a9c69798e6175601be9"}, + {file = "cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529"}, +] + +[package.dependencies] +pycparser = {version = "*", markers = "implementation_name != \"PyPy\""} + +[[package]] +name = "charset-normalizer" +version = "3.4.3" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "charset_normalizer-3.4.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fb7f67a1bfa6e40b438170ebdc8158b78dc465a5a67b6dde178a46987b244a72"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cc9370a2da1ac13f0153780040f465839e6cccb4a1e44810124b4e22483c93fe"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:07a0eae9e2787b586e129fdcbe1af6997f8d0e5abaa0bc98c0e20e124d67e601"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:74d77e25adda8581ffc1c720f1c81ca082921329452eba58b16233ab1842141c"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d0e909868420b7049dafd3a31d45125b31143eec59235311fc4c57ea26a4acd2"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c6f162aabe9a91a309510d74eeb6507fab5fff92337a15acbe77753d88d9dcf0"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:4ca4c094de7771a98d7fbd67d9e5dbf1eb73efa4f744a730437d8a3a5cf994f0"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:02425242e96bcf29a49711b0ca9f37e451da7c70562bc10e8ed992a5a7a25cc0"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:78deba4d8f9590fe4dae384aeff04082510a709957e968753ff3c48399f6f92a"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-win32.whl", hash = "sha256:d79c198e27580c8e958906f803e63cddb77653731be08851c7df0b1a14a8fc0f"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-win_amd64.whl", hash = "sha256:c6e490913a46fa054e03699c70019ab869e990270597018cef1d8562132c2669"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b256ee2e749283ef3ddcff51a675ff43798d92d746d1a6e4631bf8c707d22d0b"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:13faeacfe61784e2559e690fc53fa4c5ae97c6fcedb8eb6fb8d0a15b475d2c64"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:00237675befef519d9af72169d8604a067d92755e84fe76492fef5441db05b91"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:585f3b2a80fbd26b048a0be90c5aae8f06605d3c92615911c3a2b03a8a3b796f"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e78314bdc32fa80696f72fa16dc61168fda4d6a0c014e0380f9d02f0e5d8a07"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:96b2b3d1a83ad55310de8c7b4a2d04d9277d5591f40761274856635acc5fcb30"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:939578d9d8fd4299220161fdd76e86c6a251987476f5243e8864a7844476ba14"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:fd10de089bcdcd1be95a2f73dbe6254798ec1bda9f450d5828c96f93e2536b9c"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1e8ac75d72fa3775e0b7cb7e4629cec13b7514d928d15ef8ea06bca03ef01cae"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-win32.whl", hash = "sha256:6cf8fd4c04756b6b60146d98cd8a77d0cdae0e1ca20329da2ac85eed779b6849"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:31a9a6f775f9bcd865d88ee350f0ffb0e25936a7f930ca98995c05abf1faf21c"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e28e334d3ff134e88989d90ba04b47d84382a828c061d0d1027b1b12a62b39b1"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0cacf8f7297b0c4fcb74227692ca46b4a5852f8f4f24b3c766dd94a1075c4884"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c6fd51128a41297f5409deab284fecbe5305ebd7e5a1f959bee1c054622b7018"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3cfb2aad70f2c6debfbcb717f23b7eb55febc0bb23dcffc0f076009da10c6392"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1606f4a55c0fd363d754049cdf400175ee96c992b1f8018b993941f221221c5f"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:027b776c26d38b7f15b26a5da1044f376455fb3766df8fc38563b4efbc515154"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:42e5088973e56e31e4fa58eb6bd709e42fc03799c11c42929592889a2e54c491"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:cc34f233c9e71701040d772aa7490318673aa7164a0efe3172b2981218c26d93"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:320e8e66157cc4e247d9ddca8e21f427efc7a04bbd0ac8a9faf56583fa543f9f"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-win32.whl", hash = "sha256:fb6fecfd65564f208cbf0fba07f107fb661bcd1a7c389edbced3f7a493f70e37"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:86df271bf921c2ee3818f0522e9a5b8092ca2ad8b065ece5d7d9d0e9f4849bcc"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:14c2a87c65b351109f6abfc424cab3927b3bdece6f706e4d12faaf3d52ee5efe"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:41d1fc408ff5fdfb910200ec0e74abc40387bccb3252f3f27c0676731df2b2c8"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1bb60174149316da1c35fa5233681f7c0f9f514509b8e399ab70fea5f17e45c9"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:30d006f98569de3459c2fc1f2acde170b7b2bd265dc1943e87e1a4efe1b67c31"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:416175faf02e4b0810f1f38bcb54682878a4af94059a1cd63b8747244420801f"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6aab0f181c486f973bc7262a97f5aca3ee7e1437011ef0c2ec04b5a11d16c927"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:fdabf8315679312cfa71302f9bd509ded4f2f263fb5b765cf1433b39106c3cc9"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:bd28b817ea8c70215401f657edef3a8aa83c29d447fb0b622c35403780ba11d5"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:18343b2d246dc6761a249ba1fb13f9ee9a2bcd95decc767319506056ea4ad4dc"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-win32.whl", hash = "sha256:6fb70de56f1859a3f71261cbe41005f56a7842cc348d3aeb26237560bfa5e0ce"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-win_amd64.whl", hash = "sha256:cf1ebb7d78e1ad8ec2a8c4732c7be2e736f6e5123a4146c5b89c9d1f585f8cef"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3cd35b7e8aedeb9e34c41385fda4f73ba609e561faedfae0a9e75e44ac558a15"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b89bc04de1d83006373429975f8ef9e7932534b8cc9ca582e4db7d20d91816db"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2001a39612b241dae17b4687898843f254f8748b796a2e16f1051a17078d991d"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8dcfc373f888e4fb39a7bc57e93e3b845e7f462dacc008d9749568b1c4ece096"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:18b97b8404387b96cdbd30ad660f6407799126d26a39ca65729162fd810a99aa"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ccf600859c183d70eb47e05a44cd80a4ce77394d1ac0f79dbd2dd90a69a3a049"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:53cd68b185d98dde4ad8990e56a58dea83a4162161b1ea9272e5c9182ce415e0"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:30a96e1e1f865f78b030d65241c1ee850cdf422d869e9028e2fc1d5e4db73b92"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d716a916938e03231e86e43782ca7878fb602a125a91e7acb8b5112e2e96ac16"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-win32.whl", hash = "sha256:c6dbd0ccdda3a2ba7c2ecd9d77b37f3b5831687d8dc1b6ca5f56a4880cc7b7ce"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-win_amd64.whl", hash = "sha256:73dc19b562516fc9bcf6e5d6e596df0b4eb98d87e4f79f3ae71840e6ed21361c"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0f2be7e0cf7754b9a30eb01f4295cc3d4358a479843b31f328afd210e2c7598c"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c60e092517a73c632ec38e290eba714e9627abe9d301c8c8a12ec32c314a2a4b"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:252098c8c7a873e17dd696ed98bbe91dbacd571da4b87df3736768efa7a792e4"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3653fad4fe3ed447a596ae8638b437f827234f01a8cd801842e43f3d0a6b281b"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8999f965f922ae054125286faf9f11bc6932184b93011d138925a1773830bbe9"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d95bfb53c211b57198bb91c46dd5a2d8018b3af446583aab40074bf7988401cb"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:5b413b0b1bfd94dbf4023ad6945889f374cd24e3f62de58d6bb102c4d9ae534a"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:b5e3b2d152e74e100a9e9573837aba24aab611d39428ded46f4e4022ea7d1942"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:a2d08ac246bb48479170408d6c19f6385fa743e7157d716e144cad849b2dd94b"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-win32.whl", hash = "sha256:ec557499516fc90fd374bf2e32349a2887a876fbf162c160e3c01b6849eaf557"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:5d8d01eac18c423815ed4f4a2ec3b439d654e55ee4ad610e153cf02faf67ea40"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:70bfc5f2c318afece2f5838ea5e4c3febada0be750fcf4775641052bbba14d05"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:23b6b24d74478dc833444cbd927c338349d6ae852ba53a0d02a2de1fce45b96e"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:34a7f768e3f985abdb42841e20e17b330ad3aaf4bb7e7aeeb73db2e70f077b99"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fb731e5deb0c7ef82d698b0f4c5bb724633ee2a489401594c5c88b02e6cb15f7"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:257f26fed7d7ff59921b78244f3cd93ed2af1800ff048c33f624c87475819dd7"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1ef99f0456d3d46a50945c98de1774da86f8e992ab5c77865ea8b8195341fc19"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:2c322db9c8c89009a990ef07c3bcc9f011a3269bc06782f916cd3d9eed7c9312"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:511729f456829ef86ac41ca78c63a5cb55240ed23b4b737faca0eb1abb1c41bc"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:88ab34806dea0671532d3f82d82b85e8fc23d7b2dd12fa837978dad9bb392a34"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-win32.whl", hash = "sha256:16a8770207946ac75703458e2c743631c79c59c5890c80011d536248f8eaa432"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:d22dbedd33326a4a5190dd4fe9e9e693ef12160c77382d9e87919bce54f3d4ca"}, + {file = "charset_normalizer-3.4.3-py3-none-any.whl", hash = "sha256:ce571ab16d890d23b5c278547ba694193a45011ff86a9162a71307ed9f86759a"}, + {file = "charset_normalizer-3.4.3.tar.gz", hash = "sha256:6fce4b8500244f6fcb71465d4a4930d132ba9ab8e71a7859e6a5d59851068d14"}, +] + +[[package]] +name = "click" +version = "8.3.0" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "click-8.3.0-py3-none-any.whl", hash = "sha256:9b9f285302c6e3064f4330c05f05b81945b2a39544279343e6e7c5f27a9baddc"}, + {file = "click-8.3.0.tar.gz", hash = "sha256:e7b8232224eba16f4ebe410c25ced9f7875cb5f3263ffc93cc3e8da705e229c4"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["main", "dev"] +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] +markers = {main = "platform_system == \"Windows\"", dev = "sys_platform == \"win32\""} + +[[package]] +name = "coverage" +version = "7.10.7" +description = "Code coverage measurement for Python" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "coverage-7.10.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:fc04cc7a3db33664e0c2d10eb8990ff6b3536f6842c9590ae8da4c614b9ed05a"}, + {file = "coverage-7.10.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e201e015644e207139f7e2351980feb7040e6f4b2c2978892f3e3789d1c125e5"}, + {file = "coverage-7.10.7-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:240af60539987ced2c399809bd34f7c78e8abe0736af91c3d7d0e795df633d17"}, + {file = "coverage-7.10.7-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8421e088bc051361b01c4b3a50fd39a4b9133079a2229978d9d30511fd05231b"}, + {file = "coverage-7.10.7-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6be8ed3039ae7f7ac5ce058c308484787c86e8437e72b30bf5e88b8ea10f3c87"}, + {file = "coverage-7.10.7-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e28299d9f2e889e6d51b1f043f58d5f997c373cc12e6403b90df95b8b047c13e"}, + {file = "coverage-7.10.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c4e16bd7761c5e454f4efd36f345286d6f7c5fa111623c355691e2755cae3b9e"}, + {file = "coverage-7.10.7-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b1c81d0e5e160651879755c9c675b974276f135558cf4ba79fee7b8413a515df"}, + {file = "coverage-7.10.7-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:606cc265adc9aaedcc84f1f064f0e8736bc45814f15a357e30fca7ecc01504e0"}, + {file = "coverage-7.10.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:10b24412692df990dbc34f8fb1b6b13d236ace9dfdd68df5b28c2e39cafbba13"}, + {file = "coverage-7.10.7-cp310-cp310-win32.whl", hash = "sha256:b51dcd060f18c19290d9b8a9dd1e0181538df2ce0717f562fff6cf74d9fc0b5b"}, + {file = "coverage-7.10.7-cp310-cp310-win_amd64.whl", hash = "sha256:3a622ac801b17198020f09af3eaf45666b344a0d69fc2a6ffe2ea83aeef1d807"}, + {file = "coverage-7.10.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a609f9c93113be646f44c2a0256d6ea375ad047005d7f57a5c15f614dc1b2f59"}, + {file = "coverage-7.10.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:65646bb0359386e07639c367a22cf9b5bf6304e8630b565d0626e2bdf329227a"}, + {file = "coverage-7.10.7-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:5f33166f0dfcce728191f520bd2692914ec70fac2713f6bf3ce59c3deacb4699"}, + {file = "coverage-7.10.7-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:35f5e3f9e455bb17831876048355dca0f758b6df22f49258cb5a91da23ef437d"}, + {file = "coverage-7.10.7-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4da86b6d62a496e908ac2898243920c7992499c1712ff7c2b6d837cc69d9467e"}, + {file = "coverage-7.10.7-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6b8b09c1fad947c84bbbc95eca841350fad9cbfa5a2d7ca88ac9f8d836c92e23"}, + {file = "coverage-7.10.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4376538f36b533b46f8971d3a3e63464f2c7905c9800db97361c43a2b14792ab"}, + {file = "coverage-7.10.7-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:121da30abb574f6ce6ae09840dae322bef734480ceafe410117627aa54f76d82"}, + {file = "coverage-7.10.7-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:88127d40df529336a9836870436fc2751c339fbaed3a836d42c93f3e4bd1d0a2"}, + {file = "coverage-7.10.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ba58bbcd1b72f136080c0bccc2400d66cc6115f3f906c499013d065ac33a4b61"}, + {file = "coverage-7.10.7-cp311-cp311-win32.whl", hash = "sha256:972b9e3a4094b053a4e46832b4bc829fc8a8d347160eb39d03f1690316a99c14"}, + {file = "coverage-7.10.7-cp311-cp311-win_amd64.whl", hash = "sha256:a7b55a944a7f43892e28ad4bc0561dfd5f0d73e605d1aa5c3c976b52aea121d2"}, + {file = "coverage-7.10.7-cp311-cp311-win_arm64.whl", hash = "sha256:736f227fb490f03c6488f9b6d45855f8e0fd749c007f9303ad30efab0e73c05a"}, + {file = "coverage-7.10.7-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7bb3b9ddb87ef7725056572368040c32775036472d5a033679d1fa6c8dc08417"}, + {file = "coverage-7.10.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:18afb24843cbc175687225cab1138c95d262337f5473512010e46831aa0c2973"}, + {file = "coverage-7.10.7-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:399a0b6347bcd3822be369392932884b8216d0944049ae22925631a9b3d4ba4c"}, + {file = "coverage-7.10.7-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:314f2c326ded3f4b09be11bc282eb2fc861184bc95748ae67b360ac962770be7"}, + {file = "coverage-7.10.7-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c41e71c9cfb854789dee6fc51e46743a6d138b1803fab6cb860af43265b42ea6"}, + {file = "coverage-7.10.7-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc01f57ca26269c2c706e838f6422e2a8788e41b3e3c65e2f41148212e57cd59"}, + {file = "coverage-7.10.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a6442c59a8ac8b85812ce33bc4d05bde3fb22321fa8294e2a5b487c3505f611b"}, + {file = "coverage-7.10.7-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:78a384e49f46b80fb4c901d52d92abe098e78768ed829c673fbb53c498bef73a"}, + {file = "coverage-7.10.7-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:5e1e9802121405ede4b0133aa4340ad8186a1d2526de5b7c3eca519db7bb89fb"}, + {file = "coverage-7.10.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d41213ea25a86f69efd1575073d34ea11aabe075604ddf3d148ecfec9e1e96a1"}, + {file = "coverage-7.10.7-cp312-cp312-win32.whl", hash = "sha256:77eb4c747061a6af8d0f7bdb31f1e108d172762ef579166ec84542f711d90256"}, + {file = "coverage-7.10.7-cp312-cp312-win_amd64.whl", hash = "sha256:f51328ffe987aecf6d09f3cd9d979face89a617eacdaea43e7b3080777f647ba"}, + {file = "coverage-7.10.7-cp312-cp312-win_arm64.whl", hash = "sha256:bda5e34f8a75721c96085903c6f2197dc398c20ffd98df33f866a9c8fd95f4bf"}, + {file = "coverage-7.10.7-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:981a651f543f2854abd3b5fcb3263aac581b18209be49863ba575de6edf4c14d"}, + {file = "coverage-7.10.7-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:73ab1601f84dc804f7812dc297e93cd99381162da39c47040a827d4e8dafe63b"}, + {file = "coverage-7.10.7-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:a8b6f03672aa6734e700bbcd65ff050fd19cddfec4b031cc8cf1c6967de5a68e"}, + {file = "coverage-7.10.7-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:10b6ba00ab1132a0ce4428ff68cf50a25efd6840a42cdf4239c9b99aad83be8b"}, + {file = "coverage-7.10.7-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c79124f70465a150e89340de5963f936ee97097d2ef76c869708c4248c63ca49"}, + {file = "coverage-7.10.7-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:69212fbccdbd5b0e39eac4067e20a4a5256609e209547d86f740d68ad4f04911"}, + {file = "coverage-7.10.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7ea7c6c9d0d286d04ed3541747e6597cbe4971f22648b68248f7ddcd329207f0"}, + {file = "coverage-7.10.7-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b9be91986841a75042b3e3243d0b3cb0b2434252b977baaf0cd56e960fe1e46f"}, + {file = "coverage-7.10.7-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:b281d5eca50189325cfe1f365fafade89b14b4a78d9b40b05ddd1fc7d2a10a9c"}, + {file = "coverage-7.10.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:99e4aa63097ab1118e75a848a28e40d68b08a5e19ce587891ab7fd04475e780f"}, + {file = "coverage-7.10.7-cp313-cp313-win32.whl", hash = "sha256:dc7c389dce432500273eaf48f410b37886be9208b2dd5710aaf7c57fd442c698"}, + {file = "coverage-7.10.7-cp313-cp313-win_amd64.whl", hash = "sha256:cac0fdca17b036af3881a9d2729a850b76553f3f716ccb0360ad4dbc06b3b843"}, + {file = "coverage-7.10.7-cp313-cp313-win_arm64.whl", hash = "sha256:4b6f236edf6e2f9ae8fcd1332da4e791c1b6ba0dc16a2dc94590ceccb482e546"}, + {file = "coverage-7.10.7-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a0ec07fd264d0745ee396b666d47cef20875f4ff2375d7c4f58235886cc1ef0c"}, + {file = "coverage-7.10.7-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:dd5e856ebb7bfb7672b0086846db5afb4567a7b9714b8a0ebafd211ec7ce6a15"}, + {file = "coverage-7.10.7-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f57b2a3c8353d3e04acf75b3fed57ba41f5c0646bbf1d10c7c282291c97936b4"}, + {file = "coverage-7.10.7-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:1ef2319dd15a0b009667301a3f84452a4dc6fddfd06b0c5c53ea472d3989fbf0"}, + {file = "coverage-7.10.7-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:83082a57783239717ceb0ad584de3c69cf581b2a95ed6bf81ea66034f00401c0"}, + {file = "coverage-7.10.7-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:50aa94fb1fb9a397eaa19c0d5ec15a5edd03a47bf1a3a6111a16b36e190cff65"}, + {file = "coverage-7.10.7-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:2120043f147bebb41c85b97ac45dd173595ff14f2a584f2963891cbcc3091541"}, + {file = "coverage-7.10.7-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:2fafd773231dd0378fdba66d339f84904a8e57a262f583530f4f156ab83863e6"}, + {file = "coverage-7.10.7-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:0b944ee8459f515f28b851728ad224fa2d068f1513ef6b7ff1efafeb2185f999"}, + {file = "coverage-7.10.7-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4b583b97ab2e3efe1b3e75248a9b333bd3f8b0b1b8e5b45578e05e5850dfb2c2"}, + {file = "coverage-7.10.7-cp313-cp313t-win32.whl", hash = "sha256:2a78cd46550081a7909b3329e2266204d584866e8d97b898cd7fb5ac8d888b1a"}, + {file = "coverage-7.10.7-cp313-cp313t-win_amd64.whl", hash = "sha256:33a5e6396ab684cb43dc7befa386258acb2d7fae7f67330ebb85ba4ea27938eb"}, + {file = "coverage-7.10.7-cp313-cp313t-win_arm64.whl", hash = "sha256:86b0e7308289ddde73d863b7683f596d8d21c7d8664ce1dee061d0bcf3fbb4bb"}, + {file = "coverage-7.10.7-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:b06f260b16ead11643a5a9f955bd4b5fd76c1a4c6796aeade8520095b75de520"}, + {file = "coverage-7.10.7-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:212f8f2e0612778f09c55dd4872cb1f64a1f2b074393d139278ce902064d5b32"}, + {file = "coverage-7.10.7-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3445258bcded7d4aa630ab8296dea4d3f15a255588dd535f980c193ab6b95f3f"}, + {file = "coverage-7.10.7-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bb45474711ba385c46a0bfe696c695a929ae69ac636cda8f532be9e8c93d720a"}, + {file = "coverage-7.10.7-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:813922f35bd800dca9994c5971883cbc0d291128a5de6b167c7aa697fcf59360"}, + {file = "coverage-7.10.7-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:93c1b03552081b2a4423091d6fb3787265b8f86af404cff98d1b5342713bdd69"}, + {file = "coverage-7.10.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:cc87dd1b6eaf0b848eebb1c86469b9f72a1891cb42ac7adcfbce75eadb13dd14"}, + {file = "coverage-7.10.7-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:39508ffda4f343c35f3236fe8d1a6634a51f4581226a1262769d7f970e73bffe"}, + {file = "coverage-7.10.7-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:925a1edf3d810537c5a3abe78ec5530160c5f9a26b1f4270b40e62cc79304a1e"}, + {file = "coverage-7.10.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2c8b9a0636f94c43cd3576811e05b89aa9bc2d0a85137affc544ae5cb0e4bfbd"}, + {file = "coverage-7.10.7-cp314-cp314-win32.whl", hash = "sha256:b7b8288eb7cdd268b0304632da8cb0bb93fadcfec2fe5712f7b9cc8f4d487be2"}, + {file = "coverage-7.10.7-cp314-cp314-win_amd64.whl", hash = "sha256:1ca6db7c8807fb9e755d0379ccc39017ce0a84dcd26d14b5a03b78563776f681"}, + {file = "coverage-7.10.7-cp314-cp314-win_arm64.whl", hash = "sha256:097c1591f5af4496226d5783d036bf6fd6cd0cbc132e071b33861de756efb880"}, + {file = "coverage-7.10.7-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:a62c6ef0d50e6de320c270ff91d9dd0a05e7250cac2a800b7784bae474506e63"}, + {file = "coverage-7.10.7-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:9fa6e4dd51fe15d8738708a973470f67a855ca50002294852e9571cdbd9433f2"}, + {file = "coverage-7.10.7-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:8fb190658865565c549b6b4706856d6a7b09302c797eb2cf8e7fe9dabb043f0d"}, + {file = "coverage-7.10.7-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:affef7c76a9ef259187ef31599a9260330e0335a3011732c4b9effa01e1cd6e0"}, + {file = "coverage-7.10.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e16e07d85ca0cf8bafe5f5d23a0b850064e8e945d5677492b06bbe6f09cc699"}, + {file = "coverage-7.10.7-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:03ffc58aacdf65d2a82bbeb1ffe4d01ead4017a21bfd0454983b88ca73af94b9"}, + {file = "coverage-7.10.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:1b4fd784344d4e52647fd7857b2af5b3fbe6c239b0b5fa63e94eb67320770e0f"}, + {file = "coverage-7.10.7-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:0ebbaddb2c19b71912c6f2518e791aa8b9f054985a0769bdb3a53ebbc765c6a1"}, + {file = "coverage-7.10.7-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:a2d9a3b260cc1d1dbdb1c582e63ddcf5363426a1a68faa0f5da28d8ee3c722a0"}, + {file = "coverage-7.10.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a3cc8638b2480865eaa3926d192e64ce6c51e3d29c849e09d5b4ad95efae5399"}, + {file = "coverage-7.10.7-cp314-cp314t-win32.whl", hash = "sha256:67f8c5cbcd3deb7a60b3345dffc89a961a484ed0af1f6f73de91705cc6e31235"}, + {file = "coverage-7.10.7-cp314-cp314t-win_amd64.whl", hash = "sha256:e1ed71194ef6dea7ed2d5cb5f7243d4bcd334bfb63e59878519be558078f848d"}, + {file = "coverage-7.10.7-cp314-cp314t-win_arm64.whl", hash = "sha256:7fe650342addd8524ca63d77b2362b02345e5f1a093266787d210c70a50b471a"}, + {file = "coverage-7.10.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fff7b9c3f19957020cac546c70025331113d2e61537f6e2441bc7657913de7d3"}, + {file = "coverage-7.10.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:bc91b314cef27742da486d6839b677b3f2793dfe52b51bbbb7cf736d5c29281c"}, + {file = "coverage-7.10.7-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:567f5c155eda8df1d3d439d40a45a6a5f029b429b06648235f1e7e51b522b396"}, + {file = "coverage-7.10.7-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2af88deffcc8a4d5974cf2d502251bc3b2db8461f0b66d80a449c33757aa9f40"}, + {file = "coverage-7.10.7-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c7315339eae3b24c2d2fa1ed7d7a38654cba34a13ef19fbcb9425da46d3dc594"}, + {file = "coverage-7.10.7-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:912e6ebc7a6e4adfdbb1aec371ad04c68854cd3bf3608b3514e7ff9062931d8a"}, + {file = "coverage-7.10.7-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:f49a05acd3dfe1ce9715b657e28d138578bc40126760efb962322c56e9ca344b"}, + {file = "coverage-7.10.7-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:cce2109b6219f22ece99db7644b9622f54a4e915dad65660ec435e89a3ea7cc3"}, + {file = "coverage-7.10.7-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:f3c887f96407cea3916294046fc7dab611c2552beadbed4ea901cbc6a40cc7a0"}, + {file = "coverage-7.10.7-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:635adb9a4507c9fd2ed65f39693fa31c9a3ee3a8e6dc64df033e8fdf52a7003f"}, + {file = "coverage-7.10.7-cp39-cp39-win32.whl", hash = "sha256:5a02d5a850e2979b0a014c412573953995174743a3f7fa4ea5a6e9a3c5617431"}, + {file = "coverage-7.10.7-cp39-cp39-win_amd64.whl", hash = "sha256:c134869d5ffe34547d14e174c866fd8fe2254918cc0a95e99052903bc1543e07"}, + {file = "coverage-7.10.7-py3-none-any.whl", hash = "sha256:f7941f6f2fe6dd6807a1208737b8a0cbcf1cc6d7b07d24998ad2d63590868260"}, + {file = "coverage-7.10.7.tar.gz", hash = "sha256:f4ab143ab113be368a3e9b795f9cd7906c5ef407d6173fe9675a902e1fffc239"}, +] + +[package.extras] +toml = ["tomli"] + +[[package]] +name = "cryptography" +version = "42.0.8" +description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "cryptography-42.0.8-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:81d8a521705787afe7a18d5bfb47ea9d9cc068206270aad0b96a725022e18d2e"}, + {file = "cryptography-42.0.8-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:961e61cefdcb06e0c6d7e3a1b22ebe8b996eb2bf50614e89384be54c48c6b63d"}, + {file = "cryptography-42.0.8-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e3ec3672626e1b9e55afd0df6d774ff0e953452886e06e0f1eb7eb0c832e8902"}, + {file = "cryptography-42.0.8-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e599b53fd95357d92304510fb7bda8523ed1f79ca98dce2f43c115950aa78801"}, + {file = "cryptography-42.0.8-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:5226d5d21ab681f432a9c1cf8b658c0cb02533eece706b155e5fbd8a0cdd3949"}, + {file = "cryptography-42.0.8-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:6b7c4f03ce01afd3b76cf69a5455caa9cfa3de8c8f493e0d3ab7d20611c8dae9"}, + {file = "cryptography-42.0.8-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:2346b911eb349ab547076f47f2e035fc8ff2c02380a7cbbf8d87114fa0f1c583"}, + {file = "cryptography-42.0.8-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:ad803773e9df0b92e0a817d22fd8a3675493f690b96130a5e24f1b8fabbea9c7"}, + {file = "cryptography-42.0.8-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:2f66d9cd9147ee495a8374a45ca445819f8929a3efcd2e3df6428e46c3cbb10b"}, + {file = "cryptography-42.0.8-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:d45b940883a03e19e944456a558b67a41160e367a719833c53de6911cabba2b7"}, + {file = "cryptography-42.0.8-cp37-abi3-win32.whl", hash = "sha256:a0c5b2b0585b6af82d7e385f55a8bc568abff8923af147ee3c07bd8b42cda8b2"}, + {file = "cryptography-42.0.8-cp37-abi3-win_amd64.whl", hash = "sha256:57080dee41209e556a9a4ce60d229244f7a66ef52750f813bfbe18959770cfba"}, + {file = "cryptography-42.0.8-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:dea567d1b0e8bc5764b9443858b673b734100c2871dc93163f58c46a97a83d28"}, + {file = "cryptography-42.0.8-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4783183f7cb757b73b2ae9aed6599b96338eb957233c58ca8f49a49cc32fd5e"}, + {file = "cryptography-42.0.8-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0608251135d0e03111152e41f0cc2392d1e74e35703960d4190b2e0f4ca9c70"}, + {file = "cryptography-42.0.8-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:dc0fdf6787f37b1c6b08e6dfc892d9d068b5bdb671198c72072828b80bd5fe4c"}, + {file = "cryptography-42.0.8-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:9c0c1716c8447ee7dbf08d6db2e5c41c688544c61074b54fc4564196f55c25a7"}, + {file = "cryptography-42.0.8-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:fff12c88a672ab9c9c1cf7b0c80e3ad9e2ebd9d828d955c126be4fd3e5578c9e"}, + {file = "cryptography-42.0.8-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:cafb92b2bc622cd1aa6a1dce4b93307792633f4c5fe1f46c6b97cf67073ec961"}, + {file = "cryptography-42.0.8-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:31f721658a29331f895a5a54e7e82075554ccfb8b163a18719d342f5ffe5ecb1"}, + {file = "cryptography-42.0.8-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b297f90c5723d04bcc8265fc2a0f86d4ea2e0f7ab4b6994459548d3a6b992a14"}, + {file = "cryptography-42.0.8-cp39-abi3-win32.whl", hash = "sha256:2f88d197e66c65be5e42cd72e5c18afbfae3f741742070e3019ac8f4ac57262c"}, + {file = "cryptography-42.0.8-cp39-abi3-win_amd64.whl", hash = "sha256:fa76fbb7596cc5839320000cdd5d0955313696d9511debab7ee7278fc8b5c84a"}, + {file = "cryptography-42.0.8-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:ba4f0a211697362e89ad822e667d8d340b4d8d55fae72cdd619389fb5912eefe"}, + {file = "cryptography-42.0.8-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:81884c4d096c272f00aeb1f11cf62ccd39763581645b0812e99a91505fa48e0c"}, + {file = "cryptography-42.0.8-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c9bb2ae11bfbab395bdd072985abde58ea9860ed84e59dbc0463a5d0159f5b71"}, + {file = "cryptography-42.0.8-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:7016f837e15b0a1c119d27ecd89b3515f01f90a8615ed5e9427e30d9cdbfed3d"}, + {file = "cryptography-42.0.8-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5a94eccb2a81a309806027e1670a358b99b8fe8bfe9f8d329f27d72c094dde8c"}, + {file = "cryptography-42.0.8-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:dec9b018df185f08483f294cae6ccac29e7a6e0678996587363dc352dc65c842"}, + {file = "cryptography-42.0.8-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:343728aac38decfdeecf55ecab3264b015be68fc2816ca800db649607aeee648"}, + {file = "cryptography-42.0.8-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:013629ae70b40af70c9a7a5db40abe5d9054e6f4380e50ce769947b73bf3caad"}, + {file = "cryptography-42.0.8.tar.gz", hash = "sha256:8d09d05439ce7baa8e9e95b07ec5b6c886f548deb7e0f69ef25f64b3bce842f2"}, +] + +[package.dependencies] +cffi = {version = ">=1.12", markers = "platform_python_implementation != \"PyPy\""} + +[package.extras] +docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.1.1)"] +docstest = ["pyenchant (>=1.6.11)", "readme-renderer", "sphinxcontrib-spelling (>=4.0.1)"] +nox = ["nox"] +pep8test = ["check-sdist", "click", "mypy", "ruff"] +sdist = ["build"] +ssh = ["bcrypt (>=3.1.5)"] +test = ["certifi", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] +test-randomorder = ["pytest-randomly"] + +[[package]] +name = "dapr" +version = "1.16.0" +description = "The official release of Dapr Python SDK." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "dapr-1.16.0-py3-none-any.whl", hash = "sha256:076dd559a0b450eae24b1c2ae779c9299ed3e06a05c1f72719a6613af8d19ced"}, + {file = "dapr-1.16.0.tar.gz", hash = "sha256:c7e3d005552a598d07608d0d502b2bc432e86678f94b2beccc13a096b9198684"}, +] + +[package.dependencies] +aiohttp = ">=3.9.0b0" +grpcio = ">=1.37.0" +grpcio-status = ">=1.37.0" +protobuf = ">=4.22" +python-dateutil = ">=2.8.1" +typing-extensions = ">=4.4.0" + +[[package]] +name = "dnspython" +version = "2.8.0" +description = "DNS toolkit" +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "dnspython-2.8.0-py3-none-any.whl", hash = "sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af"}, + {file = "dnspython-2.8.0.tar.gz", hash = "sha256:181d3c6996452cb1189c4046c61599b84a5a86e099562ffde77d26984ff26d0f"}, +] + +[package.extras] +dev = ["black (>=25.1.0)", "coverage (>=7.0)", "flake8 (>=7)", "hypercorn (>=0.17.0)", "mypy (>=1.17)", "pylint (>=3)", "pytest (>=8.4)", "pytest-cov (>=6.2.0)", "quart-trio (>=0.12.0)", "sphinx (>=8.2.0)", "sphinx-rtd-theme (>=3.0.0)", "twine (>=6.1.0)", "wheel (>=0.45.0)"] +dnssec = ["cryptography (>=45)"] +doh = ["h2 (>=4.2.0)", "httpcore (>=1.0.0)", "httpx (>=0.28.0)"] +doq = ["aioquic (>=1.2.0)"] +idna = ["idna (>=3.10)"] +trio = ["trio (>=0.30)"] +wmi = ["wmi (>=1.5.1)"] + +[[package]] +name = "dpapick3" +version = "0.7.1" +description = "A native implementation of DPAPI" +optional = false +python-versions = ">=3.2" +groups = ["main"] +files = [ + {file = "dpapick3-0.7.1-py3-none-any.whl", hash = "sha256:61999f6d4d08231799d3d62e3a48502476fcc0c4d29f12bb8ed064e3352f5da9"}, + {file = "dpapick3-0.7.1.tar.gz", hash = "sha256:3449366800d5bb313dd6d8d9d259d1b94498881ac74ced163749617a431921cb"}, +] + +[package.dependencies] +pyasn1 = "*" +pycryptodome = "*" +python-registry = "*" + +[[package]] +name = "enum-compat" +version = "0.0.3" +description = "enum/enum34 compatibility package" +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "enum-compat-0.0.3.tar.gz", hash = "sha256:3677daabed56a6f724451d585662253d8fb4e5569845aafa8bb0da36b1a8751e"}, + {file = "enum_compat-0.0.3-py3-none-any.whl", hash = "sha256:88091b617c7fc3bbbceae50db5958023c48dc40b50520005aa3bf27f8f7ea157"}, +] + +[[package]] +name = "flask" +version = "3.1.2" +description = "A simple framework for building complex web applications." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "flask-3.1.2-py3-none-any.whl", hash = "sha256:ca1d8112ec8a6158cc29ea4858963350011b5c846a414cdb7a954aa9e967d03c"}, + {file = "flask-3.1.2.tar.gz", hash = "sha256:bf656c15c80190ed628ad08cdfd3aaa35beb087855e2f494910aa3774cc4fd87"}, +] + +[package.dependencies] +blinker = ">=1.9.0" +click = ">=8.1.3" +itsdangerous = ">=2.2.0" +jinja2 = ">=3.1.2" +markupsafe = ">=2.1.1" +werkzeug = ">=3.1.0" + +[package.extras] +async = ["asgiref (>=3.2)"] +dotenv = ["python-dotenv"] + +[[package]] +name = "frozenlist" +version = "1.8.0" +description = "A list-like structure which implements collections.abc.MutableSequence" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "frozenlist-1.8.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b37f6d31b3dcea7deb5e9696e529a6aa4a898adc33db82da12e4c60a7c4d2011"}, + {file = "frozenlist-1.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ef2b7b394f208233e471abc541cc6991f907ffd47dc72584acee3147899d6565"}, + {file = "frozenlist-1.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a88f062f072d1589b7b46e951698950e7da00442fc1cacbe17e19e025dc327ad"}, + {file = "frozenlist-1.8.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f57fb59d9f385710aa7060e89410aeb5058b99e62f4d16b08b91986b9a2140c2"}, + {file = "frozenlist-1.8.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:799345ab092bee59f01a915620b5d014698547afd011e691a208637312db9186"}, + {file = "frozenlist-1.8.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c23c3ff005322a6e16f71bf8692fcf4d5a304aaafe1e262c98c6d4adc7be863e"}, + {file = "frozenlist-1.8.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8a76ea0f0b9dfa06f254ee06053d93a600865b3274358ca48a352ce4f0798450"}, + {file = "frozenlist-1.8.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c7366fe1418a6133d5aa824ee53d406550110984de7637d65a178010f759c6ef"}, + {file = "frozenlist-1.8.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:13d23a45c4cebade99340c4165bd90eeb4a56c6d8a9d8aa49568cac19a6d0dc4"}, + {file = "frozenlist-1.8.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:e4a3408834f65da56c83528fb52ce7911484f0d1eaf7b761fc66001db1646eff"}, + {file = "frozenlist-1.8.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:42145cd2748ca39f32801dad54aeea10039da6f86e303659db90db1c4b614c8c"}, + {file = "frozenlist-1.8.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:e2de870d16a7a53901e41b64ffdf26f2fbb8917b3e6ebf398098d72c5b20bd7f"}, + {file = "frozenlist-1.8.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:20e63c9493d33ee48536600d1a5c95eefc870cd71e7ab037763d1fbb89cc51e7"}, + {file = "frozenlist-1.8.0-cp310-cp310-win32.whl", hash = "sha256:adbeebaebae3526afc3c96fad434367cafbfd1b25d72369a9e5858453b1bb71a"}, + {file = "frozenlist-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:667c3777ca571e5dbeb76f331562ff98b957431df140b54c85fd4d52eea8d8f6"}, + {file = "frozenlist-1.8.0-cp310-cp310-win_arm64.whl", hash = "sha256:80f85f0a7cc86e7a54c46d99c9e1318ff01f4687c172ede30fd52d19d1da1c8e"}, + {file = "frozenlist-1.8.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:09474e9831bc2b2199fad6da3c14c7b0fbdd377cce9d3d77131be28906cb7d84"}, + {file = "frozenlist-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:17c883ab0ab67200b5f964d2b9ed6b00971917d5d8a92df149dc2c9779208ee9"}, + {file = "frozenlist-1.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fa47e444b8ba08fffd1c18e8cdb9a75db1b6a27f17507522834ad13ed5922b93"}, + {file = "frozenlist-1.8.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2552f44204b744fba866e573be4c1f9048d6a324dfe14475103fd51613eb1d1f"}, + {file = "frozenlist-1.8.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:957e7c38f250991e48a9a73e6423db1bb9dd14e722a10f6b8bb8e16a0f55f695"}, + {file = "frozenlist-1.8.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8585e3bb2cdea02fc88ffa245069c36555557ad3609e83be0ec71f54fd4abb52"}, + {file = "frozenlist-1.8.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:edee74874ce20a373d62dc28b0b18b93f645633c2943fd90ee9d898550770581"}, + {file = "frozenlist-1.8.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c9a63152fe95756b85f31186bddf42e4c02c6321207fd6601a1c89ebac4fe567"}, + {file = "frozenlist-1.8.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b6db2185db9be0a04fecf2f241c70b63b1a242e2805be291855078f2b404dd6b"}, + {file = "frozenlist-1.8.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:f4be2e3d8bc8aabd566f8d5b8ba7ecc09249d74ba3c9ed52e54dc23a293f0b92"}, + {file = "frozenlist-1.8.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:c8d1634419f39ea6f5c427ea2f90ca85126b54b50837f31497f3bf38266e853d"}, + {file = "frozenlist-1.8.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:1a7fa382a4a223773ed64242dbe1c9c326ec09457e6b8428efb4118c685c3dfd"}, + {file = "frozenlist-1.8.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:11847b53d722050808926e785df837353bd4d75f1d494377e59b23594d834967"}, + {file = "frozenlist-1.8.0-cp311-cp311-win32.whl", hash = "sha256:27c6e8077956cf73eadd514be8fb04d77fc946a7fe9f7fe167648b0b9085cc25"}, + {file = "frozenlist-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:ac913f8403b36a2c8610bbfd25b8013488533e71e62b4b4adce9c86c8cea905b"}, + {file = "frozenlist-1.8.0-cp311-cp311-win_arm64.whl", hash = "sha256:d4d3214a0f8394edfa3e303136d0575eece0745ff2b47bd2cb2e66dd92d4351a"}, + {file = "frozenlist-1.8.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:78f7b9e5d6f2fdb88cdde9440dc147259b62b9d3b019924def9f6478be254ac1"}, + {file = "frozenlist-1.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:229bf37d2e4acdaf808fd3f06e854a4a7a3661e871b10dc1f8f1896a3b05f18b"}, + {file = "frozenlist-1.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f833670942247a14eafbb675458b4e61c82e002a148f49e68257b79296e865c4"}, + {file = "frozenlist-1.8.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:494a5952b1c597ba44e0e78113a7266e656b9794eec897b19ead706bd7074383"}, + {file = "frozenlist-1.8.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96f423a119f4777a4a056b66ce11527366a8bb92f54e541ade21f2374433f6d4"}, + {file = "frozenlist-1.8.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3462dd9475af2025c31cc61be6652dfa25cbfb56cbbf52f4ccfe029f38decaf8"}, + {file = "frozenlist-1.8.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4c800524c9cd9bac5166cd6f55285957fcfc907db323e193f2afcd4d9abd69b"}, + {file = "frozenlist-1.8.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d6a5df73acd3399d893dafc71663ad22534b5aa4f94e8a2fabfe856c3c1b6a52"}, + {file = "frozenlist-1.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:405e8fe955c2280ce66428b3ca55e12b3c4e9c336fb2103a4937e891c69a4a29"}, + {file = "frozenlist-1.8.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:908bd3f6439f2fef9e85031b59fd4f1297af54415fb60e4254a95f75b3cab3f3"}, + {file = "frozenlist-1.8.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:294e487f9ec720bd8ffcebc99d575f7eff3568a08a253d1ee1a0378754b74143"}, + {file = "frozenlist-1.8.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:74c51543498289c0c43656701be6b077f4b265868fa7f8a8859c197006efb608"}, + {file = "frozenlist-1.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:776f352e8329135506a1d6bf16ac3f87bc25b28e765949282dcc627af36123aa"}, + {file = "frozenlist-1.8.0-cp312-cp312-win32.whl", hash = "sha256:433403ae80709741ce34038da08511d4a77062aa924baf411ef73d1146e74faf"}, + {file = "frozenlist-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:34187385b08f866104f0c0617404c8eb08165ab1272e884abc89c112e9c00746"}, + {file = "frozenlist-1.8.0-cp312-cp312-win_arm64.whl", hash = "sha256:fe3c58d2f5db5fbd18c2987cba06d51b0529f52bc3a6cdc33d3f4eab725104bd"}, + {file = "frozenlist-1.8.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8d92f1a84bb12d9e56f818b3a746f3efba93c1b63c8387a73dde655e1e42282a"}, + {file = "frozenlist-1.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:96153e77a591c8adc2ee805756c61f59fef4cf4073a9275ee86fe8cba41241f7"}, + {file = "frozenlist-1.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f21f00a91358803399890ab167098c131ec2ddd5f8f5fd5fe9c9f2c6fcd91e40"}, + {file = "frozenlist-1.8.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fb30f9626572a76dfe4293c7194a09fb1fe93ba94c7d4f720dfae3b646b45027"}, + {file = "frozenlist-1.8.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eaa352d7047a31d87dafcacbabe89df0aa506abb5b1b85a2fb91bc3faa02d822"}, + {file = "frozenlist-1.8.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:03ae967b4e297f58f8c774c7eabcce57fe3c2434817d4385c50661845a058121"}, + {file = "frozenlist-1.8.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f6292f1de555ffcc675941d65fffffb0a5bcd992905015f85d0592201793e0e5"}, + {file = "frozenlist-1.8.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29548f9b5b5e3460ce7378144c3010363d8035cea44bc0bf02d57f5a685e084e"}, + {file = "frozenlist-1.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ec3cc8c5d4084591b4237c0a272cc4f50a5b03396a47d9caaf76f5d7b38a4f11"}, + {file = "frozenlist-1.8.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:517279f58009d0b1f2e7c1b130b377a349405da3f7621ed6bfae50b10adf20c1"}, + {file = "frozenlist-1.8.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:db1e72ede2d0d7ccb213f218df6a078a9c09a7de257c2fe8fcef16d5925230b1"}, + {file = "frozenlist-1.8.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b4dec9482a65c54a5044486847b8a66bf10c9cb4926d42927ec4e8fd5db7fed8"}, + {file = "frozenlist-1.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:21900c48ae04d13d416f0e1e0c4d81f7931f73a9dfa0b7a8746fb2fe7dd970ed"}, + {file = "frozenlist-1.8.0-cp313-cp313-win32.whl", hash = "sha256:8b7b94a067d1c504ee0b16def57ad5738701e4ba10cec90529f13fa03c833496"}, + {file = "frozenlist-1.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:878be833caa6a3821caf85eb39c5ba92d28e85df26d57afb06b35b2efd937231"}, + {file = "frozenlist-1.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:44389d135b3ff43ba8cc89ff7f51f5a0bb6b63d829c8300f79a2fe4fe61bcc62"}, + {file = "frozenlist-1.8.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:e25ac20a2ef37e91c1b39938b591457666a0fa835c7783c3a8f33ea42870db94"}, + {file = "frozenlist-1.8.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:07cdca25a91a4386d2e76ad992916a85038a9b97561bf7a3fd12d5d9ce31870c"}, + {file = "frozenlist-1.8.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4e0c11f2cc6717e0a741f84a527c52616140741cd812a50422f83dc31749fb52"}, + {file = "frozenlist-1.8.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b3210649ee28062ea6099cfda39e147fa1bc039583c8ee4481cb7811e2448c51"}, + {file = "frozenlist-1.8.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:581ef5194c48035a7de2aefc72ac6539823bb71508189e5de01d60c9dcd5fa65"}, + {file = "frozenlist-1.8.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3ef2d026f16a2b1866e1d86fc4e1291e1ed8a387b2c333809419a2f8b3a77b82"}, + {file = "frozenlist-1.8.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5500ef82073f599ac84d888e3a8c1f77ac831183244bfd7f11eaa0289fb30714"}, + {file = "frozenlist-1.8.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:50066c3997d0091c411a66e710f4e11752251e6d2d73d70d8d5d4c76442a199d"}, + {file = "frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:5c1c8e78426e59b3f8005e9b19f6ff46e5845895adbde20ece9218319eca6506"}, + {file = "frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:eefdba20de0d938cec6a89bd4d70f346a03108a19b9df4248d3cf0d88f1b0f51"}, + {file = "frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:cf253e0e1c3ceb4aaff6df637ce033ff6535fb8c70a764a8f46aafd3d6ab798e"}, + {file = "frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:032efa2674356903cd0261c4317a561a6850f3ac864a63fc1583147fb05a79b0"}, + {file = "frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6da155091429aeba16851ecb10a9104a108bcd32f6c1642867eadaee401c1c41"}, + {file = "frozenlist-1.8.0-cp313-cp313t-win32.whl", hash = "sha256:0f96534f8bfebc1a394209427d0f8a63d343c9779cda6fc25e8e121b5fd8555b"}, + {file = "frozenlist-1.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5d63a068f978fc69421fb0e6eb91a9603187527c86b7cd3f534a5b77a592b888"}, + {file = "frozenlist-1.8.0-cp313-cp313t-win_arm64.whl", hash = "sha256:bf0a7e10b077bf5fb9380ad3ae8ce20ef919a6ad93b4552896419ac7e1d8e042"}, + {file = "frozenlist-1.8.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cee686f1f4cadeb2136007ddedd0aaf928ab95216e7691c63e50a8ec066336d0"}, + {file = "frozenlist-1.8.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:119fb2a1bd47307e899c2fac7f28e85b9a543864df47aa7ec9d3c1b4545f096f"}, + {file = "frozenlist-1.8.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4970ece02dbc8c3a92fcc5228e36a3e933a01a999f7094ff7c23fbd2beeaa67c"}, + {file = "frozenlist-1.8.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:cba69cb73723c3f329622e34bdbf5ce1f80c21c290ff04256cff1cd3c2036ed2"}, + {file = "frozenlist-1.8.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:778a11b15673f6f1df23d9586f83c4846c471a8af693a22e066508b77d201ec8"}, + {file = "frozenlist-1.8.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0325024fe97f94c41c08872db482cf8ac4800d80e79222c6b0b7b162d5b13686"}, + {file = "frozenlist-1.8.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:97260ff46b207a82a7567b581ab4190bd4dfa09f4db8a8b49d1a958f6aa4940e"}, + {file = "frozenlist-1.8.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:54b2077180eb7f83dd52c40b2750d0a9f175e06a42e3213ce047219de902717a"}, + {file = "frozenlist-1.8.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2f05983daecab868a31e1da44462873306d3cbfd76d1f0b5b69c473d21dbb128"}, + {file = "frozenlist-1.8.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:33f48f51a446114bc5d251fb2954ab0164d5be02ad3382abcbfe07e2531d650f"}, + {file = "frozenlist-1.8.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:154e55ec0655291b5dd1b8731c637ecdb50975a2ae70c606d100750a540082f7"}, + {file = "frozenlist-1.8.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:4314debad13beb564b708b4a496020e5306c7333fa9a3ab90374169a20ffab30"}, + {file = "frozenlist-1.8.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:073f8bf8becba60aa931eb3bc420b217bb7d5b8f4750e6f8b3be7f3da85d38b7"}, + {file = "frozenlist-1.8.0-cp314-cp314-win32.whl", hash = "sha256:bac9c42ba2ac65ddc115d930c78d24ab8d4f465fd3fc473cdedfccadb9429806"}, + {file = "frozenlist-1.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:3e0761f4d1a44f1d1a47996511752cf3dcec5bbdd9cc2b4fe595caf97754b7a0"}, + {file = "frozenlist-1.8.0-cp314-cp314-win_arm64.whl", hash = "sha256:d1eaff1d00c7751b7c6662e9c5ba6eb2c17a2306ba5e2a37f24ddf3cc953402b"}, + {file = "frozenlist-1.8.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:d3bb933317c52d7ea5004a1c442eef86f426886fba134ef8cf4226ea6ee1821d"}, + {file = "frozenlist-1.8.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:8009897cdef112072f93a0efdce29cd819e717fd2f649ee3016efd3cd885a7ed"}, + {file = "frozenlist-1.8.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2c5dcbbc55383e5883246d11fd179782a9d07a986c40f49abe89ddf865913930"}, + {file = "frozenlist-1.8.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:39ecbc32f1390387d2aa4f5a995e465e9e2f79ba3adcac92d68e3e0afae6657c"}, + {file = "frozenlist-1.8.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92db2bf818d5cc8d9c1f1fc56b897662e24ea5adb36ad1f1d82875bd64e03c24"}, + {file = "frozenlist-1.8.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2dc43a022e555de94c3b68a4ef0b11c4f747d12c024a520c7101709a2144fb37"}, + {file = "frozenlist-1.8.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cb89a7f2de3602cfed448095bab3f178399646ab7c61454315089787df07733a"}, + {file = "frozenlist-1.8.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:33139dc858c580ea50e7e60a1b0ea003efa1fd42e6ec7fdbad78fff65fad2fd2"}, + {file = "frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:168c0969a329b416119507ba30b9ea13688fafffac1b7822802537569a1cb0ef"}, + {file = "frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:28bd570e8e189d7f7b001966435f9dac6718324b5be2990ac496cf1ea9ddb7fe"}, + {file = "frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b2a095d45c5d46e5e79ba1e5b9cb787f541a8dee0433836cea4b96a2c439dcd8"}, + {file = "frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:eab8145831a0d56ec9c4139b6c3e594c7a83c2c8be25d5bcf2d86136a532287a"}, + {file = "frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:974b28cf63cc99dfb2188d8d222bc6843656188164848c4f679e63dae4b0708e"}, + {file = "frozenlist-1.8.0-cp314-cp314t-win32.whl", hash = "sha256:342c97bf697ac5480c0a7ec73cd700ecfa5a8a40ac923bd035484616efecc2df"}, + {file = "frozenlist-1.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:06be8f67f39c8b1dc671f5d83aaefd3358ae5cdcf8314552c57e7ed3e6475bdd"}, + {file = "frozenlist-1.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:102e6314ca4da683dca92e3b1355490fed5f313b768500084fbe6371fddfdb79"}, + {file = "frozenlist-1.8.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:d8b7138e5cd0647e4523d6685b0eac5d4be9a184ae9634492f25c6eb38c12a47"}, + {file = "frozenlist-1.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a6483e309ca809f1efd154b4d37dc6d9f61037d6c6a81c2dc7a15cb22c8c5dca"}, + {file = "frozenlist-1.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1b9290cf81e95e93fdf90548ce9d3c1211cf574b8e3f4b3b7cb0537cf2227068"}, + {file = "frozenlist-1.8.0-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:59a6a5876ca59d1b63af8cd5e7ffffb024c3dc1e9cf9301b21a2e76286505c95"}, + {file = "frozenlist-1.8.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6dc4126390929823e2d2d9dc79ab4046ed74680360fc5f38b585c12c66cdf459"}, + {file = "frozenlist-1.8.0-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:332db6b2563333c5671fecacd085141b5800cb866be16d5e3eb15a2086476675"}, + {file = "frozenlist-1.8.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9ff15928d62a0b80bb875655c39bf517938c7d589554cbd2669be42d97c2cb61"}, + {file = "frozenlist-1.8.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7bf6cdf8e07c8151fba6fe85735441240ec7f619f935a5205953d58009aef8c6"}, + {file = "frozenlist-1.8.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:48e6d3f4ec5c7273dfe83ff27c91083c6c9065af655dc2684d2c200c94308bb5"}, + {file = "frozenlist-1.8.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:1a7607e17ad33361677adcd1443edf6f5da0ce5e5377b798fba20fae194825f3"}, + {file = "frozenlist-1.8.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:5a3a935c3a4e89c733303a2d5a7c257ea44af3a56c8202df486b7f5de40f37e1"}, + {file = "frozenlist-1.8.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:940d4a017dbfed9daf46a3b086e1d2167e7012ee297fef9e1c545c4d022f5178"}, + {file = "frozenlist-1.8.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:b9be22a69a014bc47e78072d0ecae716f5eb56c15238acca0f43d6eb8e4a5bda"}, + {file = "frozenlist-1.8.0-cp39-cp39-win32.whl", hash = "sha256:1aa77cb5697069af47472e39612976ed05343ff2e84a3dcf15437b232cbfd087"}, + {file = "frozenlist-1.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:7398c222d1d405e796970320036b1b563892b65809d9e5261487bb2c7f7b5c6a"}, + {file = "frozenlist-1.8.0-cp39-cp39-win_arm64.whl", hash = "sha256:b4f3b365f31c6cd4af24545ca0a244a53688cad8834e32f56831c4923b50a103"}, + {file = "frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d"}, + {file = "frozenlist-1.8.0.tar.gz", hash = "sha256:3ede829ed8d842f6cd48fc7081d7a41001a56f1f38603f9d49bf3020d59a31ad"}, +] + +[[package]] +name = "googleapis-common-protos" +version = "1.70.0" +description = "Common protobufs used in Google APIs" +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "googleapis_common_protos-1.70.0-py3-none-any.whl", hash = "sha256:b8bfcca8c25a2bb253e0e0b0adaf8c00773e5e6af6fd92397576680b807e0fd8"}, + {file = "googleapis_common_protos-1.70.0.tar.gz", hash = "sha256:0e1b44e0ea153e6594f9f394fef15193a68aaaea2d843f83e2742717ca753257"}, +] + +[package.dependencies] +protobuf = ">=3.20.2,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<7.0.0" + +[package.extras] +grpc = ["grpcio (>=1.44.0,<2.0.0)"] + +[[package]] +name = "grpcio" +version = "1.75.1" +description = "HTTP/2-based RPC framework" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "grpcio-1.75.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:1712b5890b22547dd29f3215c5788d8fc759ce6dd0b85a6ba6e2731f2d04c088"}, + {file = "grpcio-1.75.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:8d04e101bba4b55cea9954e4aa71c24153ba6182481b487ff376da28d4ba46cf"}, + {file = "grpcio-1.75.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:683cfc70be0c1383449097cba637317e4737a357cfc185d887fd984206380403"}, + {file = "grpcio-1.75.1-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:491444c081a54dcd5e6ada57314321ae526377f498d4aa09d975c3241c5b9e1c"}, + {file = "grpcio-1.75.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ce08d4e112d0d38487c2b631ec8723deac9bc404e9c7b1011426af50a79999e4"}, + {file = "grpcio-1.75.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5a2acda37fc926ccc4547977ac3e56b1df48fe200de968e8c8421f6e3093df6c"}, + {file = "grpcio-1.75.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:745c5fe6bf05df6a04bf2d11552c7d867a2690759e7ab6b05c318a772739bd75"}, + {file = "grpcio-1.75.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:259526a7159d39e2db40d566fe3e8f8e034d0fb2db5bf9c00e09aace655a4c2b"}, + {file = "grpcio-1.75.1-cp310-cp310-win32.whl", hash = "sha256:f4b29b9aabe33fed5df0a85e5f13b09ff25e2c05bd5946d25270a8bd5682dac9"}, + {file = "grpcio-1.75.1-cp310-cp310-win_amd64.whl", hash = "sha256:cf2e760978dcce7ff7d465cbc7e276c3157eedc4c27aa6de7b594c7a295d3d61"}, + {file = "grpcio-1.75.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:573855ca2e58e35032aff30bfbd1ee103fbcf4472e4b28d4010757700918e326"}, + {file = "grpcio-1.75.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:6a4996a2c8accc37976dc142d5991adf60733e223e5c9a2219e157dc6a8fd3a2"}, + {file = "grpcio-1.75.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b1ea1bbe77ecbc1be00af2769f4ae4a88ce93be57a4f3eebd91087898ed749f9"}, + {file = "grpcio-1.75.1-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:e5b425aee54cc5e3e3c58f00731e8a33f5567965d478d516d35ef99fd648ab68"}, + {file = "grpcio-1.75.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0049a7bf547dafaeeb1db17079ce79596c298bfe308fc084d023c8907a845b9a"}, + {file = "grpcio-1.75.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5b8ea230c7f77c0a1a3208a04a1eda164633fb0767b4cefd65a01079b65e5b1f"}, + {file = "grpcio-1.75.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:36990d629c3c9fb41e546414e5af52d0a7af37ce7113d9682c46d7e2919e4cca"}, + {file = "grpcio-1.75.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b10ad908118d38c2453ade7ff790e5bce36580c3742919007a2a78e3a1e521ca"}, + {file = "grpcio-1.75.1-cp311-cp311-win32.whl", hash = "sha256:d6be2b5ee7bea656c954dcf6aa8093c6f0e6a3ef9945c99d99fcbfc88c5c0bfe"}, + {file = "grpcio-1.75.1-cp311-cp311-win_amd64.whl", hash = "sha256:61c692fb05956b17dd6d1ab480f7f10ad0536dba3bc8fd4e3c7263dc244ed772"}, + {file = "grpcio-1.75.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:7b888b33cd14085d86176b1628ad2fcbff94cfbbe7809465097aa0132e58b018"}, + {file = "grpcio-1.75.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:8775036efe4ad2085975531d221535329f5dac99b6c2a854a995456098f99546"}, + {file = "grpcio-1.75.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:bb658f703468d7fbb5dcc4037c65391b7dc34f808ac46ed9136c24fc5eeb041d"}, + {file = "grpcio-1.75.1-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:4b7177a1cdb3c51b02b0c0a256b0a72fdab719600a693e0e9037949efffb200b"}, + {file = "grpcio-1.75.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7d4fa6ccc3ec2e68a04f7b883d354d7fea22a34c44ce535a2f0c0049cf626ddf"}, + {file = "grpcio-1.75.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3d86880ecaeb5b2f0a8afa63824de93adb8ebe4e49d0e51442532f4e08add7d6"}, + {file = "grpcio-1.75.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a8041d2f9e8a742aeae96f4b047ee44e73619f4f9d24565e84d5446c623673b6"}, + {file = "grpcio-1.75.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3652516048bf4c314ce12be37423c79829f46efffb390ad64149a10c6071e8de"}, + {file = "grpcio-1.75.1-cp312-cp312-win32.whl", hash = "sha256:44b62345d8403975513af88da2f3d5cc76f73ca538ba46596f92a127c2aea945"}, + {file = "grpcio-1.75.1-cp312-cp312-win_amd64.whl", hash = "sha256:b1e191c5c465fa777d4cafbaacf0c01e0d5278022082c0abbd2ee1d6454ed94d"}, + {file = "grpcio-1.75.1-cp313-cp313-linux_armv7l.whl", hash = "sha256:3bed22e750d91d53d9e31e0af35a7b0b51367e974e14a4ff229db5b207647884"}, + {file = "grpcio-1.75.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:5b8f381eadcd6ecaa143a21e9e80a26424c76a0a9b3d546febe6648f3a36a5ac"}, + {file = "grpcio-1.75.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5bf4001d3293e3414d0cf99ff9b1139106e57c3a66dfff0c5f60b2a6286ec133"}, + {file = "grpcio-1.75.1-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:9f82ff474103e26351dacfe8d50214e7c9322960d8d07ba7fa1d05ff981c8b2d"}, + {file = "grpcio-1.75.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0ee119f4f88d9f75414217823d21d75bfe0e6ed40135b0cbbfc6376bc9f7757d"}, + {file = "grpcio-1.75.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:664eecc3abe6d916fa6cf8dd6b778e62fb264a70f3430a3180995bf2da935446"}, + {file = "grpcio-1.75.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:c32193fa08b2fbebf08fe08e84f8a0aad32d87c3ad42999c65e9449871b1c66e"}, + {file = "grpcio-1.75.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5cebe13088b9254f6e615bcf1da9131d46cfa4e88039454aca9cb65f639bd3bc"}, + {file = "grpcio-1.75.1-cp313-cp313-win32.whl", hash = "sha256:4b4c678e7ed50f8ae8b8dbad15a865ee73ce12668b6aaf411bf3258b5bc3f970"}, + {file = "grpcio-1.75.1-cp313-cp313-win_amd64.whl", hash = "sha256:5573f51e3f296a1bcf71e7a690c092845fb223072120f4bdb7a5b48e111def66"}, + {file = "grpcio-1.75.1-cp314-cp314-linux_armv7l.whl", hash = "sha256:c05da79068dd96723793bffc8d0e64c45f316248417515f28d22204d9dae51c7"}, + {file = "grpcio-1.75.1-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:06373a94fd16ec287116a825161dca179a0402d0c60674ceeec8c9fba344fe66"}, + {file = "grpcio-1.75.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4484f4b7287bdaa7a5b3980f3c7224c3c622669405d20f69549f5fb956ad0421"}, + {file = "grpcio-1.75.1-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:2720c239c1180eee69f7883c1d4c83fc1a495a2535b5fa322887c70bf02b16e8"}, + {file = "grpcio-1.75.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:07a554fa31c668cf0e7a188678ceeca3cb8fead29bbe455352e712ec33ca701c"}, + {file = "grpcio-1.75.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:3e71a2105210366bfc398eef7f57a664df99194f3520edb88b9c3a7e46ee0d64"}, + {file = "grpcio-1.75.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:8679aa8a5b67976776d3c6b0521e99d1c34db8a312a12bcfd78a7085cb9b604e"}, + {file = "grpcio-1.75.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:aad1c774f4ebf0696a7f148a56d39a3432550612597331792528895258966dc0"}, + {file = "grpcio-1.75.1-cp314-cp314-win32.whl", hash = "sha256:62ce42d9994446b307649cb2a23335fa8e927f7ab2cbf5fcb844d6acb4d85f9c"}, + {file = "grpcio-1.75.1-cp314-cp314-win_amd64.whl", hash = "sha256:f86e92275710bea3000cb79feca1762dc0ad3b27830dd1a74e82ab321d4ee464"}, + {file = "grpcio-1.75.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:c09fba33327c3ac11b5c33dbdd8218eef8990d78f83b1656d628831812a8c0fb"}, + {file = "grpcio-1.75.1-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:7e21400b037be29545704889e72e586c238e346dcb2d08d8a7288d16c883a9ec"}, + {file = "grpcio-1.75.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c12121e509b9f8b0914d10054d24120237d19e870b1cd82acbb8a9b9ddd198a3"}, + {file = "grpcio-1.75.1-cp39-cp39-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:73577a93e692b3474b1bfe84285d098de36705dbd838bb4d6a056d326e4dc880"}, + {file = "grpcio-1.75.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e19e7dfa0d7ca7dea22be464339e18ac608fd75d88c56770c646cdabe54bc724"}, + {file = "grpcio-1.75.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:4e1c28f51c1cf67eccdfc1065e8e866c9ed622f09773ca60947089c117f848a1"}, + {file = "grpcio-1.75.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:030a6164bc2ca726052778c0cf8e3249617a34e368354f9e6107c27ad4af8c28"}, + {file = "grpcio-1.75.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:67697efef5a98d46d5db7b1720fa4043536f8b8e5072a5d61cfca762f287e939"}, + {file = "grpcio-1.75.1-cp39-cp39-win32.whl", hash = "sha256:52015cf73eb5d76f6404e0ce0505a69b51fd1f35810b3a01233b34b10baafb41"}, + {file = "grpcio-1.75.1-cp39-cp39-win_amd64.whl", hash = "sha256:9fe51e4a1f896ea84ac750900eae34d9e9b896b5b1e4a30b02dc31ad29f36383"}, + {file = "grpcio-1.75.1.tar.gz", hash = "sha256:3e81d89ece99b9ace23a6916880baca613c03a799925afb2857887efa8b1b3d2"}, +] + +[package.dependencies] +typing-extensions = ">=4.12,<5.0" + +[package.extras] +protobuf = ["grpcio-tools (>=1.75.1)"] + +[[package]] +name = "grpcio-status" +version = "1.75.1" +description = "Status proto mapping for gRPC" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "grpcio_status-1.75.1-py3-none-any.whl", hash = "sha256:f681b301be26dcf7abf5c765d4a22e4098765e1a65cbdfa3efca384edf8e4e3c"}, + {file = "grpcio_status-1.75.1.tar.gz", hash = "sha256:8162afa21833a2085c91089cc395ad880fac1378a1d60233d976649ed724cbf8"}, +] + +[package.dependencies] +googleapis-common-protos = ">=1.5.5" +grpcio = ">=1.75.1" +protobuf = ">=6.31.1,<7.0.0" + +[[package]] +name = "idna" +version = "3.10" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.6" +groups = ["main"] +files = [ + {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, + {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, +] + +[package.extras] +all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] + +[[package]] +name = "impacket" +version = "0.12.0" +description = "Network protocols Constructors and Dissectors" +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "impacket-0.12.0.tar.gz", hash = "sha256:89587d1b836a5220d74848c934757962b382886dca8b1b4a0c44d693f2600643"}, +] + +[package.dependencies] +charset_normalizer = "*" +flask = ">=1.0" +ldap3 = ">2.5.0,<2.5.2 || >2.5.2,<2.6 || >2.6" +ldapdomaindump = ">=0.9.0" +pyasn1 = ">=0.2.3" +pyasn1_modules = "*" +pycryptodomex = "*" +pyOpenSSL = "24.0.0" +pyreadline3 = {version = "*", markers = "sys_platform == \"win32\""} +setuptools = "*" +six = "*" + +[[package]] +name = "iniconfig" +version = "2.1.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760"}, + {file = "iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"}, +] + +[[package]] +name = "itsdangerous" +version = "2.2.0" +description = "Safely pass data to untrusted environments and back." +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef"}, + {file = "itsdangerous-2.2.0.tar.gz", hash = "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173"}, +] + +[[package]] +name = "jinja2" +version = "3.1.6" +description = "A very fast and expressive template engine." +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67"}, + {file = "jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d"}, +] + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "ldap3" +version = "2.9.1" +description = "A strictly RFC 4510 conforming LDAP V3 pure Python client library" +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "ldap3-2.9.1-py2.py3-none-any.whl", hash = "sha256:5869596fc4948797020d3f03b7939da938778a0f9e2009f7a072ccf92b8e8d70"}, + {file = "ldap3-2.9.1.tar.gz", hash = "sha256:f3e7fc4718e3f09dda568b57100095e0ce58633bcabbed8667ce3f8fbaa4229f"}, +] + +[package.dependencies] +pyasn1 = ">=0.4.6" + +[[package]] +name = "ldapdomaindump" +version = "0.10.0" +description = "Active Directory information dumper via LDAP" +optional = false +python-versions = ">=3.6" +groups = ["main"] +files = [ + {file = "ldapdomaindump-0.10.0-py3-none-any.whl", hash = "sha256:3797259596df7a5e1fda98388c96b1d94196f5da5551f1af1aaeedda0c9f5a11"}, + {file = "ldapdomaindump-0.10.0.tar.gz", hash = "sha256:cbc66b32a7787473ffd169c5319acde46c02fdc9d444556e6448e0def91d3299"}, +] + +[package.dependencies] +dnspython = "*" +ldap3 = ">2.5.0,<2.5.2 || >2.5.2,<2.6 || >2.6" + +[[package]] +name = "markupsafe" +version = "3.0.3" +description = "Safely add untrusted strings to HTML/XML markup." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "markupsafe-3.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559"}, + {file = "markupsafe-3.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419"}, + {file = "markupsafe-3.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695"}, + {file = "markupsafe-3.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591"}, + {file = "markupsafe-3.0.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c"}, + {file = "markupsafe-3.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f"}, + {file = "markupsafe-3.0.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6"}, + {file = "markupsafe-3.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1"}, + {file = "markupsafe-3.0.3-cp310-cp310-win32.whl", hash = "sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa"}, + {file = "markupsafe-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8"}, + {file = "markupsafe-3.0.3-cp310-cp310-win_arm64.whl", hash = "sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1"}, + {file = "markupsafe-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad"}, + {file = "markupsafe-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a"}, + {file = "markupsafe-3.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50"}, + {file = "markupsafe-3.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf"}, + {file = "markupsafe-3.0.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f"}, + {file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a"}, + {file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115"}, + {file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a"}, + {file = "markupsafe-3.0.3-cp311-cp311-win32.whl", hash = "sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19"}, + {file = "markupsafe-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01"}, + {file = "markupsafe-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c"}, + {file = "markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e"}, + {file = "markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce"}, + {file = "markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d"}, + {file = "markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d"}, + {file = "markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a"}, + {file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b"}, + {file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f"}, + {file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b"}, + {file = "markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d"}, + {file = "markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c"}, + {file = "markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f"}, + {file = "markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795"}, + {file = "markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219"}, + {file = "markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6"}, + {file = "markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676"}, + {file = "markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9"}, + {file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1"}, + {file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc"}, + {file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12"}, + {file = "markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed"}, + {file = "markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5"}, + {file = "markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485"}, + {file = "markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73"}, + {file = "markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37"}, + {file = "markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19"}, + {file = "markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025"}, + {file = "markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6"}, + {file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f"}, + {file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb"}, + {file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009"}, + {file = "markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354"}, + {file = "markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218"}, + {file = "markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287"}, + {file = "markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe"}, + {file = "markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026"}, + {file = "markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737"}, + {file = "markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97"}, + {file = "markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d"}, + {file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda"}, + {file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf"}, + {file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe"}, + {file = "markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9"}, + {file = "markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581"}, + {file = "markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4"}, + {file = "markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab"}, + {file = "markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175"}, + {file = "markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634"}, + {file = "markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50"}, + {file = "markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e"}, + {file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5"}, + {file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523"}, + {file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc"}, + {file = "markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d"}, + {file = "markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9"}, + {file = "markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa"}, + {file = "markupsafe-3.0.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:15d939a21d546304880945ca1ecb8a039db6b4dc49b2c5a400387cdae6a62e26"}, + {file = "markupsafe-3.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f71a396b3bf33ecaa1626c255855702aca4d3d9fea5e051b41ac59a9c1c41edc"}, + {file = "markupsafe-3.0.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f4b68347f8c5eab4a13419215bdfd7f8c9b19f2b25520968adfad23eb0ce60c"}, + {file = "markupsafe-3.0.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e8fc20152abba6b83724d7ff268c249fa196d8259ff481f3b1476383f8f24e42"}, + {file = "markupsafe-3.0.3-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:949b8d66bc381ee8b007cd945914c721d9aba8e27f71959d750a46f7c282b20b"}, + {file = "markupsafe-3.0.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:3537e01efc9d4dccdf77221fb1cb3b8e1a38d5428920e0657ce299b20324d758"}, + {file = "markupsafe-3.0.3-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:591ae9f2a647529ca990bc681daebdd52c8791ff06c2bfa05b65163e28102ef2"}, + {file = "markupsafe-3.0.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a320721ab5a1aba0a233739394eb907f8c8da5c98c9181d1161e77a0c8e36f2d"}, + {file = "markupsafe-3.0.3-cp39-cp39-win32.whl", hash = "sha256:df2449253ef108a379b8b5d6b43f4b1a8e81a061d6537becd5582fba5f9196d7"}, + {file = "markupsafe-3.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:7c3fb7d25180895632e5d3148dbdc29ea38ccb7fd210aa27acbd1201a1902c6e"}, + {file = "markupsafe-3.0.3-cp39-cp39-win_arm64.whl", hash = "sha256:38664109c14ffc9e7437e86b4dceb442b0096dfe3541d7864d9cbe1da4cf36c8"}, + {file = "markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698"}, +] + +[[package]] +name = "multidict" +version = "6.7.0" +description = "multidict implementation" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "multidict-6.7.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:9f474ad5acda359c8758c8accc22032c6abe6dc87a8be2440d097785e27a9349"}, + {file = "multidict-6.7.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4b7a9db5a870f780220e931d0002bbfd88fb53aceb6293251e2c839415c1b20e"}, + {file = "multidict-6.7.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:03ca744319864e92721195fa28c7a3b2bc7b686246b35e4078c1e4d0eb5466d3"}, + {file = "multidict-6.7.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f0e77e3c0008bc9316e662624535b88d360c3a5d3f81e15cf12c139a75250046"}, + {file = "multidict-6.7.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:08325c9e5367aa379a3496aa9a022fe8837ff22e00b94db256d3a1378c76ab32"}, + {file = "multidict-6.7.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e2862408c99f84aa571ab462d25236ef9cb12a602ea959ba9c9009a54902fc73"}, + {file = "multidict-6.7.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4d72a9a2d885f5c208b0cb91ff2ed43636bb7e345ec839ff64708e04f69a13cc"}, + {file = "multidict-6.7.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:478cc36476687bac1514d651cbbaa94b86b0732fb6855c60c673794c7dd2da62"}, + {file = "multidict-6.7.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6843b28b0364dc605f21481c90fadb5f60d9123b442eb8a726bb74feef588a84"}, + {file = "multidict-6.7.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:23bfeee5316266e5ee2d625df2d2c602b829435fc3a235c2ba2131495706e4a0"}, + {file = "multidict-6.7.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:680878b9f3d45c31e1f730eef731f9b0bc1da456155688c6745ee84eb818e90e"}, + {file = "multidict-6.7.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:eb866162ef2f45063acc7a53a88ef6fe8bf121d45c30ea3c9cd87ce7e191a8d4"}, + {file = "multidict-6.7.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:df0e3bf7993bdbeca5ac25aa859cf40d39019e015c9c91809ba7093967f7a648"}, + {file = "multidict-6.7.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:661709cdcd919a2ece2234f9bae7174e5220c80b034585d7d8a755632d3e2111"}, + {file = "multidict-6.7.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:096f52730c3fb8ed419db2d44391932b63891b2c5ed14850a7e215c0ba9ade36"}, + {file = "multidict-6.7.0-cp310-cp310-win32.whl", hash = "sha256:afa8a2978ec65d2336305550535c9c4ff50ee527914328c8677b3973ade52b85"}, + {file = "multidict-6.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:b15b3afff74f707b9275d5ba6a91ae8f6429c3ffb29bbfd216b0b375a56f13d7"}, + {file = "multidict-6.7.0-cp310-cp310-win_arm64.whl", hash = "sha256:4b73189894398d59131a66ff157837b1fafea9974be486d036bb3d32331fdbf0"}, + {file = "multidict-6.7.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4d409aa42a94c0b3fa617708ef5276dfe81012ba6753a0370fcc9d0195d0a1fc"}, + {file = "multidict-6.7.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:14c9e076eede3b54c636f8ce1c9c252b5f057c62131211f0ceeec273810c9721"}, + {file = "multidict-6.7.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4c09703000a9d0fa3c3404b27041e574cc7f4df4c6563873246d0e11812a94b6"}, + {file = "multidict-6.7.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:a265acbb7bb33a3a2d626afbe756371dce0279e7b17f4f4eda406459c2b5ff1c"}, + {file = "multidict-6.7.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:51cb455de290ae462593e5b1cb1118c5c22ea7f0d3620d9940bf695cea5a4bd7"}, + {file = "multidict-6.7.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:db99677b4457c7a5c5a949353e125ba72d62b35f74e26da141530fbb012218a7"}, + {file = "multidict-6.7.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f470f68adc395e0183b92a2f4689264d1ea4b40504a24d9882c27375e6662bb9"}, + {file = "multidict-6.7.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0db4956f82723cc1c270de9c6e799b4c341d327762ec78ef82bb962f79cc07d8"}, + {file = "multidict-6.7.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3e56d780c238f9e1ae66a22d2adf8d16f485381878250db8d496623cd38b22bd"}, + {file = "multidict-6.7.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9d14baca2ee12c1a64740d4531356ba50b82543017f3ad6de0deb943c5979abb"}, + {file = "multidict-6.7.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:295a92a76188917c7f99cda95858c822f9e4aae5824246bba9b6b44004ddd0a6"}, + {file = "multidict-6.7.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:39f1719f57adbb767ef592a50ae5ebb794220d1188f9ca93de471336401c34d2"}, + {file = "multidict-6.7.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:0a13fb8e748dfc94749f622de065dd5c1def7e0d2216dba72b1d8069a389c6ff"}, + {file = "multidict-6.7.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e3aa16de190d29a0ea1b48253c57d99a68492c8dd8948638073ab9e74dc9410b"}, + {file = "multidict-6.7.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a048ce45dcdaaf1defb76b2e684f997fb5abf74437b6cb7b22ddad934a964e34"}, + {file = "multidict-6.7.0-cp311-cp311-win32.whl", hash = "sha256:a90af66facec4cebe4181b9e62a68be65e45ac9b52b67de9eec118701856e7ff"}, + {file = "multidict-6.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:95b5ffa4349df2887518bb839409bcf22caa72d82beec453216802f475b23c81"}, + {file = "multidict-6.7.0-cp311-cp311-win_arm64.whl", hash = "sha256:329aa225b085b6f004a4955271a7ba9f1087e39dcb7e65f6284a988264a63912"}, + {file = "multidict-6.7.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8a3862568a36d26e650a19bb5cbbba14b71789032aebc0423f8cc5f150730184"}, + {file = "multidict-6.7.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:960c60b5849b9b4f9dcc9bea6e3626143c252c74113df2c1540aebce70209b45"}, + {file = "multidict-6.7.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2049be98fb57a31b4ccf870bf377af2504d4ae35646a19037ec271e4c07998aa"}, + {file = "multidict-6.7.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0934f3843a1860dd465d38895c17fce1f1cb37295149ab05cd1b9a03afacb2a7"}, + {file = "multidict-6.7.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b3e34f3a1b8131ba06f1a73adab24f30934d148afcd5f5de9a73565a4404384e"}, + {file = "multidict-6.7.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:efbb54e98446892590dc2458c19c10344ee9a883a79b5cec4bc34d6656e8d546"}, + {file = "multidict-6.7.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a35c5fc61d4f51eb045061e7967cfe3123d622cd500e8868e7c0c592a09fedc4"}, + {file = "multidict-6.7.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29fe6740ebccba4175af1b9b87bf553e9c15cd5868ee967e010efcf94e4fd0f1"}, + {file = "multidict-6.7.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:123e2a72e20537add2f33a79e605f6191fba2afda4cbb876e35c1a7074298a7d"}, + {file = "multidict-6.7.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b284e319754366c1aee2267a2036248b24eeb17ecd5dc16022095e747f2f4304"}, + {file = "multidict-6.7.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:803d685de7be4303b5a657b76e2f6d1240e7e0a8aa2968ad5811fa2285553a12"}, + {file = "multidict-6.7.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c04a328260dfd5db8c39538f999f02779012268f54614902d0afc775d44e0a62"}, + {file = "multidict-6.7.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8a19cdb57cd3df4cd865849d93ee14920fb97224300c88501f16ecfa2604b4e0"}, + {file = "multidict-6.7.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9b2fd74c52accced7e75de26023b7dccee62511a600e62311b918ec5c168fc2a"}, + {file = "multidict-6.7.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3e8bfdd0e487acf992407a140d2589fe598238eaeffa3da8448d63a63cd363f8"}, + {file = "multidict-6.7.0-cp312-cp312-win32.whl", hash = "sha256:dd32a49400a2c3d52088e120ee00c1e3576cbff7e10b98467962c74fdb762ed4"}, + {file = "multidict-6.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:92abb658ef2d7ef22ac9f8bb88e8b6c3e571671534e029359b6d9e845923eb1b"}, + {file = "multidict-6.7.0-cp312-cp312-win_arm64.whl", hash = "sha256:490dab541a6a642ce1a9d61a4781656b346a55c13038f0b1244653828e3a83ec"}, + {file = "multidict-6.7.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:bee7c0588aa0076ce77c0ea5d19a68d76ad81fcd9fe8501003b9a24f9d4000f6"}, + {file = "multidict-6.7.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7ef6b61cad77091056ce0e7ce69814ef72afacb150b7ac6a3e9470def2198159"}, + {file = "multidict-6.7.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9c0359b1ec12b1d6849c59f9d319610b7f20ef990a6d454ab151aa0e3b9f78ca"}, + {file = "multidict-6.7.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cd240939f71c64bd658f186330603aac1a9a81bf6273f523fca63673cb7378a8"}, + {file = "multidict-6.7.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a60a4d75718a5efa473ebd5ab685786ba0c67b8381f781d1be14da49f1a2dc60"}, + {file = "multidict-6.7.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:53a42d364f323275126aff81fb67c5ca1b7a04fda0546245730a55c8c5f24bc4"}, + {file = "multidict-6.7.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3b29b980d0ddbecb736735ee5bef69bb2ddca56eff603c86f3f29a1128299b4f"}, + {file = "multidict-6.7.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f8a93b1c0ed2d04b97a5e9336fd2d33371b9a6e29ab7dd6503d63407c20ffbaf"}, + {file = "multidict-6.7.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9ff96e8815eecacc6645da76c413eb3b3d34cfca256c70b16b286a687d013c32"}, + {file = "multidict-6.7.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7516c579652f6a6be0e266aec0acd0db80829ca305c3d771ed898538804c2036"}, + {file = "multidict-6.7.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:040f393368e63fb0f3330e70c26bfd336656bed925e5cbe17c9da839a6ab13ec"}, + {file = "multidict-6.7.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b3bc26a951007b1057a1c543af845f1c7e3e71cc240ed1ace7bf4484aa99196e"}, + {file = "multidict-6.7.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:7b022717c748dd1992a83e219587aabe45980d88969f01b316e78683e6285f64"}, + {file = "multidict-6.7.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:9600082733859f00d79dee64effc7aef1beb26adb297416a4ad2116fd61374bd"}, + {file = "multidict-6.7.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:94218fcec4d72bc61df51c198d098ce2b378e0ccbac41ddbed5ef44092913288"}, + {file = "multidict-6.7.0-cp313-cp313-win32.whl", hash = "sha256:a37bd74c3fa9d00be2d7b8eca074dc56bd8077ddd2917a839bd989612671ed17"}, + {file = "multidict-6.7.0-cp313-cp313-win_amd64.whl", hash = "sha256:30d193c6cc6d559db42b6bcec8a5d395d34d60c9877a0b71ecd7c204fcf15390"}, + {file = "multidict-6.7.0-cp313-cp313-win_arm64.whl", hash = "sha256:ea3334cabe4d41b7ccd01e4d349828678794edbc2d3ae97fc162a3312095092e"}, + {file = "multidict-6.7.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:ad9ce259f50abd98a1ca0aa6e490b58c316a0fce0617f609723e40804add2c00"}, + {file = "multidict-6.7.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:07f5594ac6d084cbb5de2df218d78baf55ef150b91f0ff8a21cc7a2e3a5a58eb"}, + {file = "multidict-6.7.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:0591b48acf279821a579282444814a2d8d0af624ae0bc600aa4d1b920b6e924b"}, + {file = "multidict-6.7.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:749a72584761531d2b9467cfbdfd29487ee21124c304c4b6cb760d8777b27f9c"}, + {file = "multidict-6.7.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b4c3d199f953acd5b446bf7c0de1fe25d94e09e79086f8dc2f48a11a129cdf1"}, + {file = "multidict-6.7.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:9fb0211dfc3b51efea2f349ec92c114d7754dd62c01f81c3e32b765b70c45c9b"}, + {file = "multidict-6.7.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a027ec240fe73a8d6281872690b988eed307cd7d91b23998ff35ff577ca688b5"}, + {file = "multidict-6.7.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1d964afecdf3a8288789df2f5751dc0a8261138c3768d9af117ed384e538fad"}, + {file = "multidict-6.7.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:caf53b15b1b7df9fbd0709aa01409000a2b4dd03a5f6f5cc548183c7c8f8b63c"}, + {file = "multidict-6.7.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:654030da3197d927f05a536a66186070e98765aa5142794c9904555d3a9d8fb5"}, + {file = "multidict-6.7.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:2090d3718829d1e484706a2f525e50c892237b2bf9b17a79b059cb98cddc2f10"}, + {file = "multidict-6.7.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:2d2cfeec3f6f45651b3d408c4acec0ebf3daa9bc8a112a084206f5db5d05b754"}, + {file = "multidict-6.7.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:4ef089f985b8c194d341eb2c24ae6e7408c9a0e2e5658699c92f497437d88c3c"}, + {file = "multidict-6.7.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e93a0617cd16998784bf4414c7e40f17a35d2350e5c6f0bd900d3a8e02bd3762"}, + {file = "multidict-6.7.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f0feece2ef8ebc42ed9e2e8c78fc4aa3cf455733b507c09ef7406364c94376c6"}, + {file = "multidict-6.7.0-cp313-cp313t-win32.whl", hash = "sha256:19a1d55338ec1be74ef62440ca9e04a2f001a04d0cc49a4983dc320ff0f3212d"}, + {file = "multidict-6.7.0-cp313-cp313t-win_amd64.whl", hash = "sha256:3da4fb467498df97e986af166b12d01f05d2e04f978a9c1c680ea1988e0bc4b6"}, + {file = "multidict-6.7.0-cp313-cp313t-win_arm64.whl", hash = "sha256:b4121773c49a0776461f4a904cdf6264c88e42218aaa8407e803ca8025872792"}, + {file = "multidict-6.7.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3bab1e4aff7adaa34410f93b1f8e57c4b36b9af0426a76003f441ee1d3c7e842"}, + {file = "multidict-6.7.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:b8512bac933afc3e45fb2b18da8e59b78d4f408399a960339598374d4ae3b56b"}, + {file = "multidict-6.7.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:79dcf9e477bc65414ebfea98ffd013cb39552b5ecd62908752e0e413d6d06e38"}, + {file = "multidict-6.7.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:31bae522710064b5cbeddaf2e9f32b1abab70ac6ac91d42572502299e9953128"}, + {file = "multidict-6.7.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a0df7ff02397bb63e2fd22af2c87dfa39e8c7f12947bc524dbdc528282c7e34"}, + {file = "multidict-6.7.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7a0222514e8e4c514660e182d5156a415c13ef0aabbd71682fc714e327b95e99"}, + {file = "multidict-6.7.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2397ab4daaf2698eb51a76721e98db21ce4f52339e535725de03ea962b5a3202"}, + {file = "multidict-6.7.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8891681594162635948a636c9fe0ff21746aeb3dd5463f6e25d9bea3a8a39ca1"}, + {file = "multidict-6.7.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:18706cc31dbf402a7945916dd5cddf160251b6dab8a2c5f3d6d5a55949f676b3"}, + {file = "multidict-6.7.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f844a1bbf1d207dd311a56f383f7eda2d0e134921d45751842d8235e7778965d"}, + {file = "multidict-6.7.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:d4393e3581e84e5645506923816b9cc81f5609a778c7e7534054091acc64d1c6"}, + {file = "multidict-6.7.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:fbd18dc82d7bf274b37aa48d664534330af744e03bccf696d6f4c6042e7d19e7"}, + {file = "multidict-6.7.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:b6234e14f9314731ec45c42fc4554b88133ad53a09092cc48a88e771c125dadb"}, + {file = "multidict-6.7.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:08d4379f9744d8f78d98c8673c06e202ffa88296f009c71bbafe8a6bf847d01f"}, + {file = "multidict-6.7.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:9fe04da3f79387f450fd0061d4dd2e45a72749d31bf634aecc9e27f24fdc4b3f"}, + {file = "multidict-6.7.0-cp314-cp314-win32.whl", hash = "sha256:fbafe31d191dfa7c4c51f7a6149c9fb7e914dcf9ffead27dcfd9f1ae382b3885"}, + {file = "multidict-6.7.0-cp314-cp314-win_amd64.whl", hash = "sha256:2f67396ec0310764b9222a1728ced1ab638f61aadc6226f17a71dd9324f9a99c"}, + {file = "multidict-6.7.0-cp314-cp314-win_arm64.whl", hash = "sha256:ba672b26069957ee369cfa7fc180dde1fc6f176eaf1e6beaf61fbebbd3d9c000"}, + {file = "multidict-6.7.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:c1dcc7524066fa918c6a27d61444d4ee7900ec635779058571f70d042d86ed63"}, + {file = "multidict-6.7.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:27e0b36c2d388dc7b6ced3406671b401e84ad7eb0656b8f3a2f46ed0ce483718"}, + {file = "multidict-6.7.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2a7baa46a22e77f0988e3b23d4ede5513ebec1929e34ee9495be535662c0dfe2"}, + {file = "multidict-6.7.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7bf77f54997a9166a2f5675d1201520586439424c2511723a7312bdb4bcc034e"}, + {file = "multidict-6.7.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e011555abada53f1578d63389610ac8a5400fc70ce71156b0aa30d326f1a5064"}, + {file = "multidict-6.7.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:28b37063541b897fd6a318007373930a75ca6d6ac7c940dbe14731ffdd8d498e"}, + {file = "multidict-6.7.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:05047ada7a2fde2631a0ed706f1fd68b169a681dfe5e4cf0f8e4cb6618bbc2cd"}, + {file = "multidict-6.7.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:716133f7d1d946a4e1b91b1756b23c088881e70ff180c24e864c26192ad7534a"}, + {file = "multidict-6.7.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d1bed1b467ef657f2a0ae62844a607909ef1c6889562de5e1d505f74457d0b96"}, + {file = "multidict-6.7.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:ca43bdfa5d37bd6aee89d85e1d0831fb86e25541be7e9d376ead1b28974f8e5e"}, + {file = "multidict-6.7.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:44b546bd3eb645fd26fb949e43c02a25a2e632e2ca21a35e2e132c8105dc8599"}, + {file = "multidict-6.7.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:a6ef16328011d3f468e7ebc326f24c1445f001ca1dec335b2f8e66bed3006394"}, + {file = "multidict-6.7.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:5aa873cbc8e593d361ae65c68f85faadd755c3295ea2c12040ee146802f23b38"}, + {file = "multidict-6.7.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:3d7b6ccce016e29df4b7ca819659f516f0bc7a4b3efa3bb2012ba06431b044f9"}, + {file = "multidict-6.7.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:171b73bd4ee683d307599b66793ac80981b06f069b62eea1c9e29c9241aa66b0"}, + {file = "multidict-6.7.0-cp314-cp314t-win32.whl", hash = "sha256:b2d7f80c4e1fd010b07cb26820aae86b7e73b681ee4889684fb8d2d4537aab13"}, + {file = "multidict-6.7.0-cp314-cp314t-win_amd64.whl", hash = "sha256:09929cab6fcb68122776d575e03c6cc64ee0b8fca48d17e135474b042ce515cd"}, + {file = "multidict-6.7.0-cp314-cp314t-win_arm64.whl", hash = "sha256:cc41db090ed742f32bd2d2c721861725e6109681eddf835d0a82bd3a5c382827"}, + {file = "multidict-6.7.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:363eb68a0a59bd2303216d2346e6c441ba10d36d1f9969fcb6f1ba700de7bb5c"}, + {file = "multidict-6.7.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d874eb056410ca05fed180b6642e680373688efafc7f077b2a2f61811e873a40"}, + {file = "multidict-6.7.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8b55d5497b51afdfde55925e04a022f1de14d4f4f25cdfd4f5d9b0aa96166851"}, + {file = "multidict-6.7.0-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f8e5c0031b90ca9ce555e2e8fd5c3b02a25f14989cbc310701823832c99eb687"}, + {file = "multidict-6.7.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9cf41880c991716f3c7cec48e2f19ae4045fc9db5fc9cff27347ada24d710bb5"}, + {file = "multidict-6.7.0-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8cfc12a8630a29d601f48d47787bd7eb730e475e83edb5d6c5084317463373eb"}, + {file = "multidict-6.7.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3996b50c3237c4aec17459217c1e7bbdead9a22a0fcd3c365564fbd16439dde6"}, + {file = "multidict-6.7.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7f5170993a0dd3ab871c74f45c0a21a4e2c37a2f2b01b5f722a2ad9c6650469e"}, + {file = "multidict-6.7.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ec81878ddf0e98817def1e77d4f50dae5ef5b0e4fe796fae3bd674304172416e"}, + {file = "multidict-6.7.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:9281bf5b34f59afbc6b1e477a372e9526b66ca446f4bf62592839c195a718b32"}, + {file = "multidict-6.7.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:68af405971779d8b37198726f2b6fe3955db846fee42db7a4286fc542203934c"}, + {file = "multidict-6.7.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3ba3ef510467abb0667421a286dc906e30eb08569365f5cdb131d7aff7c2dd84"}, + {file = "multidict-6.7.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:b61189b29081a20c7e4e0b49b44d5d44bb0dc92be3c6d06a11cc043f81bf9329"}, + {file = "multidict-6.7.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:fb287618b9c7aa3bf8d825f02d9201b2f13078a5ed3b293c8f4d953917d84d5e"}, + {file = "multidict-6.7.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:521f33e377ff64b96c4c556b81c55d0cfffb96a11c194fd0c3f1e56f3d8dd5a4"}, + {file = "multidict-6.7.0-cp39-cp39-win32.whl", hash = "sha256:ce8fdc2dca699f8dbf055a61d73eaa10482569ad20ee3c36ef9641f69afa8c91"}, + {file = "multidict-6.7.0-cp39-cp39-win_amd64.whl", hash = "sha256:7e73299c99939f089dd9b2120a04a516b95cdf8c1cd2b18c53ebf0de80b1f18f"}, + {file = "multidict-6.7.0-cp39-cp39-win_arm64.whl", hash = "sha256:6bdce131e14b04fd34a809b6380dbfd826065c3e2fe8a50dbae659fa0c390546"}, + {file = "multidict-6.7.0-py3-none-any.whl", hash = "sha256:394fc5c42a333c9ffc3e421a4c85e08580d990e08b99f6bf35b4132114c5dcb3"}, + {file = "multidict-6.7.0.tar.gz", hash = "sha256:c6e99d9a65ca282e578dfea819cfa9c0a62b2499d8677392e09feaf305e9e6f5"}, +] + +[[package]] +name = "packaging" +version = "25.0" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"}, + {file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"}, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746"}, + {file = "pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["coverage", "pytest", "pytest-benchmark"] + +[[package]] +name = "propcache" +version = "0.4.1" +description = "Accelerated property cache" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "propcache-0.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c2d1fa3201efaf55d730400d945b5b3ab6e672e100ba0f9a409d950ab25d7db"}, + {file = "propcache-0.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1eb2994229cc8ce7fe9b3db88f5465f5fd8651672840b2e426b88cdb1a30aac8"}, + {file = "propcache-0.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:66c1f011f45a3b33d7bcb22daed4b29c0c9e2224758b6be00686731e1b46f925"}, + {file = "propcache-0.4.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9a52009f2adffe195d0b605c25ec929d26b36ef986ba85244891dee3b294df21"}, + {file = "propcache-0.4.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5d4e2366a9c7b837555cf02fb9be2e3167d333aff716332ef1b7c3a142ec40c5"}, + {file = "propcache-0.4.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:9d2b6caef873b4f09e26ea7e33d65f42b944837563a47a94719cc3544319a0db"}, + {file = "propcache-0.4.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2b16ec437a8c8a965ecf95739448dd938b5c7f56e67ea009f4300d8df05f32b7"}, + {file = "propcache-0.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:296f4c8ed03ca7476813fe666c9ea97869a8d7aec972618671b33a38a5182ef4"}, + {file = "propcache-0.4.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:1f0978529a418ebd1f49dad413a2b68af33f85d5c5ca5c6ca2a3bed375a7ac60"}, + {file = "propcache-0.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fd138803047fb4c062b1c1dd95462f5209456bfab55c734458f15d11da288f8f"}, + {file = "propcache-0.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8c9b3cbe4584636d72ff556d9036e0c9317fa27b3ac1f0f558e7e84d1c9c5900"}, + {file = "propcache-0.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f93243fdc5657247533273ac4f86ae106cc6445a0efacb9a1bfe982fcfefd90c"}, + {file = "propcache-0.4.1-cp310-cp310-win32.whl", hash = "sha256:a0ee98db9c5f80785b266eb805016e36058ac72c51a064040f2bc43b61101cdb"}, + {file = "propcache-0.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:1cdb7988c4e5ac7f6d175a28a9aa0c94cb6f2ebe52756a3c0cda98d2809a9e37"}, + {file = "propcache-0.4.1-cp310-cp310-win_arm64.whl", hash = "sha256:d82ad62b19645419fe79dd63b3f9253e15b30e955c0170e5cebc350c1844e581"}, + {file = "propcache-0.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:60a8fda9644b7dfd5dece8c61d8a85e271cb958075bfc4e01083c148b61a7caf"}, + {file = "propcache-0.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c30b53e7e6bda1d547cabb47c825f3843a0a1a42b0496087bb58d8fedf9f41b5"}, + {file = "propcache-0.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6918ecbd897443087a3b7cd978d56546a812517dcaaca51b49526720571fa93e"}, + {file = "propcache-0.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3d902a36df4e5989763425a8ab9e98cd8ad5c52c823b34ee7ef307fd50582566"}, + {file = "propcache-0.4.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a9695397f85973bb40427dedddf70d8dc4a44b22f1650dd4af9eedf443d45165"}, + {file = "propcache-0.4.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2bb07ffd7eaad486576430c89f9b215f9e4be68c4866a96e97db9e97fead85dc"}, + {file = "propcache-0.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fd6f30fdcf9ae2a70abd34da54f18da086160e4d7d9251f81f3da0ff84fc5a48"}, + {file = "propcache-0.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fc38cba02d1acba4e2869eef1a57a43dfbd3d49a59bf90dda7444ec2be6a5570"}, + {file = "propcache-0.4.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:67fad6162281e80e882fb3ec355398cf72864a54069d060321f6cd0ade95fe85"}, + {file = "propcache-0.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f10207adf04d08bec185bae14d9606a1444715bc99180f9331c9c02093e1959e"}, + {file = "propcache-0.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e9b0d8d0845bbc4cfcdcbcdbf5086886bc8157aa963c31c777ceff7846c77757"}, + {file = "propcache-0.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:981333cb2f4c1896a12f4ab92a9cc8f09ea664e9b7dbdc4eff74627af3a11c0f"}, + {file = "propcache-0.4.1-cp311-cp311-win32.whl", hash = "sha256:f1d2f90aeec838a52f1c1a32fe9a619fefd5e411721a9117fbf82aea638fe8a1"}, + {file = "propcache-0.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:364426a62660f3f699949ac8c621aad6977be7126c5807ce48c0aeb8e7333ea6"}, + {file = "propcache-0.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:e53f3a38d3510c11953f3e6a33f205c6d1b001129f972805ca9b42fc308bc239"}, + {file = "propcache-0.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e153e9cd40cc8945138822807139367f256f89c6810c2634a4f6902b52d3b4e2"}, + {file = "propcache-0.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cd547953428f7abb73c5ad82cbb32109566204260d98e41e5dfdc682eb7f8403"}, + {file = "propcache-0.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f048da1b4f243fc44f205dfd320933a951b8d89e0afd4c7cacc762a8b9165207"}, + {file = "propcache-0.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ec17c65562a827bba85e3872ead335f95405ea1674860d96483a02f5c698fa72"}, + {file = "propcache-0.4.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:405aac25c6394ef275dee4c709be43745d36674b223ba4eb7144bf4d691b7367"}, + {file = "propcache-0.4.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0013cb6f8dde4b2a2f66903b8ba740bdfe378c943c4377a200551ceb27f379e4"}, + {file = "propcache-0.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:15932ab57837c3368b024473a525e25d316d8353016e7cc0e5ba9eb343fbb1cf"}, + {file = "propcache-0.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:031dce78b9dc099f4c29785d9cf5577a3faf9ebf74ecbd3c856a7b92768c3df3"}, + {file = "propcache-0.4.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ab08df6c9a035bee56e31af99be621526bd237bea9f32def431c656b29e41778"}, + {file = "propcache-0.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4d7af63f9f93fe593afbf104c21b3b15868efb2c21d07d8732c0c4287e66b6a6"}, + {file = "propcache-0.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:cfc27c945f422e8b5071b6e93169679e4eb5bf73bbcbf1ba3ae3a83d2f78ebd9"}, + {file = "propcache-0.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:35c3277624a080cc6ec6f847cbbbb5b49affa3598c4535a0a4682a697aaa5c75"}, + {file = "propcache-0.4.1-cp312-cp312-win32.whl", hash = "sha256:671538c2262dadb5ba6395e26c1731e1d52534bfe9ae56d0b5573ce539266aa8"}, + {file = "propcache-0.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:cb2d222e72399fcf5890d1d5cc1060857b9b236adff2792ff48ca2dfd46c81db"}, + {file = "propcache-0.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:204483131fb222bdaaeeea9f9e6c6ed0cac32731f75dfc1d4a567fc1926477c1"}, + {file = "propcache-0.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:43eedf29202c08550aac1d14e0ee619b0430aaef78f85864c1a892294fbc28cf"}, + {file = "propcache-0.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d62cdfcfd89ccb8de04e0eda998535c406bf5e060ffd56be6c586cbcc05b3311"}, + {file = "propcache-0.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cae65ad55793da34db5f54e4029b89d3b9b9490d8abe1b4c7ab5d4b8ec7ebf74"}, + {file = "propcache-0.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:333ddb9031d2704a301ee3e506dc46b1fe5f294ec198ed6435ad5b6a085facfe"}, + {file = "propcache-0.4.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:fd0858c20f078a32cf55f7e81473d96dcf3b93fd2ccdb3d40fdf54b8573df3af"}, + {file = "propcache-0.4.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:678ae89ebc632c5c204c794f8dab2837c5f159aeb59e6ed0539500400577298c"}, + {file = "propcache-0.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d472aeb4fbf9865e0c6d622d7f4d54a4e101a89715d8904282bb5f9a2f476c3f"}, + {file = "propcache-0.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4d3df5fa7e36b3225954fba85589da77a0fe6a53e3976de39caf04a0db4c36f1"}, + {file = "propcache-0.4.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:ee17f18d2498f2673e432faaa71698032b0127ebf23ae5974eeaf806c279df24"}, + {file = "propcache-0.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:580e97762b950f993ae618e167e7be9256b8353c2dcd8b99ec100eb50f5286aa"}, + {file = "propcache-0.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:501d20b891688eb8e7aa903021f0b72d5a55db40ffaab27edefd1027caaafa61"}, + {file = "propcache-0.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a0bd56e5b100aef69bd8562b74b46254e7c8812918d3baa700c8a8009b0af66"}, + {file = "propcache-0.4.1-cp313-cp313-win32.whl", hash = "sha256:bcc9aaa5d80322bc2fb24bb7accb4a30f81e90ab8d6ba187aec0744bc302ad81"}, + {file = "propcache-0.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:381914df18634f5494334d201e98245c0596067504b9372d8cf93f4bb23e025e"}, + {file = "propcache-0.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:8873eb4460fd55333ea49b7d189749ecf6e55bf85080f11b1c4530ed3034cba1"}, + {file = "propcache-0.4.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:92d1935ee1f8d7442da9c0c4fa7ac20d07e94064184811b685f5c4fada64553b"}, + {file = "propcache-0.4.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:473c61b39e1460d386479b9b2f337da492042447c9b685f28be4f74d3529e566"}, + {file = "propcache-0.4.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:c0ef0aaafc66fbd87842a3fe3902fd889825646bc21149eafe47be6072725835"}, + {file = "propcache-0.4.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f95393b4d66bfae908c3ca8d169d5f79cd65636ae15b5e7a4f6e67af675adb0e"}, + {file = "propcache-0.4.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c07fda85708bc48578467e85099645167a955ba093be0a2dcba962195676e859"}, + {file = "propcache-0.4.1-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:af223b406d6d000830c6f65f1e6431783fc3f713ba3e6cc8c024d5ee96170a4b"}, + {file = "propcache-0.4.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a78372c932c90ee474559c5ddfffd718238e8673c340dc21fe45c5b8b54559a0"}, + {file = "propcache-0.4.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:564d9f0d4d9509e1a870c920a89b2fec951b44bf5ba7d537a9e7c1ccec2c18af"}, + {file = "propcache-0.4.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:17612831fda0138059cc5546f4d12a2aacfb9e47068c06af35c400ba58ba7393"}, + {file = "propcache-0.4.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:41a89040cb10bd345b3c1a873b2bf36413d48da1def52f268a055f7398514874"}, + {file = "propcache-0.4.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e35b88984e7fa64aacecea39236cee32dd9bd8c55f57ba8a75cf2399553f9bd7"}, + {file = "propcache-0.4.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6f8b465489f927b0df505cbe26ffbeed4d6d8a2bbc61ce90eb074ff129ef0ab1"}, + {file = "propcache-0.4.1-cp313-cp313t-win32.whl", hash = "sha256:2ad890caa1d928c7c2965b48f3a3815c853180831d0e5503d35cf00c472f4717"}, + {file = "propcache-0.4.1-cp313-cp313t-win_amd64.whl", hash = "sha256:f7ee0e597f495cf415bcbd3da3caa3bd7e816b74d0d52b8145954c5e6fd3ff37"}, + {file = "propcache-0.4.1-cp313-cp313t-win_arm64.whl", hash = "sha256:929d7cbe1f01bb7baffb33dc14eb5691c95831450a26354cd210a8155170c93a"}, + {file = "propcache-0.4.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3f7124c9d820ba5548d431afb4632301acf965db49e666aa21c305cbe8c6de12"}, + {file = "propcache-0.4.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:c0d4b719b7da33599dfe3b22d3db1ef789210a0597bc650b7cee9c77c2be8c5c"}, + {file = "propcache-0.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9f302f4783709a78240ebc311b793f123328716a60911d667e0c036bc5dcbded"}, + {file = "propcache-0.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c80ee5802e3fb9ea37938e7eecc307fb984837091d5fd262bb37238b1ae97641"}, + {file = "propcache-0.4.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ed5a841e8bb29a55fb8159ed526b26adc5bdd7e8bd7bf793ce647cb08656cdf4"}, + {file = "propcache-0.4.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:55c72fd6ea2da4c318e74ffdf93c4fe4e926051133657459131a95c846d16d44"}, + {file = "propcache-0.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8326e144341460402713f91df60ade3c999d601e7eb5ff8f6f7862d54de0610d"}, + {file = "propcache-0.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:060b16ae65bc098da7f6d25bf359f1f31f688384858204fe5d652979e0015e5b"}, + {file = "propcache-0.4.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:89eb3fa9524f7bec9de6e83cf3faed9d79bffa560672c118a96a171a6f55831e"}, + {file = "propcache-0.4.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:dee69d7015dc235f526fe80a9c90d65eb0039103fe565776250881731f06349f"}, + {file = "propcache-0.4.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:5558992a00dfd54ccbc64a32726a3357ec93825a418a401f5cc67df0ac5d9e49"}, + {file = "propcache-0.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c9b822a577f560fbd9554812526831712c1436d2c046cedee4c3796d3543b144"}, + {file = "propcache-0.4.1-cp314-cp314-win32.whl", hash = "sha256:ab4c29b49d560fe48b696cdcb127dd36e0bc2472548f3bf56cc5cb3da2b2984f"}, + {file = "propcache-0.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:5a103c3eb905fcea0ab98be99c3a9a5ab2de60228aa5aceedc614c0281cf6153"}, + {file = "propcache-0.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:74c1fb26515153e482e00177a1ad654721bf9207da8a494a0c05e797ad27b992"}, + {file = "propcache-0.4.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:824e908bce90fb2743bd6b59db36eb4f45cd350a39637c9f73b1c1ea66f5b75f"}, + {file = "propcache-0.4.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:c2b5e7db5328427c57c8e8831abda175421b709672f6cfc3d630c3b7e2146393"}, + {file = "propcache-0.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6f6ff873ed40292cd4969ef5310179afd5db59fdf055897e282485043fc80ad0"}, + {file = "propcache-0.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:49a2dc67c154db2c1463013594c458881a069fcf98940e61a0569016a583020a"}, + {file = "propcache-0.4.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:005f08e6a0529984491e37d8dbc3dd86f84bd78a8ceb5fa9a021f4c48d4984be"}, + {file = "propcache-0.4.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5c3310452e0d31390da9035c348633b43d7e7feb2e37be252be6da45abd1abcc"}, + {file = "propcache-0.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c3c70630930447f9ef1caac7728c8ad1c56bc5015338b20fed0d08ea2480b3a"}, + {file = "propcache-0.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8e57061305815dfc910a3634dcf584f08168a8836e6999983569f51a8544cd89"}, + {file = "propcache-0.4.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:521a463429ef54143092c11a77e04056dd00636f72e8c45b70aaa3140d639726"}, + {file = "propcache-0.4.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:120c964da3fdc75e3731aa392527136d4ad35868cc556fd09bb6d09172d9a367"}, + {file = "propcache-0.4.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:d8f353eb14ee3441ee844ade4277d560cdd68288838673273b978e3d6d2c8f36"}, + {file = "propcache-0.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ab2943be7c652f09638800905ee1bab2c544e537edb57d527997a24c13dc1455"}, + {file = "propcache-0.4.1-cp314-cp314t-win32.whl", hash = "sha256:05674a162469f31358c30bcaa8883cb7829fa3110bf9c0991fe27d7896c42d85"}, + {file = "propcache-0.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:990f6b3e2a27d683cb7602ed6c86f15ee6b43b1194736f9baaeb93d0016633b1"}, + {file = "propcache-0.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:ecef2343af4cc68e05131e45024ba34f6095821988a9d0a02aa7c73fcc448aa9"}, + {file = "propcache-0.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3d233076ccf9e450c8b3bc6720af226b898ef5d051a2d145f7d765e6e9f9bcff"}, + {file = "propcache-0.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:357f5bb5c377a82e105e44bd3d52ba22b616f7b9773714bff93573988ef0a5fb"}, + {file = "propcache-0.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cbc3b6dfc728105b2a57c06791eb07a94229202ea75c59db644d7d496b698cac"}, + {file = "propcache-0.4.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:182b51b421f0501952d938dc0b0eb45246a5b5153c50d42b495ad5fb7517c888"}, + {file = "propcache-0.4.1-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4b536b39c5199b96fc6245eb5fb796c497381d3942f169e44e8e392b29c9ebcc"}, + {file = "propcache-0.4.1-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:db65d2af507bbfbdcedb254a11149f894169d90488dd3e7190f7cdcb2d6cd57a"}, + {file = "propcache-0.4.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fd2dbc472da1f772a4dae4fa24be938a6c544671a912e30529984dd80400cd88"}, + {file = "propcache-0.4.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:daede9cd44e0f8bdd9e6cc9a607fc81feb80fae7a5fc6cecaff0e0bb32e42d00"}, + {file = "propcache-0.4.1-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:71b749281b816793678ae7f3d0d84bd36e694953822eaad408d682efc5ca18e0"}, + {file = "propcache-0.4.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:0002004213ee1f36cfb3f9a42b5066100c44276b9b72b4e1504cddd3d692e86e"}, + {file = "propcache-0.4.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:fe49d0a85038f36ba9e3ffafa1103e61170b28e95b16622e11be0a0ea07c6781"}, + {file = "propcache-0.4.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:99d43339c83aaf4d32bda60928231848eee470c6bda8d02599cc4cebe872d183"}, + {file = "propcache-0.4.1-cp39-cp39-win32.whl", hash = "sha256:a129e76735bc792794d5177069691c3217898b9f5cee2b2661471e52ffe13f19"}, + {file = "propcache-0.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:948dab269721ae9a87fd16c514a0a2c2a1bdb23a9a61b969b0f9d9ee2968546f"}, + {file = "propcache-0.4.1-cp39-cp39-win_arm64.whl", hash = "sha256:5fd37c406dd6dc85aa743e214cef35dc54bbdd1419baac4f6ae5e5b1a2976938"}, + {file = "propcache-0.4.1-py3-none-any.whl", hash = "sha256:af2a6052aeb6cf17d3e46ee169099044fd8224cbaf75c76a2ef596e8163e2237"}, + {file = "propcache-0.4.1.tar.gz", hash = "sha256:f48107a8c637e80362555f37ecf49abe20370e557cc4ab374f04ec4423c97c3d"}, +] + +[[package]] +name = "protobuf" +version = "6.32.1" +description = "" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "protobuf-6.32.1-cp310-abi3-win32.whl", hash = "sha256:a8a32a84bc9f2aad712041b8b366190f71dde248926da517bde9e832e4412085"}, + {file = "protobuf-6.32.1-cp310-abi3-win_amd64.whl", hash = "sha256:b00a7d8c25fa471f16bc8153d0e53d6c9e827f0953f3c09aaa4331c718cae5e1"}, + {file = "protobuf-6.32.1-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:d8c7e6eb619ffdf105ee4ab76af5a68b60a9d0f66da3ea12d1640e6d8dab7281"}, + {file = "protobuf-6.32.1-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:2f5b80a49e1eb7b86d85fcd23fe92df154b9730a725c3b38c4e43b9d77018bf4"}, + {file = "protobuf-6.32.1-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:b1864818300c297265c83a4982fd3169f97122c299f56a56e2445c3698d34710"}, + {file = "protobuf-6.32.1-cp39-cp39-win32.whl", hash = "sha256:68ff170bac18c8178f130d1ccb94700cf72852298e016a2443bdb9502279e5f1"}, + {file = "protobuf-6.32.1-cp39-cp39-win_amd64.whl", hash = "sha256:d0975d0b2f3e6957111aa3935d08a0eb7e006b1505d825f862a1fffc8348e122"}, + {file = "protobuf-6.32.1-py3-none-any.whl", hash = "sha256:2601b779fc7d32a866c6b4404f9d42a3f67c5b9f3f15b4db3cccabe06b95c346"}, + {file = "protobuf-6.32.1.tar.gz", hash = "sha256:ee2469e4a021474ab9baafea6cd070e5bf27c7d29433504ddea1a4ee5850f68d"}, +] + +[[package]] +name = "py-cpuinfo" +version = "9.0.0" +description = "Get CPU info with pure Python" +optional = false +python-versions = "*" +groups = ["dev"] +files = [ + {file = "py-cpuinfo-9.0.0.tar.gz", hash = "sha256:3cdbbf3fac90dc6f118bfd64384f309edeadd902d7c8fb17f02ffa1fc3f49690"}, + {file = "py_cpuinfo-9.0.0-py3-none-any.whl", hash = "sha256:859625bc251f64e21f077d099d4162689c762b5d6a4c3c97553d56241c9674d5"}, +] + +[[package]] +name = "pyasn1" +version = "0.6.1" +description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629"}, + {file = "pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034"}, +] + +[[package]] +name = "pyasn1-modules" +version = "0.4.2" +description = "A collection of ASN.1-based protocols modules" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "pyasn1_modules-0.4.2-py3-none-any.whl", hash = "sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a"}, + {file = "pyasn1_modules-0.4.2.tar.gz", hash = "sha256:677091de870a80aae844b1ca6134f54652fa2c8c5a52aa396440ac3106e941e6"}, +] + +[package.dependencies] +pyasn1 = ">=0.6.1,<0.7.0" + +[[package]] +name = "pycparser" +version = "2.23" +description = "C parser in Python" +optional = false +python-versions = ">=3.8" +groups = ["main"] +markers = "platform_python_implementation != \"PyPy\" and implementation_name != \"PyPy\"" +files = [ + {file = "pycparser-2.23-py3-none-any.whl", hash = "sha256:e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934"}, + {file = "pycparser-2.23.tar.gz", hash = "sha256:78816d4f24add8f10a06d6f05b4d424ad9e96cfebf68a4ddc99c65c0720d00c2"}, +] + +[[package]] +name = "pycryptodome" +version = "3.23.0" +description = "Cryptographic library for Python" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["main"] +files = [ + {file = "pycryptodome-3.23.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a176b79c49af27d7f6c12e4b178b0824626f40a7b9fed08f712291b6d54bf566"}, + {file = "pycryptodome-3.23.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:573a0b3017e06f2cffd27d92ef22e46aa3be87a2d317a5abf7cc0e84e321bd75"}, + {file = "pycryptodome-3.23.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:63dad881b99ca653302b2c7191998dd677226222a3f2ea79999aa51ce695f720"}, + {file = "pycryptodome-3.23.0-cp27-cp27m-win32.whl", hash = "sha256:b34e8e11d97889df57166eda1e1ddd7676da5fcd4d71a0062a760e75060514b4"}, + {file = "pycryptodome-3.23.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:7ac1080a8da569bde76c0a104589c4f414b8ba296c0b3738cf39a466a9fb1818"}, + {file = "pycryptodome-3.23.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:6fe8258e2039eceb74dfec66b3672552b6b7d2c235b2dfecc05d16b8921649a8"}, + {file = "pycryptodome-3.23.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:0011f7f00cdb74879142011f95133274741778abba114ceca229adbf8e62c3e4"}, + {file = "pycryptodome-3.23.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:90460fc9e088ce095f9ee8356722d4f10f86e5be06e2354230a9880b9c549aae"}, + {file = "pycryptodome-3.23.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4764e64b269fc83b00f682c47443c2e6e85b18273712b98aa43bcb77f8570477"}, + {file = "pycryptodome-3.23.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb8f24adb74984aa0e5d07a2368ad95276cf38051fe2dc6605cbcf482e04f2a7"}, + {file = "pycryptodome-3.23.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d97618c9c6684a97ef7637ba43bdf6663a2e2e77efe0f863cce97a76af396446"}, + {file = "pycryptodome-3.23.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9a53a4fe5cb075075d515797d6ce2f56772ea7e6a1e5e4b96cf78a14bac3d265"}, + {file = "pycryptodome-3.23.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:763d1d74f56f031788e5d307029caef067febf890cd1f8bf61183ae142f1a77b"}, + {file = "pycryptodome-3.23.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:954af0e2bd7cea83ce72243b14e4fb518b18f0c1649b576d114973e2073b273d"}, + {file = "pycryptodome-3.23.0-cp313-cp313t-win32.whl", hash = "sha256:257bb3572c63ad8ba40b89f6fc9d63a2a628e9f9708d31ee26560925ebe0210a"}, + {file = "pycryptodome-3.23.0-cp313-cp313t-win_amd64.whl", hash = "sha256:6501790c5b62a29fcb227bd6b62012181d886a767ce9ed03b303d1f22eb5c625"}, + {file = "pycryptodome-3.23.0-cp313-cp313t-win_arm64.whl", hash = "sha256:9a77627a330ab23ca43b48b130e202582e91cc69619947840ea4d2d1be21eb39"}, + {file = "pycryptodome-3.23.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:187058ab80b3281b1de11c2e6842a357a1f71b42cb1e15bce373f3d238135c27"}, + {file = "pycryptodome-3.23.0-cp37-abi3-macosx_10_9_x86_64.whl", hash = "sha256:cfb5cd445280c5b0a4e6187a7ce8de5a07b5f3f897f235caa11f1f435f182843"}, + {file = "pycryptodome-3.23.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67bd81fcbe34f43ad9422ee8fd4843c8e7198dd88dd3d40e6de42ee65fbe1490"}, + {file = "pycryptodome-3.23.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8987bd3307a39bc03df5c8e0e3d8be0c4c3518b7f044b0f4c15d1aa78f52575"}, + {file = "pycryptodome-3.23.0-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aa0698f65e5b570426fc31b8162ed4603b0c2841cbb9088e2b01641e3065915b"}, + {file = "pycryptodome-3.23.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:53ecbafc2b55353edcebd64bf5da94a2a2cdf5090a6915bcca6eca6cc452585a"}, + {file = "pycryptodome-3.23.0-cp37-abi3-musllinux_1_2_i686.whl", hash = "sha256:156df9667ad9f2ad26255926524e1c136d6664b741547deb0a86a9acf5ea631f"}, + {file = "pycryptodome-3.23.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:dea827b4d55ee390dc89b2afe5927d4308a8b538ae91d9c6f7a5090f397af1aa"}, + {file = "pycryptodome-3.23.0-cp37-abi3-win32.whl", hash = "sha256:507dbead45474b62b2bbe318eb1c4c8ee641077532067fec9c1aa82c31f84886"}, + {file = "pycryptodome-3.23.0-cp37-abi3-win_amd64.whl", hash = "sha256:c75b52aacc6c0c260f204cbdd834f76edc9fb0d8e0da9fbf8352ef58202564e2"}, + {file = "pycryptodome-3.23.0-cp37-abi3-win_arm64.whl", hash = "sha256:11eeeb6917903876f134b56ba11abe95c0b0fd5e3330def218083c7d98bbcb3c"}, + {file = "pycryptodome-3.23.0-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:350ebc1eba1da729b35ab7627a833a1a355ee4e852d8ba0447fafe7b14504d56"}, + {file = "pycryptodome-3.23.0-pp27-pypy_73-win32.whl", hash = "sha256:93837e379a3e5fd2bb00302a47aee9fdf7940d83595be3915752c74033d17ca7"}, + {file = "pycryptodome-3.23.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ddb95b49df036ddd264a0ad246d1be5b672000f12d6961ea2c267083a5e19379"}, + {file = "pycryptodome-3.23.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e95564beb8782abfd9e431c974e14563a794a4944c29d6d3b7b5ea042110b4"}, + {file = "pycryptodome-3.23.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14e15c081e912c4b0d75632acd8382dfce45b258667aa3c67caf7a4d4c13f630"}, + {file = "pycryptodome-3.23.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7fc76bf273353dc7e5207d172b83f569540fc9a28d63171061c42e361d22353"}, + {file = "pycryptodome-3.23.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:45c69ad715ca1a94f778215a11e66b7ff989d792a4d63b68dc586a1da1392ff5"}, + {file = "pycryptodome-3.23.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:865d83c906b0fc6a59b510deceee656b6bc1c4fa0d82176e2b77e97a420a996a"}, + {file = "pycryptodome-3.23.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89d4d56153efc4d81defe8b65fd0821ef8b2d5ddf8ed19df31ba2f00872b8002"}, + {file = "pycryptodome-3.23.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3f2d0aaf8080bda0587d58fc9fe4766e012441e2eed4269a77de6aea981c8be"}, + {file = "pycryptodome-3.23.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:64093fc334c1eccfd3933c134c4457c34eaca235eeae49d69449dc4728079339"}, + {file = "pycryptodome-3.23.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:ce64e84a962b63a47a592690bdc16a7eaf709d2c2697ababf24a0def566899a6"}, + {file = "pycryptodome-3.23.0.tar.gz", hash = "sha256:447700a657182d60338bab09fdb27518f8856aecd80ae4c6bdddb67ff5da44ef"}, +] + +[[package]] +name = "pycryptodomex" +version = "3.23.0" +description = "Cryptographic library for Python" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["main"] +files = [ + {file = "pycryptodomex-3.23.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:add243d204e125f189819db65eed55e6b4713f70a7e9576c043178656529cec7"}, + {file = "pycryptodomex-3.23.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:1c6d919fc8429e5cb228ba8c0d4d03d202a560b421c14867a65f6042990adc8e"}, + {file = "pycryptodomex-3.23.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:1c3a65ad441746b250d781910d26b7ed0a396733c6f2dbc3327bd7051ec8a541"}, + {file = "pycryptodomex-3.23.0-cp27-cp27m-win32.whl", hash = "sha256:47f6d318fe864d02d5e59a20a18834819596c4ed1d3c917801b22b92b3ffa648"}, + {file = "pycryptodomex-3.23.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:d9825410197a97685d6a1fa2a86196430b01877d64458a20e95d4fd00d739a08"}, + {file = "pycryptodomex-3.23.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:267a3038f87a8565bd834317dbf053a02055915acf353bf42ededb9edaf72010"}, + {file = "pycryptodomex-3.23.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:7b37e08e3871efe2187bc1fd9320cc81d87caf19816c648f24443483005ff886"}, + {file = "pycryptodomex-3.23.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:91979028227543010d7b2ba2471cf1d1e398b3f183cb105ac584df0c36dac28d"}, + {file = "pycryptodomex-3.23.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b8962204c47464d5c1c4038abeadd4514a133b28748bcd9fa5b6d62e3cec6fa"}, + {file = "pycryptodomex-3.23.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a33986a0066860f7fcf7c7bd2bc804fa90e434183645595ae7b33d01f3c91ed8"}, + {file = "pycryptodomex-3.23.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7947ab8d589e3178da3d7cdeabe14f841b391e17046954f2fbcd941705762b5"}, + {file = "pycryptodomex-3.23.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c25e30a20e1b426e1f0fa00131c516f16e474204eee1139d1603e132acffc314"}, + {file = "pycryptodomex-3.23.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:da4fa650cef02db88c2b98acc5434461e027dce0ae8c22dd5a69013eaf510006"}, + {file = "pycryptodomex-3.23.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:58b851b9effd0d072d4ca2e4542bf2a4abcf13c82a29fd2c93ce27ee2a2e9462"}, + {file = "pycryptodomex-3.23.0-cp313-cp313t-win32.whl", hash = "sha256:a9d446e844f08299236780f2efa9898c818fe7e02f17263866b8550c7d5fb328"}, + {file = "pycryptodomex-3.23.0-cp313-cp313t-win_amd64.whl", hash = "sha256:bc65bdd9fc8de7a35a74cab1c898cab391a4add33a8fe740bda00f5976ca4708"}, + {file = "pycryptodomex-3.23.0-cp313-cp313t-win_arm64.whl", hash = "sha256:c885da45e70139464f082018ac527fdaad26f1657a99ee13eecdce0f0ca24ab4"}, + {file = "pycryptodomex-3.23.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:06698f957fe1ab229a99ba2defeeae1c09af185baa909a31a5d1f9d42b1aaed6"}, + {file = "pycryptodomex-3.23.0-cp37-abi3-macosx_10_9_x86_64.whl", hash = "sha256:b2c2537863eccef2d41061e82a881dcabb04944c5c06c5aa7110b577cc487545"}, + {file = "pycryptodomex-3.23.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:43c446e2ba8df8889e0e16f02211c25b4934898384c1ec1ec04d7889c0333587"}, + {file = "pycryptodomex-3.23.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f489c4765093fb60e2edafdf223397bc716491b2b69fe74367b70d6999257a5c"}, + {file = "pycryptodomex-3.23.0-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bdc69d0d3d989a1029df0eed67cc5e8e5d968f3724f4519bd03e0ec68df7543c"}, + {file = "pycryptodomex-3.23.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:6bbcb1dd0f646484939e142462d9e532482bc74475cecf9c4903d4e1cd21f003"}, + {file = "pycryptodomex-3.23.0-cp37-abi3-musllinux_1_2_i686.whl", hash = "sha256:8a4fcd42ccb04c31268d1efeecfccfd1249612b4de6374205376b8f280321744"}, + {file = "pycryptodomex-3.23.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:55ccbe27f049743a4caf4f4221b166560d3438d0b1e5ab929e07ae1702a4d6fd"}, + {file = "pycryptodomex-3.23.0-cp37-abi3-win32.whl", hash = "sha256:189afbc87f0b9f158386bf051f720e20fa6145975f1e76369303d0f31d1a8d7c"}, + {file = "pycryptodomex-3.23.0-cp37-abi3-win_amd64.whl", hash = "sha256:52e5ca58c3a0b0bd5e100a9fbc8015059b05cffc6c66ce9d98b4b45e023443b9"}, + {file = "pycryptodomex-3.23.0-cp37-abi3-win_arm64.whl", hash = "sha256:02d87b80778c171445d67e23d1caef279bf4b25c3597050ccd2e13970b57fd51"}, + {file = "pycryptodomex-3.23.0-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:febec69c0291efd056c65691b6d9a339f8b4bc43c6635b8699471248fe897fea"}, + {file = "pycryptodomex-3.23.0-pp27-pypy_73-win32.whl", hash = "sha256:c84b239a1f4ec62e9c789aafe0543f0594f0acd90c8d9e15bcece3efe55eca66"}, + {file = "pycryptodomex-3.23.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ebfff755c360d674306e5891c564a274a47953562b42fb74a5c25b8fc1fb1cb5"}, + {file = "pycryptodomex-3.23.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eca54f4bb349d45afc17e3011ed4264ef1cc9e266699874cdd1349c504e64798"}, + {file = "pycryptodomex-3.23.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2596e643d4365e14d0879dc5aafe6355616c61c2176009270f3048f6d9a61f"}, + {file = "pycryptodomex-3.23.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fdfac7cda115bca3a5abb2f9e43bc2fb66c2b65ab074913643803ca7083a79ea"}, + {file = "pycryptodomex-3.23.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:14c37aaece158d0ace436f76a7bb19093db3b4deade9797abfc39ec6cd6cc2fe"}, + {file = "pycryptodomex-3.23.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7de1e40a41a5d7f1ac42b6569b10bcdded34339950945948529067d8426d2785"}, + {file = "pycryptodomex-3.23.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bffc92138d75664b6d543984db7893a628559b9e78658563b0395e2a5fb47ed9"}, + {file = "pycryptodomex-3.23.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df027262368334552db2c0ce39706b3fb32022d1dce34673d0f9422df004b96a"}, + {file = "pycryptodomex-3.23.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4e79f1aaff5a3a374e92eb462fa9e598585452135012e2945f96874ca6eeb1ff"}, + {file = "pycryptodomex-3.23.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:27e13c80ac9a0a1d050ef0a7e0a18cc04c8850101ec891815b6c5a0375e8a245"}, + {file = "pycryptodomex-3.23.0.tar.gz", hash = "sha256:71909758f010c82bc99b0abf4ea12012c98962fbf0583c2164f8b84533c2e4da"}, +] + +[[package]] +name = "pydantic" +version = "2.12.0" +description = "Data validation using Python type hints" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "pydantic-2.12.0-py3-none-any.whl", hash = "sha256:f6a1da352d42790537e95e83a8bdfb91c7efbae63ffd0b86fa823899e807116f"}, + {file = "pydantic-2.12.0.tar.gz", hash = "sha256:c1a077e6270dbfb37bfd8b498b3981e2bb18f68103720e51fa6c306a5a9af563"}, +] + +[package.dependencies] +annotated-types = ">=0.6.0" +pydantic-core = "2.41.1" +typing-extensions = ">=4.14.1" +typing-inspection = ">=0.4.2" + +[package.extras] +email = ["email-validator (>=2.0.0)"] +timezone = ["tzdata"] + +[[package]] +name = "pydantic-core" +version = "2.41.1" +description = "Core functionality for Pydantic validation and serialization" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "pydantic_core-2.41.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:e63036298322e9aea1c8b7c0a6c1204d615dbf6ec0668ce5b83ff27f07404a61"}, + {file = "pydantic_core-2.41.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:241299ca91fc77ef64f11ed909d2d9220a01834e8e6f8de61275c4dd16b7c936"}, + {file = "pydantic_core-2.41.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ab7e594a2a5c24ab8013a7dc8cfe5f2260e80e490685814122081705c2cf2b0"}, + {file = "pydantic_core-2.41.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b054ef1a78519cb934b58e9c90c09e93b837c935dcd907b891f2b265b129eb6e"}, + {file = "pydantic_core-2.41.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f2ab7d10d0ab2ed6da54c757233eb0f48ebfb4f86e9b88ccecb3f92bbd61a538"}, + {file = "pydantic_core-2.41.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2757606b7948bb853a27e4040820306eaa0ccb9e8f9f8a0fa40cb674e170f350"}, + {file = "pydantic_core-2.41.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cec0e75eb61f606bad0a32f2be87507087514e26e8c73db6cbdb8371ccd27917"}, + {file = "pydantic_core-2.41.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0234236514f44a5bf552105cfe2543a12f48203397d9d0f866affa569345a5b5"}, + {file = "pydantic_core-2.41.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:1b974e41adfbb4ebb0f65fc4ca951347b17463d60893ba7d5f7b9bb087c83897"}, + {file = "pydantic_core-2.41.1-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:248dafb3204136113c383e91a4d815269f51562b6659b756cf3df14eefc7d0bb"}, + {file = "pydantic_core-2.41.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:678f9d76a91d6bcedd7568bbf6beb77ae8447f85d1aeebaab7e2f0829cfc3a13"}, + {file = "pydantic_core-2.41.1-cp310-cp310-win32.whl", hash = "sha256:dff5bee1d21ee58277900692a641925d2dddfde65182c972569b1a276d2ac8fb"}, + {file = "pydantic_core-2.41.1-cp310-cp310-win_amd64.whl", hash = "sha256:5042da12e5d97d215f91567110fdfa2e2595a25f17c19b9ff024f31c34f9b53e"}, + {file = "pydantic_core-2.41.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4f276a6134fe1fc1daa692642a3eaa2b7b858599c49a7610816388f5e37566a1"}, + {file = "pydantic_core-2.41.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:07588570a805296ece009c59d9a679dc08fab72fb337365afb4f3a14cfbfc176"}, + {file = "pydantic_core-2.41.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28527e4b53400cd60ffbd9812ccb2b5135d042129716d71afd7e45bf42b855c0"}, + {file = "pydantic_core-2.41.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:46a1c935c9228bad738c8a41de06478770927baedf581d172494ab36a6b96575"}, + {file = "pydantic_core-2.41.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:447ddf56e2b7d28d200d3e9eafa936fe40485744b5a824b67039937580b3cb20"}, + {file = "pydantic_core-2.41.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:63892ead40c1160ac860b5debcc95c95c5a0035e543a8b5a4eac70dd22e995f4"}, + {file = "pydantic_core-2.41.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f4a9543ca355e6df8fbe9c83e9faab707701e9103ae857ecb40f1c0cf8b0e94d"}, + {file = "pydantic_core-2.41.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f2611bdb694116c31e551ed82e20e39a90bea9b7ad9e54aaf2d045ad621aa7a1"}, + {file = "pydantic_core-2.41.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fecc130893a9b5f7bfe230be1bb8c61fe66a19db8ab704f808cb25a82aad0bc9"}, + {file = "pydantic_core-2.41.1-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:1e2df5f8344c99b6ea5219f00fdc8950b8e6f2c422fbc1cc122ec8641fac85a1"}, + {file = "pydantic_core-2.41.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:35291331e9d8ed94c257bab6be1cb3a380b5eee570a2784bffc055e18040a2ea"}, + {file = "pydantic_core-2.41.1-cp311-cp311-win32.whl", hash = "sha256:2876a095292668d753f1a868c4a57c4ac9f6acbd8edda8debe4218d5848cf42f"}, + {file = "pydantic_core-2.41.1-cp311-cp311-win_amd64.whl", hash = "sha256:b92d6c628e9a338846a28dfe3fcdc1a3279388624597898b105e078cdfc59298"}, + {file = "pydantic_core-2.41.1-cp311-cp311-win_arm64.whl", hash = "sha256:7d82ae99409eb69d507a89835488fb657faa03ff9968a9379567b0d2e2e56bc5"}, + {file = "pydantic_core-2.41.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:db2f82c0ccbce8f021ad304ce35cbe02aa2f95f215cac388eed542b03b4d5eb4"}, + {file = "pydantic_core-2.41.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:47694a31c710ced9205d5f1e7e8af3ca57cbb8a503d98cb9e33e27c97a501601"}, + {file = "pydantic_core-2.41.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:93e9decce94daf47baf9e9d392f5f2557e783085f7c5e522011545d9d6858e00"}, + {file = "pydantic_core-2.41.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ab0adafdf2b89c8b84f847780a119437a0931eca469f7b44d356f2b426dd9741"}, + {file = "pydantic_core-2.41.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5da98cc81873f39fd56882e1569c4677940fbc12bce6213fad1ead784192d7c8"}, + {file = "pydantic_core-2.41.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:209910e88afb01fd0fd403947b809ba8dba0e08a095e1f703294fda0a8fdca51"}, + {file = "pydantic_core-2.41.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:365109d1165d78d98e33c5bfd815a9b5d7d070f578caefaabcc5771825b4ecb5"}, + {file = "pydantic_core-2.41.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:706abf21e60a2857acdb09502bc853ee5bce732955e7b723b10311114f033115"}, + {file = "pydantic_core-2.41.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:bf0bd5417acf7f6a7ec3b53f2109f587be176cb35f9cf016da87e6017437a72d"}, + {file = "pydantic_core-2.41.1-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:2e71b1c6ceb9c78424ae9f63a07292fb769fb890a4e7efca5554c47f33a60ea5"}, + {file = "pydantic_core-2.41.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:80745b9770b4a38c25015b517451c817799bfb9d6499b0d13d8227ec941cb513"}, + {file = "pydantic_core-2.41.1-cp312-cp312-win32.whl", hash = "sha256:83b64d70520e7890453f1aa21d66fda44e7b35f1cfea95adf7b4289a51e2b479"}, + {file = "pydantic_core-2.41.1-cp312-cp312-win_amd64.whl", hash = "sha256:377defd66ee2003748ee93c52bcef2d14fde48fe28a0b156f88c3dbf9bc49a50"}, + {file = "pydantic_core-2.41.1-cp312-cp312-win_arm64.whl", hash = "sha256:c95caff279d49c1d6cdfe2996e6c2ad712571d3b9caaa209a404426c326c4bde"}, + {file = "pydantic_core-2.41.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:70e790fce5f05204ef4403159857bfcd587779da78627b0babb3654f75361ebf"}, + {file = "pydantic_core-2.41.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9cebf1ca35f10930612d60bd0f78adfacee824c30a880e3534ba02c207cceceb"}, + {file = "pydantic_core-2.41.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:170406a37a5bc82c22c3274616bf6f17cc7df9c4a0a0a50449e559cb755db669"}, + {file = "pydantic_core-2.41.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:12d4257fc9187a0ccd41b8b327d6a4e57281ab75e11dda66a9148ef2e1fb712f"}, + {file = "pydantic_core-2.41.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a75a33b4db105dd1c8d57839e17ee12db8d5ad18209e792fa325dbb4baeb00f4"}, + {file = "pydantic_core-2.41.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:08a589f850803a74e0fcb16a72081cafb0d72a3cdda500106942b07e76b7bf62"}, + {file = "pydantic_core-2.41.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a97939d6ea44763c456bd8a617ceada2c9b96bb5b8ab3dfa0d0827df7619014"}, + {file = "pydantic_core-2.41.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d2ae423c65c556f09569524b80ffd11babff61f33055ef9773d7c9fabc11ed8d"}, + {file = "pydantic_core-2.41.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:4dc703015fbf8764d6a8001c327a87f1823b7328d40b47ce6000c65918ad2b4f"}, + {file = "pydantic_core-2.41.1-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:968e4ffdfd35698a5fe659e5e44c508b53664870a8e61c8f9d24d3d145d30257"}, + {file = "pydantic_core-2.41.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:fff2b76c8e172d34771cd4d4f0ade08072385310f214f823b5a6ad4006890d32"}, + {file = "pydantic_core-2.41.1-cp313-cp313-win32.whl", hash = "sha256:a38a5263185407ceb599f2f035faf4589d57e73c7146d64f10577f6449e8171d"}, + {file = "pydantic_core-2.41.1-cp313-cp313-win_amd64.whl", hash = "sha256:b42ae7fd6760782c975897e1fdc810f483b021b32245b0105d40f6e7a3803e4b"}, + {file = "pydantic_core-2.41.1-cp313-cp313-win_arm64.whl", hash = "sha256:ad4111acc63b7384e205c27a2f15e23ac0ee21a9d77ad6f2e9cb516ec90965fb"}, + {file = "pydantic_core-2.41.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:440d0df7415b50084a4ba9d870480c16c5f67c0d1d4d5119e3f70925533a0edc"}, + {file = "pydantic_core-2.41.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:71eaa38d342099405dae6484216dcf1e8e4b0bebd9b44a4e08c9b43db6a2ab67"}, + {file = "pydantic_core-2.41.1-cp313-cp313t-win_amd64.whl", hash = "sha256:555ecf7e50f1161d3f693bc49f23c82cf6cdeafc71fa37a06120772a09a38795"}, + {file = "pydantic_core-2.41.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:05226894a26f6f27e1deb735d7308f74ef5fa3a6de3e0135bb66cdcaee88f64b"}, + {file = "pydantic_core-2.41.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:85ff7911c6c3e2fd8d3779c50925f6406d770ea58ea6dde9c230d35b52b16b4a"}, + {file = "pydantic_core-2.41.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47f1f642a205687d59b52dc1a9a607f45e588f5a2e9eeae05edd80c7a8c47674"}, + {file = "pydantic_core-2.41.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:df11c24e138876ace5ec6043e5cae925e34cf38af1a1b3d63589e8f7b5f5cdc4"}, + {file = "pydantic_core-2.41.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7f0bf7f5c8f7bf345c527e8a0d72d6b26eda99c1227b0c34e7e59e181260de31"}, + {file = "pydantic_core-2.41.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:82b887a711d341c2c47352375d73b029418f55b20bd7815446d175a70effa706"}, + {file = "pydantic_core-2.41.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b5f1d5d6bbba484bdf220c72d8ecd0be460f4bd4c5e534a541bb2cd57589fb8b"}, + {file = "pydantic_core-2.41.1-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2bf1917385ebe0f968dc5c6ab1375886d56992b93ddfe6bf52bff575d03662be"}, + {file = "pydantic_core-2.41.1-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:4f94f3ab188f44b9a73f7295663f3ecb8f2e2dd03a69c8f2ead50d37785ecb04"}, + {file = "pydantic_core-2.41.1-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:3925446673641d37c30bd84a9d597e49f72eacee8b43322c8999fa17d5ae5bc4"}, + {file = "pydantic_core-2.41.1-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:49bd51cc27adb980c7b97357ae036ce9b3c4d0bb406e84fbe16fb2d368b602a8"}, + {file = "pydantic_core-2.41.1-cp314-cp314-win32.whl", hash = "sha256:a31ca0cd0e4d12ea0df0077df2d487fc3eb9d7f96bbb13c3c5b88dcc21d05159"}, + {file = "pydantic_core-2.41.1-cp314-cp314-win_amd64.whl", hash = "sha256:1b5c4374a152e10a22175d7790e644fbd8ff58418890e07e2073ff9d4414efae"}, + {file = "pydantic_core-2.41.1-cp314-cp314-win_arm64.whl", hash = "sha256:4fee76d757639b493eb600fba668f1e17475af34c17dd61db7a47e824d464ca9"}, + {file = "pydantic_core-2.41.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f9b9c968cfe5cd576fdd7361f47f27adeb120517e637d1b189eea1c3ece573f4"}, + {file = "pydantic_core-2.41.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1ebc7ab67b856384aba09ed74e3e977dded40e693de18a4f197c67d0d4e6d8e"}, + {file = "pydantic_core-2.41.1-cp314-cp314t-win_amd64.whl", hash = "sha256:8ae0dc57b62a762985bc7fbf636be3412394acc0ddb4ade07fe104230f1b9762"}, + {file = "pydantic_core-2.41.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:10ce489cf09a4956a1549af839b983edc59b0f60e1b068c21b10154e58f54f80"}, + {file = "pydantic_core-2.41.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ff548c908caffd9455fd1342366bcf8a1ec8a3fca42f35c7fc60883d6a901074"}, + {file = "pydantic_core-2.41.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d43bf082025082bda13be89a5f876cc2386b7727c7b322be2d2b706a45cea8e"}, + {file = "pydantic_core-2.41.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:666aee751faf1c6864b2db795775dd67b61fdcf646abefa309ed1da039a97209"}, + {file = "pydantic_core-2.41.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b83aaeff0d7bde852c32e856f3ee410842ebc08bc55c510771d87dcd1c01e1ed"}, + {file = "pydantic_core-2.41.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:055c7931b0329cb8acde20cdde6d9c2cbc2a02a0a8e54a792cddd91e2ea92c65"}, + {file = "pydantic_core-2.41.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:530bbb1347e3e5ca13a91ac087c4971d7da09630ef8febd27a20a10800c2d06d"}, + {file = "pydantic_core-2.41.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:65a0ea16cfea7bfa9e43604c8bd726e63a3788b61c384c37664b55209fcb1d74"}, + {file = "pydantic_core-2.41.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:8fa93fadff794c6d15c345c560513b160197342275c6d104cc879f932b978afc"}, + {file = "pydantic_core-2.41.1-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:c8a1af9ac51969a494c6a82b563abae6859dc082d3b999e8fa7ba5ee1b05e8e8"}, + {file = "pydantic_core-2.41.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:30edab28829703f876897c9471a857e43d847b8799c3c9e2fbce644724b50aa4"}, + {file = "pydantic_core-2.41.1-cp39-cp39-win32.whl", hash = "sha256:84d0ff869f98be2e93efdf1ae31e5a15f0926d22af8677d51676e373abbfe57a"}, + {file = "pydantic_core-2.41.1-cp39-cp39-win_amd64.whl", hash = "sha256:b5674314987cdde5a5511b029fa5fb1556b3d147a367e01dd583b19cfa8e35df"}, + {file = "pydantic_core-2.41.1-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:68f2251559b8efa99041bb63571ec7cdd2d715ba74cc82b3bc9eff824ebc8bf0"}, + {file = "pydantic_core-2.41.1-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:c7bc140c596097cb53b30546ca257dbe3f19282283190b1b5142928e5d5d3a20"}, + {file = "pydantic_core-2.41.1-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2896510fce8f4725ec518f8b9d7f015a00db249d2fd40788f442af303480063d"}, + {file = "pydantic_core-2.41.1-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ced20e62cfa0f496ba68fa5d6c7ee71114ea67e2a5da3114d6450d7f4683572a"}, + {file = "pydantic_core-2.41.1-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:b04fa9ed049461a7398138c604b00550bc89e3e1151d84b81ad6dc93e39c4c06"}, + {file = "pydantic_core-2.41.1-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:b3b7d9cfbfdc43c80a16638c6dc2768e3956e73031fca64e8e1a3ae744d1faeb"}, + {file = "pydantic_core-2.41.1-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eec83fc6abef04c7f9bec616e2d76ee9a6a4ae2a359b10c21d0f680e24a247ca"}, + {file = "pydantic_core-2.41.1-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6771a2d9f83c4038dfad5970a3eef215940682b2175e32bcc817bdc639019b28"}, + {file = "pydantic_core-2.41.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:fabcbdb12de6eada8d6e9a759097adb3c15440fafc675b3e94ae5c9cb8d678a0"}, + {file = "pydantic_core-2.41.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:80e97ccfaf0aaf67d55de5085b0ed0d994f57747d9d03f2de5cc9847ca737b08"}, + {file = "pydantic_core-2.41.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34df1fe8fea5d332484a763702e8b6a54048a9d4fe6ccf41e34a128238e01f52"}, + {file = "pydantic_core-2.41.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:421b5595f845842fc093f7250e24ee395f54ca62d494fdde96f43ecf9228ae01"}, + {file = "pydantic_core-2.41.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:dce8b22663c134583aaad24827863306a933f576c79da450be3984924e2031d1"}, + {file = "pydantic_core-2.41.1-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:300a9c162fea9906cc5c103893ca2602afd84f0ec90d3be36f4cc360125d22e1"}, + {file = "pydantic_core-2.41.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e019167628f6e6161ae7ab9fb70f6d076a0bf0d55aa9b20833f86a320c70dd65"}, + {file = "pydantic_core-2.41.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:13ab9cc2de6f9d4ab645a050ae5aee61a2424ac4d3a16ba23d4c2027705e0301"}, + {file = "pydantic_core-2.41.1-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:af2385d3f98243fb733862f806c5bb9122e5fba05b373e3af40e3c82d711cef1"}, + {file = "pydantic_core-2.41.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:6550617a0c2115be56f90c31a5370261d8ce9dbf051c3ed53b51172dd34da696"}, + {file = "pydantic_core-2.41.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc17b6ecf4983d298686014c92ebc955a9f9baf9f57dad4065e7906e7bee6222"}, + {file = "pydantic_core-2.41.1-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:42ae9352cf211f08b04ea110563d6b1e415878eea5b4c70f6bdb17dca3b932d2"}, + {file = "pydantic_core-2.41.1-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:e82947de92068b0a21681a13dd2102387197092fbe7defcfb8453e0913866506"}, + {file = "pydantic_core-2.41.1-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:e244c37d5471c9acdcd282890c6c4c83747b77238bfa19429b8473586c907656"}, + {file = "pydantic_core-2.41.1-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:1e798b4b304a995110d41ec93653e57975620ccb2842ba9420037985e7d7284e"}, + {file = "pydantic_core-2.41.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:f1fc716c0eb1663c59699b024428ad5ec2bcc6b928527b8fe28de6cb89f47efb"}, + {file = "pydantic_core-2.41.1.tar.gz", hash = "sha256:1ad375859a6d8c356b7704ec0f547a58e82ee80bb41baa811ad710e124bc8f2f"}, +] + +[package.dependencies] +typing-extensions = ">=4.14.1" + +[[package]] +name = "pygments" +version = "2.19.2" +description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b"}, + {file = "pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887"}, +] + +[package.extras] +windows-terminal = ["colorama (>=0.4.6)"] + +[[package]] +name = "pyopenssl" +version = "24.0.0" +description = "Python wrapper module around the OpenSSL library" +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "pyOpenSSL-24.0.0-py3-none-any.whl", hash = "sha256:ba07553fb6fd6a7a2259adb9b84e12302a9a8a75c44046e8bb5d3e5ee887e3c3"}, + {file = "pyOpenSSL-24.0.0.tar.gz", hash = "sha256:6aa33039a93fffa4563e655b61d11364d01264be8ccb49906101e02a334530bf"}, +] + +[package.dependencies] +cryptography = ">=41.0.5,<43" + +[package.extras] +docs = ["sphinx (!=5.2.0,!=5.2.0.post0,!=7.2.5)", "sphinx-rtd-theme"] +test = ["flaky", "pretend", "pytest (>=3.0.1)"] + +[[package]] +name = "pyreadline3" +version = "3.5.4" +description = "A python implementation of GNU readline." +optional = false +python-versions = ">=3.8" +groups = ["main"] +markers = "sys_platform == \"win32\"" +files = [ + {file = "pyreadline3-3.5.4-py3-none-any.whl", hash = "sha256:eaf8e6cc3c49bcccf145fc6067ba8643d1df34d604a1ec0eccbf7a18e6d3fae6"}, + {file = "pyreadline3-3.5.4.tar.gz", hash = "sha256:8d57d53039a1c75adba8e50dd3d992b28143480816187ea5efbd5c78e6c885b7"}, +] + +[package.extras] +dev = ["build", "flake8", "mypy", "pytest", "twine"] + +[[package]] +name = "pytest" +version = "8.4.2" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79"}, + {file = "pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01"}, +] + +[package.dependencies] +colorama = {version = ">=0.4", markers = "sys_platform == \"win32\""} +iniconfig = ">=1" +packaging = ">=20" +pluggy = ">=1.5,<2" +pygments = ">=2.7.2" + +[package.extras] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "pytest-asyncio" +version = "0.25.3" +description = "Pytest support for asyncio" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "pytest_asyncio-0.25.3-py3-none-any.whl", hash = "sha256:9e89518e0f9bd08928f97a3482fdc4e244df17529460bc038291ccaf8f85c7c3"}, + {file = "pytest_asyncio-0.25.3.tar.gz", hash = "sha256:fc1da2cf9f125ada7e710b4ddad05518d4cee187ae9412e9ac9271003497f07a"}, +] + +[package.dependencies] +pytest = ">=8.2,<9" + +[package.extras] +docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1)"] +testing = ["coverage (>=6.2)", "hypothesis (>=5.7.1)"] + +[[package]] +name = "pytest-benchmark" +version = "5.1.0" +description = "A ``pytest`` fixture for benchmarking code. It will group the tests into rounds that are calibrated to the chosen timer." +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "pytest-benchmark-5.1.0.tar.gz", hash = "sha256:9ea661cdc292e8231f7cd4c10b0319e56a2118e2c09d9f50e1b3d150d2aca105"}, + {file = "pytest_benchmark-5.1.0-py3-none-any.whl", hash = "sha256:922de2dfa3033c227c96da942d1878191afa135a29485fb942e85dff1c592c89"}, +] + +[package.dependencies] +py-cpuinfo = "*" +pytest = ">=8.1" + +[package.extras] +aspect = ["aspectlib"] +elasticsearch = ["elasticsearch"] +histogram = ["pygal", "pygaljs", "setuptools"] + +[[package]] +name = "pytest-cov" +version = "6.3.0" +description = "Pytest plugin for measuring coverage." +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "pytest_cov-6.3.0-py3-none-any.whl", hash = "sha256:440db28156d2468cafc0415b4f8e50856a0d11faefa38f30906048fe490f1749"}, + {file = "pytest_cov-6.3.0.tar.gz", hash = "sha256:35c580e7800f87ce892e687461166e1ac2bcb8fb9e13aea79032518d6e503ff2"}, +] + +[package.dependencies] +coverage = {version = ">=7.5", extras = ["toml"]} +pluggy = ">=1.2" +pytest = ">=6.2.5" + +[package.extras] +testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +description = "Extensions to the standard Python datetime module" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +groups = ["main"] +files = [ + {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, + {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, +] + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "python-registry" +version = "1.3.1" +description = "Read access to Windows Registry files." +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "python-registry-1.3.1.tar.gz", hash = "sha256:99185f67d5601be3e7843e55902d5769aea1740869b0882f34ff1bd4b43b1eb2"}, + {file = "python_registry-1.3.1-py2-none-any.whl", hash = "sha256:59d3b00c04bca0c4e1a12be0404da6ccf76b87537ee3a3ad2d8fc1bccf6f63ca"}, + {file = "python_registry-1.3.1-py3-none-any.whl", hash = "sha256:b5b8ae07c271dce12dacd24e16af8aa8d56167ebdb360112a4f152b6d04a4ca9"}, +] + +[package.dependencies] +enum-compat = "*" +unicodecsv = "*" + +[[package]] +name = "ruff" +version = "0.12.12" +description = "An extremely fast Python linter and code formatter, written in Rust." +optional = false +python-versions = ">=3.7" +groups = ["dev"] +files = [ + {file = "ruff-0.12.12-py3-none-linux_armv6l.whl", hash = "sha256:de1c4b916d98ab289818e55ce481e2cacfaad7710b01d1f990c497edf217dafc"}, + {file = "ruff-0.12.12-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:7acd6045e87fac75a0b0cdedacf9ab3e1ad9d929d149785903cff9bb69ad9727"}, + {file = "ruff-0.12.12-py3-none-macosx_11_0_arm64.whl", hash = "sha256:abf4073688d7d6da16611f2f126be86523a8ec4343d15d276c614bda8ec44edb"}, + {file = "ruff-0.12.12-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:968e77094b1d7a576992ac078557d1439df678a34c6fe02fd979f973af167577"}, + {file = "ruff-0.12.12-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42a67d16e5b1ffc6d21c5f67851e0e769517fb57a8ebad1d0781b30888aa704e"}, + {file = "ruff-0.12.12-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b216ec0a0674e4b1214dcc998a5088e54eaf39417327b19ffefba1c4a1e4971e"}, + {file = "ruff-0.12.12-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:59f909c0fdd8f1dcdbfed0b9569b8bf428cf144bec87d9de298dcd4723f5bee8"}, + {file = "ruff-0.12.12-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9ac93d87047e765336f0c18eacad51dad0c1c33c9df7484c40f98e1d773876f5"}, + {file = "ruff-0.12.12-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:01543c137fd3650d322922e8b14cc133b8ea734617c4891c5a9fccf4bfc9aa92"}, + {file = "ruff-0.12.12-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2afc2fa864197634e549d87fb1e7b6feb01df0a80fd510d6489e1ce8c0b1cc45"}, + {file = "ruff-0.12.12-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:0c0945246f5ad776cb8925e36af2438e66188d2b57d9cf2eed2c382c58b371e5"}, + {file = "ruff-0.12.12-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:a0fbafe8c58e37aae28b84a80ba1817f2ea552e9450156018a478bf1fa80f4e4"}, + {file = "ruff-0.12.12-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:b9c456fb2fc8e1282affa932c9e40f5ec31ec9cbb66751a316bd131273b57c23"}, + {file = "ruff-0.12.12-py3-none-musllinux_1_2_i686.whl", hash = "sha256:5f12856123b0ad0147d90b3961f5c90e7427f9acd4b40050705499c98983f489"}, + {file = "ruff-0.12.12-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:26a1b5a2bf7dd2c47e3b46d077cd9c0fc3b93e6c6cc9ed750bd312ae9dc302ee"}, + {file = "ruff-0.12.12-py3-none-win32.whl", hash = "sha256:173be2bfc142af07a01e3a759aba6f7791aa47acf3604f610b1c36db888df7b1"}, + {file = "ruff-0.12.12-py3-none-win_amd64.whl", hash = "sha256:e99620bf01884e5f38611934c09dd194eb665b0109104acae3ba6102b600fd0d"}, + {file = "ruff-0.12.12-py3-none-win_arm64.whl", hash = "sha256:2a8199cab4ce4d72d158319b63370abf60991495fb733db96cd923a34c52d093"}, + {file = "ruff-0.12.12.tar.gz", hash = "sha256:b86cd3415dbe31b3b46a71c598f4c4b2f550346d1ccf6326b347cc0c8fd063d6"}, +] + +[[package]] +name = "setuptools" +version = "80.9.0" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "setuptools-80.9.0-py3-none-any.whl", hash = "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922"}, + {file = "setuptools-80.9.0.tar.gz", hash = "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c"}, +] + +[package.extras] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)", "ruff (>=0.8.0)"] +core = ["importlib_metadata (>=6)", "jaraco.functools (>=4)", "jaraco.text (>=3.7)", "more_itertools", "more_itertools (>=8.8)", "packaging (>=24.2)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] +cover = ["pytest-cov"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"] +enabler = ["pytest-enabler (>=2.2)"] +test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.7.2)", "jaraco.test (>=5.5)", "packaging (>=24.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] +type = ["importlib_metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (==1.14.*)", "pytest-mypy"] + +[[package]] +name = "six" +version = "1.17.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +groups = ["main"] +files = [ + {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, + {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, +] + +[[package]] +name = "typing-extensions" +version = "4.15.0" +description = "Backported and Experimental Type Hints for Python 3.9+" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"}, + {file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"}, +] + +[[package]] +name = "typing-inspection" +version = "0.4.2" +description = "Runtime typing introspection tools" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7"}, + {file = "typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464"}, +] + +[package.dependencies] +typing-extensions = ">=4.12.0" + +[[package]] +name = "unicodecsv" +version = "0.14.1" +description = "Python2's stdlib csv module is nice, but it doesn't support unicode. This module is a drop-in replacement which *does*." +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "unicodecsv-0.14.1.tar.gz", hash = "sha256:018c08037d48649a0412063ff4eda26eaa81eff1546dbffa51fa5293276ff7fc"}, +] + +[[package]] +name = "werkzeug" +version = "3.1.3" +description = "The comprehensive WSGI web application library." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "werkzeug-3.1.3-py3-none-any.whl", hash = "sha256:54b78bf3716d19a65be4fceccc0d1d7b89e608834989dfae50ea87564639213e"}, + {file = "werkzeug-3.1.3.tar.gz", hash = "sha256:60723ce945c19328679790e3282cc758aa4a6040e4bb330f53d30fa546d44746"}, +] + +[package.dependencies] +MarkupSafe = ">=2.1.1" + +[package.extras] +watchdog = ["watchdog (>=2.3)"] + +[[package]] +name = "yarl" +version = "1.22.0" +description = "Yet another URL library" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "yarl-1.22.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c7bd6683587567e5a49ee6e336e0612bec8329be1b7d4c8af5687dcdeb67ee1e"}, + {file = "yarl-1.22.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5cdac20da754f3a723cceea5b3448e1a2074866406adeb4ef35b469d089adb8f"}, + {file = "yarl-1.22.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:07a524d84df0c10f41e3ee918846e1974aba4ec017f990dc735aad487a0bdfdf"}, + {file = "yarl-1.22.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e1b329cb8146d7b736677a2440e422eadd775d1806a81db2d4cded80a48efc1a"}, + {file = "yarl-1.22.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:75976c6945d85dbb9ee6308cd7ff7b1fb9409380c82d6119bd778d8fcfe2931c"}, + {file = "yarl-1.22.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:80ddf7a5f8c86cb3eb4bc9028b07bbbf1f08a96c5c0bc1244be5e8fefcb94147"}, + {file = "yarl-1.22.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d332fc2e3c94dad927f2112395772a4e4fedbcf8f80efc21ed7cdfae4d574fdb"}, + {file = "yarl-1.22.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0cf71bf877efeac18b38d3930594c0948c82b64547c1cf420ba48722fe5509f6"}, + {file = "yarl-1.22.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:663e1cadaddae26be034a6ab6072449a8426ddb03d500f43daf952b74553bba0"}, + {file = "yarl-1.22.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:6dcbb0829c671f305be48a7227918cfcd11276c2d637a8033a99a02b67bf9eda"}, + {file = "yarl-1.22.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f0d97c18dfd9a9af4490631905a3f131a8e4c9e80a39353919e2cfed8f00aedc"}, + {file = "yarl-1.22.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:437840083abe022c978470b942ff832c3940b2ad3734d424b7eaffcd07f76737"}, + {file = "yarl-1.22.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a899cbd98dce6f5d8de1aad31cb712ec0a530abc0a86bd6edaa47c1090138467"}, + {file = "yarl-1.22.0-cp310-cp310-win32.whl", hash = "sha256:595697f68bd1f0c1c159fcb97b661fc9c3f5db46498043555d04805430e79bea"}, + {file = "yarl-1.22.0-cp310-cp310-win_amd64.whl", hash = "sha256:cb95a9b1adaa48e41815a55ae740cfda005758104049a640a398120bf02515ca"}, + {file = "yarl-1.22.0-cp310-cp310-win_arm64.whl", hash = "sha256:b85b982afde6df99ecc996990d4ad7ccbdbb70e2a4ba4de0aecde5922ba98a0b"}, + {file = "yarl-1.22.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1ab72135b1f2db3fed3997d7e7dc1b80573c67138023852b6efb336a5eae6511"}, + {file = "yarl-1.22.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:669930400e375570189492dc8d8341301578e8493aec04aebc20d4717f899dd6"}, + {file = "yarl-1.22.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:792a2af6d58177ef7c19cbf0097aba92ca1b9cb3ffdd9c7470e156c8f9b5e028"}, + {file = "yarl-1.22.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3ea66b1c11c9150f1372f69afb6b8116f2dd7286f38e14ea71a44eee9ec51b9d"}, + {file = "yarl-1.22.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3e2daa88dc91870215961e96a039ec73e4937da13cf77ce17f9cad0c18df3503"}, + {file = "yarl-1.22.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ba440ae430c00eee41509353628600212112cd5018d5def7e9b05ea7ac34eb65"}, + {file = "yarl-1.22.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e6438cc8f23a9c1478633d216b16104a586b9761db62bfacb6425bac0a36679e"}, + {file = "yarl-1.22.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c52a6e78aef5cf47a98ef8e934755abf53953379b7d53e68b15ff4420e6683d"}, + {file = "yarl-1.22.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3b06bcadaac49c70f4c88af4ffcfbe3dc155aab3163e75777818092478bcbbe7"}, + {file = "yarl-1.22.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:6944b2dc72c4d7f7052683487e3677456050ff77fcf5e6204e98caf785ad1967"}, + {file = "yarl-1.22.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:d5372ca1df0f91a86b047d1277c2aaf1edb32d78bbcefffc81b40ffd18f027ed"}, + {file = "yarl-1.22.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:51af598701f5299012b8416486b40fceef8c26fc87dc6d7d1f6fc30609ea0aa6"}, + {file = "yarl-1.22.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b266bd01fedeffeeac01a79ae181719ff848a5a13ce10075adbefc8f1daee70e"}, + {file = "yarl-1.22.0-cp311-cp311-win32.whl", hash = "sha256:a9b1ba5610a4e20f655258d5a1fdc7ebe3d837bb0e45b581398b99eb98b1f5ca"}, + {file = "yarl-1.22.0-cp311-cp311-win_amd64.whl", hash = "sha256:078278b9b0b11568937d9509b589ee83ef98ed6d561dfe2020e24a9fd08eaa2b"}, + {file = "yarl-1.22.0-cp311-cp311-win_arm64.whl", hash = "sha256:b6a6f620cfe13ccec221fa312139135166e47ae169f8253f72a0abc0dae94376"}, + {file = "yarl-1.22.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e340382d1afa5d32b892b3ff062436d592ec3d692aeea3bef3a5cfe11bbf8c6f"}, + {file = "yarl-1.22.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f1e09112a2c31ffe8d80be1b0988fa6a18c5d5cad92a9ffbb1c04c91bfe52ad2"}, + {file = "yarl-1.22.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:939fe60db294c786f6b7c2d2e121576628468f65453d86b0fe36cb52f987bd74"}, + {file = "yarl-1.22.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e1651bf8e0398574646744c1885a41198eba53dc8a9312b954073f845c90a8df"}, + {file = "yarl-1.22.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b8a0588521a26bf92a57a1705b77b8b59044cdceccac7151bd8d229e66b8dedb"}, + {file = "yarl-1.22.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:42188e6a615c1a75bcaa6e150c3fe8f3e8680471a6b10150c5f7e83f47cc34d2"}, + {file = "yarl-1.22.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f6d2cb59377d99718913ad9a151030d6f83ef420a2b8f521d94609ecc106ee82"}, + {file = "yarl-1.22.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50678a3b71c751d58d7908edc96d332af328839eea883bb554a43f539101277a"}, + {file = "yarl-1.22.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1e8fbaa7cec507aa24ea27a01456e8dd4b6fab829059b69844bd348f2d467124"}, + {file = "yarl-1.22.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:433885ab5431bc3d3d4f2f9bd15bfa1614c522b0f1405d62c4f926ccd69d04fa"}, + {file = "yarl-1.22.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:b790b39c7e9a4192dc2e201a282109ed2985a1ddbd5ac08dc56d0e121400a8f7"}, + {file = "yarl-1.22.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:31f0b53913220599446872d757257be5898019c85e7971599065bc55065dc99d"}, + {file = "yarl-1.22.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a49370e8f711daec68d09b821a34e1167792ee2d24d405cbc2387be4f158b520"}, + {file = "yarl-1.22.0-cp312-cp312-win32.whl", hash = "sha256:70dfd4f241c04bd9239d53b17f11e6ab672b9f1420364af63e8531198e3f5fe8"}, + {file = "yarl-1.22.0-cp312-cp312-win_amd64.whl", hash = "sha256:8884d8b332a5e9b88e23f60bb166890009429391864c685e17bd73a9eda9105c"}, + {file = "yarl-1.22.0-cp312-cp312-win_arm64.whl", hash = "sha256:ea70f61a47f3cc93bdf8b2f368ed359ef02a01ca6393916bc8ff877427181e74"}, + {file = "yarl-1.22.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8dee9c25c74997f6a750cd317b8ca63545169c098faee42c84aa5e506c819b53"}, + {file = "yarl-1.22.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:01e73b85a5434f89fc4fe27dcda2aff08ddf35e4d47bbbea3bdcd25321af538a"}, + {file = "yarl-1.22.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:22965c2af250d20c873cdbee8ff958fb809940aeb2e74ba5f20aaf6b7ac8c70c"}, + {file = "yarl-1.22.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b4f15793aa49793ec8d1c708ab7f9eded1aa72edc5174cae703651555ed1b601"}, + {file = "yarl-1.22.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5542339dcf2747135c5c85f68680353d5cb9ffd741c0f2e8d832d054d41f35a"}, + {file = "yarl-1.22.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5c401e05ad47a75869c3ab3e35137f8468b846770587e70d71e11de797d113df"}, + {file = "yarl-1.22.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:243dda95d901c733f5b59214d28b0120893d91777cb8aa043e6ef059d3cddfe2"}, + {file = "yarl-1.22.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bec03d0d388060058f5d291a813f21c011041938a441c593374da6077fe21b1b"}, + {file = "yarl-1.22.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b0748275abb8c1e1e09301ee3cf90c8a99678a4e92e4373705f2a2570d581273"}, + {file = "yarl-1.22.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:47fdb18187e2a4e18fda2c25c05d8251a9e4a521edaed757fef033e7d8498d9a"}, + {file = "yarl-1.22.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c7044802eec4524fde550afc28edda0dd5784c4c45f0be151a2d3ba017daca7d"}, + {file = "yarl-1.22.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:139718f35149ff544caba20fce6e8a2f71f1e39b92c700d8438a0b1d2a631a02"}, + {file = "yarl-1.22.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e1b51bebd221006d3d2f95fbe124b22b247136647ae5dcc8c7acafba66e5ee67"}, + {file = "yarl-1.22.0-cp313-cp313-win32.whl", hash = "sha256:d3e32536234a95f513bd374e93d717cf6b2231a791758de6c509e3653f234c95"}, + {file = "yarl-1.22.0-cp313-cp313-win_amd64.whl", hash = "sha256:47743b82b76d89a1d20b83e60d5c20314cbd5ba2befc9cda8f28300c4a08ed4d"}, + {file = "yarl-1.22.0-cp313-cp313-win_arm64.whl", hash = "sha256:5d0fcda9608875f7d052eff120c7a5da474a6796fe4d83e152e0e4d42f6d1a9b"}, + {file = "yarl-1.22.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:719ae08b6972befcba4310e49edb1161a88cdd331e3a694b84466bd938a6ab10"}, + {file = "yarl-1.22.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:47d8a5c446df1c4db9d21b49619ffdba90e77c89ec6e283f453856c74b50b9e3"}, + {file = "yarl-1.22.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:cfebc0ac8333520d2d0423cbbe43ae43c8838862ddb898f5ca68565e395516e9"}, + {file = "yarl-1.22.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4398557cbf484207df000309235979c79c4356518fd5c99158c7d38203c4da4f"}, + {file = "yarl-1.22.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2ca6fd72a8cd803be290d42f2dec5cdcd5299eeb93c2d929bf060ad9efaf5de0"}, + {file = "yarl-1.22.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ca1f59c4e1ab6e72f0a23c13fca5430f889634166be85dbf1013683e49e3278e"}, + {file = "yarl-1.22.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6c5010a52015e7c70f86eb967db0f37f3c8bd503a695a49f8d45700144667708"}, + {file = "yarl-1.22.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d7672ecf7557476642c88497c2f8d8542f8e36596e928e9bcba0e42e1e7d71f"}, + {file = "yarl-1.22.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:3b7c88eeef021579d600e50363e0b6ee4f7f6f728cd3486b9d0f3ee7b946398d"}, + {file = "yarl-1.22.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:f4afb5c34f2c6fecdcc182dfcfc6af6cccf1aa923eed4d6a12e9d96904e1a0d8"}, + {file = "yarl-1.22.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:59c189e3e99a59cf8d83cbb31d4db02d66cda5a1a4374e8a012b51255341abf5"}, + {file = "yarl-1.22.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:5a3bf7f62a289fa90f1990422dc8dff5a458469ea71d1624585ec3a4c8d6960f"}, + {file = "yarl-1.22.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:de6b9a04c606978fdfe72666fa216ffcf2d1a9f6a381058d4378f8d7b1e5de62"}, + {file = "yarl-1.22.0-cp313-cp313t-win32.whl", hash = "sha256:1834bb90991cc2999f10f97f5f01317f99b143284766d197e43cd5b45eb18d03"}, + {file = "yarl-1.22.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ff86011bd159a9d2dfc89c34cfd8aff12875980e3bd6a39ff097887520e60249"}, + {file = "yarl-1.22.0-cp313-cp313t-win_arm64.whl", hash = "sha256:7861058d0582b847bc4e3a4a4c46828a410bca738673f35a29ba3ca5db0b473b"}, + {file = "yarl-1.22.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:34b36c2c57124530884d89d50ed2c1478697ad7473efd59cfd479945c95650e4"}, + {file = "yarl-1.22.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:0dd9a702591ca2e543631c2a017e4a547e38a5c0f29eece37d9097e04a7ac683"}, + {file = "yarl-1.22.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:594fcab1032e2d2cc3321bb2e51271e7cd2b516c7d9aee780ece81b07ff8244b"}, + {file = "yarl-1.22.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f3d7a87a78d46a2e3d5b72587ac14b4c16952dd0887dbb051451eceac774411e"}, + {file = "yarl-1.22.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:852863707010316c973162e703bddabec35e8757e67fcb8ad58829de1ebc8590"}, + {file = "yarl-1.22.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:131a085a53bfe839a477c0845acf21efc77457ba2bcf5899618136d64f3303a2"}, + {file = "yarl-1.22.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:078a8aefd263f4d4f923a9677b942b445a2be970ca24548a8102689a3a8ab8da"}, + {file = "yarl-1.22.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bca03b91c323036913993ff5c738d0842fc9c60c4648e5c8d98331526df89784"}, + {file = "yarl-1.22.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:68986a61557d37bb90d3051a45b91fa3d5c516d177dfc6dd6f2f436a07ff2b6b"}, + {file = "yarl-1.22.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:4792b262d585ff0dff6bcb787f8492e40698443ec982a3568c2096433660c694"}, + {file = "yarl-1.22.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:ebd4549b108d732dba1d4ace67614b9545b21ece30937a63a65dd34efa19732d"}, + {file = "yarl-1.22.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f87ac53513d22240c7d59203f25cc3beac1e574c6cd681bbfd321987b69f95fd"}, + {file = "yarl-1.22.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:22b029f2881599e2f1b06f8f1db2ee63bd309e2293ba2d566e008ba12778b8da"}, + {file = "yarl-1.22.0-cp314-cp314-win32.whl", hash = "sha256:6a635ea45ba4ea8238463b4f7d0e721bad669f80878b7bfd1f89266e2ae63da2"}, + {file = "yarl-1.22.0-cp314-cp314-win_amd64.whl", hash = "sha256:0d6e6885777af0f110b0e5d7e5dda8b704efed3894da26220b7f3d887b839a79"}, + {file = "yarl-1.22.0-cp314-cp314-win_arm64.whl", hash = "sha256:8218f4e98d3c10d683584cb40f0424f4b9fd6e95610232dd75e13743b070ee33"}, + {file = "yarl-1.22.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:45c2842ff0e0d1b35a6bf1cd6c690939dacb617a70827f715232b2e0494d55d1"}, + {file = "yarl-1.22.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:d947071e6ebcf2e2bee8fce76e10faca8f7a14808ca36a910263acaacef08eca"}, + {file = "yarl-1.22.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:334b8721303e61b00019474cc103bdac3d7b1f65e91f0bfedeec2d56dfe74b53"}, + {file = "yarl-1.22.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e7ce67c34138a058fd092f67d07a72b8e31ff0c9236e751957465a24b28910c"}, + {file = "yarl-1.22.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d77e1b2c6d04711478cb1c4ab90db07f1609ccf06a287d5607fcd90dc9863acf"}, + {file = "yarl-1.22.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4647674b6150d2cae088fc07de2738a84b8bcedebef29802cf0b0a82ab6face"}, + {file = "yarl-1.22.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:efb07073be061c8f79d03d04139a80ba33cbd390ca8f0297aae9cce6411e4c6b"}, + {file = "yarl-1.22.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e51ac5435758ba97ad69617e13233da53908beccc6cfcd6c34bbed8dcbede486"}, + {file = "yarl-1.22.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:33e32a0dd0c8205efa8e83d04fc9f19313772b78522d1bdc7d9aed706bfd6138"}, + {file = "yarl-1.22.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:bf4a21e58b9cde0e401e683ebd00f6ed30a06d14e93f7c8fd059f8b6e8f87b6a"}, + {file = "yarl-1.22.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:e4b582bab49ac33c8deb97e058cd67c2c50dac0dd134874106d9c774fd272529"}, + {file = "yarl-1.22.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:0b5bcc1a9c4839e7e30b7b30dd47fe5e7e44fb7054ec29b5bb8d526aa1041093"}, + {file = "yarl-1.22.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:c0232bce2170103ec23c454e54a57008a9a72b5d1c3105dc2496750da8cfa47c"}, + {file = "yarl-1.22.0-cp314-cp314t-win32.whl", hash = "sha256:8009b3173bcd637be650922ac455946197d858b3630b6d8787aa9e5c4564533e"}, + {file = "yarl-1.22.0-cp314-cp314t-win_amd64.whl", hash = "sha256:9fb17ea16e972c63d25d4a97f016d235c78dd2344820eb35bc034bc32012ee27"}, + {file = "yarl-1.22.0-cp314-cp314t-win_arm64.whl", hash = "sha256:9f6d73c1436b934e3f01df1e1b21ff765cd1d28c77dfb9ace207f746d4610ee1"}, + {file = "yarl-1.22.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3aa27acb6de7a23785d81557577491f6c38a5209a254d1191519d07d8fe51748"}, + {file = "yarl-1.22.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:af74f05666a5e531289cb1cc9c883d1de2088b8e5b4de48004e5ca8a830ac859"}, + {file = "yarl-1.22.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:62441e55958977b8167b2709c164c91a6363e25da322d87ae6dd9c6019ceecf9"}, + {file = "yarl-1.22.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b580e71cac3f8113d3135888770903eaf2f507e9421e5697d6ee6d8cd1c7f054"}, + {file = "yarl-1.22.0-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e81fda2fb4a07eda1a2252b216aa0df23ebcd4d584894e9612e80999a78fd95b"}, + {file = "yarl-1.22.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:99b6fc1d55782461b78221e95fc357b47ad98b041e8e20f47c1411d0aacddc60"}, + {file = "yarl-1.22.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:088e4e08f033db4be2ccd1f34cf29fe994772fb54cfe004bbf54db320af56890"}, + {file = "yarl-1.22.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e4e1f6f0b4da23e61188676e3ed027ef0baa833a2e633c29ff8530800edccba"}, + {file = "yarl-1.22.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:84fc3ec96fce86ce5aa305eb4aa9358279d1aa644b71fab7b8ed33fe3ba1a7ca"}, + {file = "yarl-1.22.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:5dbeefd6ca588b33576a01b0ad58aa934bc1b41ef89dee505bf2932b22ddffba"}, + {file = "yarl-1.22.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:14291620375b1060613f4aab9ebf21850058b6b1b438f386cc814813d901c60b"}, + {file = "yarl-1.22.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:a4fcfc8eb2c34148c118dfa02e6427ca278bfd0f3df7c5f99e33d2c0e81eae3e"}, + {file = "yarl-1.22.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:029866bde8d7b0878b9c160e72305bbf0a7342bcd20b9999381704ae03308dc8"}, + {file = "yarl-1.22.0-cp39-cp39-win32.whl", hash = "sha256:4dcc74149ccc8bba31ce1944acee24813e93cfdee2acda3c172df844948ddf7b"}, + {file = "yarl-1.22.0-cp39-cp39-win_amd64.whl", hash = "sha256:10619d9fdee46d20edc49d3479e2f8269d0779f1b031e6f7c2aa1c76be04b7ed"}, + {file = "yarl-1.22.0-cp39-cp39-win_arm64.whl", hash = "sha256:dd7afd3f8b0bfb4e0d9fc3c31bfe8a4ec7debe124cfd90619305def3c8ca8cd2"}, + {file = "yarl-1.22.0-py3-none-any.whl", hash = "sha256:1380560bdba02b6b6c90de54133c81c9f2a453dee9912fe58c1dcced1edb7cff"}, + {file = "yarl-1.22.0.tar.gz", hash = "sha256:bebf8557577d4401ba8bd9ff33906f1376c877aa78d1fe216ad01b4d6745af71"}, +] + +[package.dependencies] +idna = ">=2.0" +multidict = ">=4.0" +propcache = ">=0.2.1" + +[metadata] +lock-version = "2.1" +python-versions = ">=3.12" +content-hash = "f8ce18d9fe2b105d9f6adc21fb2e20fa6dadac3464d354530abf56fe2ed24dd9" diff --git a/libs/nemesis_dpapi/poetry.toml b/libs/nemesis_dpapi/poetry.toml new file mode 100644 index 0000000..be97f1e --- /dev/null +++ b/libs/nemesis_dpapi/poetry.toml @@ -0,0 +1,3 @@ +[virtualenvs] +in-project = true +prefer-active-python = true \ No newline at end of file diff --git a/libs/nemesis_dpapi/pyproject.toml b/libs/nemesis_dpapi/pyproject.toml new file mode 100644 index 0000000..595d6c3 --- /dev/null +++ b/libs/nemesis_dpapi/pyproject.toml @@ -0,0 +1,38 @@ +[project] +name = "nemesis_dpapi" +version = "0.1.0" +description = "" +readme = "README.md" +requires-python = ">=3.12" +dependencies = [ + "impacket (>=0.12.0,<0.13.0)", + "asyncpg (>=0.29.0,<=0.30.0)", + "cryptography (>=42.0.0,<43.0.0)", + "pydantic (>=2.0.0,<3.0.0)", + "pycryptodome (>=3.23.0,<4.0.0)", + "dpapick3 (>=0.7.1,<0.8.0)", + "dapr (==1.16.0)", +] + + +[build-system] +requires = ["poetry-core>=2.0.0,<3.0.0"] +build-backend = "poetry.core.masonry.api" + +[tool.poetry.group.dev.dependencies] +ruff = "^0.12.10" +pytest = "^8.4.1" +pytest-asyncio = "^0.25.0" +pytest-cov = "^6.3.0" +pytest-benchmark = "^5.1.0" + +[tool.pytest.ini_options] +markers = [ + "benchmark: performance benchmark tests" +] +# Skip benchmarks by default (use --benchmark-only to run them) +addopts = "--benchmark-skip" +# Test discovery patterns +testpaths = ["tests"] +python_files = ["test_*.py", "bench_*.py"] + diff --git a/libs/nemesis_dpapi/tests/README.md b/libs/nemesis_dpapi/tests/README.md new file mode 100644 index 0000000..0723e43 --- /dev/null +++ b/libs/nemesis_dpapi/tests/README.md @@ -0,0 +1,15 @@ +# Tests + + +# DPAPI Blobs +Test blobs were created with PowerShell: +```powershell +Add-Type -AssemblyName System.Security +$data = [Text.Encoding]::ASCII.GetBytes("test") +$encrypted_no_entropy = [Security.Cryptography.ProtectedData]::Protect($data, $null, 'CurrentUser') + +$encrypted_with_entropy = [Security.Cryptography.ProtectedData]::Protect($data, [byte[]](1,2,3,4,5), 'CurrentUser') + +Write-Host ("no entropy:`n" + [Convert]::ToBase64String($encrypted_with_entropy)) +Write-Host ("`nwith entropy:`n" + [Convert]::ToBase64String($encrypted_with_entropy)) +``` \ No newline at end of file diff --git a/libs/nemesis_dpapi/tests/__init__.py b/libs/nemesis_dpapi/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/libs/nemesis_dpapi/tests/benchmarks/__init__.py b/libs/nemesis_dpapi/tests/benchmarks/__init__.py new file mode 100644 index 0000000..1f101ed --- /dev/null +++ b/libs/nemesis_dpapi/tests/benchmarks/__init__.py @@ -0,0 +1 @@ +# Benchmark tests for DPAPI operations diff --git a/libs/nemesis_dpapi/tests/benchmarks/bench_backupkey_decryption.py b/libs/nemesis_dpapi/tests/benchmarks/bench_backupkey_decryption.py new file mode 100644 index 0000000..d3003c8 --- /dev/null +++ b/libs/nemesis_dpapi/tests/benchmarks/bench_backupkey_decryption.py @@ -0,0 +1,107 @@ +"""Benchmarks for DPAPI masterkey decryption operations.""" + +import base64 +import json +from uuid import UUID + +import pytest +from nemesis_dpapi.core import MasterKeyFile +from nemesis_dpapi.keys import DomainBackupKey + + +class TestMasterkeyDecryptionBenchmarks: + """Benchmark tests for masterkey decryption operations.""" + + def test_single_masterkey_decryption(self, benchmark, get_file_path): + """Benchmark decrypting a single masterkey using domain backup key.""" + # Load domain backup key + backup_key_path = get_file_path("backupkey.json") + with open(backup_key_path) as f: + backup_key_data = json.load(f) + + backup_key = DomainBackupKey( + guid=UUID(backup_key_data["backup_key_guid"]), + key_data=base64.b64decode(backup_key_data["key"]), + domain_controller=backup_key_data["dc"], + ) + + # Load masterkey file + masterkey_file_path = get_file_path("masterkey_domain.bin") + masterkey_file = MasterKeyFile.from_file(masterkey_file_path) + + # Verify setup works + test_result = masterkey_file.decrypt(backup_key) + assert test_result.is_decrypted, "Test decryption failed - benchmark cannot proceed" + + # Run benchmark + result = benchmark(masterkey_file.decrypt, backup_key) + + # Verify result + assert result.is_decrypted + assert result.plaintext_key_sha1 is not None + assert result.plaintext_key_sha1.hex() == "17fd87f91d25a18abd9bcd66b6d9f3c6bfc16778" + + def test_masterkey_decryption_with_warmup(self, benchmark, get_file_path): + """Benchmark masterkey decryption with warmup runs.""" + # Setup + backup_key_path = get_file_path("backupkey.json") + with open(backup_key_path) as f: + backup_key_data = json.load(f) + + backup_key = DomainBackupKey( + guid=UUID(backup_key_data["backup_key_guid"]), + key_data=base64.b64decode(backup_key_data["key"]), + domain_controller=backup_key_data["dc"], + ) + + masterkey_file_path = get_file_path("masterkey_domain.bin") + masterkey_file = MasterKeyFile.from_file(masterkey_file_path) + + # Warmup + for _ in range(5): + masterkey_file.decrypt(backup_key) + + # Benchmark + result = benchmark(masterkey_file.decrypt, backup_key) + + # Verify + assert result.is_decrypted + assert result.plaintext_key_sha1 is not None + assert result.plaintext_key_sha1.hex() == "17fd87f91d25a18abd9bcd66b6d9f3c6bfc16778" + + @pytest.mark.parametrize("iterations", [1, 10, 100]) + def test_batch_masterkey_decryption(self, benchmark, get_file_path, iterations): + """Benchmark multiple consecutive masterkey decryptions.""" + # Setup + backup_key_path = get_file_path("backupkey.json") + with open(backup_key_path) as f: + backup_key_data = json.load(f) + + backup_key = DomainBackupKey( + guid=UUID(backup_key_data["backup_key_guid"]), + key_data=base64.b64decode(backup_key_data["key"]), + domain_controller=backup_key_data["dc"], + ) + + masterkey_file_path = get_file_path("masterkey_domain.bin") + masterkey_file = MasterKeyFile.from_file(masterkey_file_path) + + def batch_decrypt(): + results = [] + for _ in range(iterations): + result = masterkey_file.decrypt(backup_key) + results.append(result) + return results + + # Add context info + benchmark.extra_info["iterations"] = iterations + + # Benchmark + results = benchmark(batch_decrypt) + + # Verify all successful + assert len(results) == iterations + for result in results: + assert result.is_decrypted + assert result.plaintext_key_sha1 is not None + assert result.plaintext_key_sha1.hex() == "17fd87f91d25a18abd9bcd66b6d9f3c6bfc16778" diff --git a/libs/nemesis_dpapi/tests/benchmarks/bench_masterkey_password_decryption.py b/libs/nemesis_dpapi/tests/benchmarks/bench_masterkey_password_decryption.py new file mode 100644 index 0000000..1a5eb34 --- /dev/null +++ b/libs/nemesis_dpapi/tests/benchmarks/bench_masterkey_password_decryption.py @@ -0,0 +1,198 @@ +"""Benchmarks for DPAPI masterkey decryption using password credentials.""" + +import pytest +from nemesis_dpapi.core import MasterKey, MasterKeyFile, MasterKeyType +from nemesis_dpapi.keys import CredKey, CredKeyHashType, MasterKeyEncryptionKey + + +class TestMasterkeyPasswordDecryptionBenchmarks: + """Benchmark tests for masterkey decryption using password credentials.""" + + def test_single_password_masterkey_decryption(self, benchmark, get_file_path): + """Benchmark decrypting a single masterkey using password.""" + # Load masterkey file that can be decrypted with password + masterkey_file_path = get_file_path("masterkey_domain.bin") + masterkey_file = MasterKeyFile.from_file(masterkey_file_path) + + # Create MasterKey object from the file + masterkey = MasterKey( + guid=masterkey_file.masterkey_guid, + masterkey_type=MasterKeyType.UNKNOWN, + encrypted_key_usercred=masterkey_file.master_key, + ) + + # Test password and SID from the test fixtures + password = "Qwerty12345" + user_sid = "S-1-5-21-3821320868-1508310791-3575676346-1103" + + # Create encryption key from password using PBKDF2 (most secure method) + cred_key = CredKey.from_password(password, CredKeyHashType.PBKDF2, user_sid) + mk_encryption_key = MasterKeyEncryptionKey.from_cred_key(cred_key, user_sid) + + # Verify setup works + test_result = masterkey.decrypt(mk_encryption_key) + assert test_result.is_decrypted, "Test decryption failed - benchmark cannot proceed" + + # Run benchmark + result = benchmark(masterkey.decrypt, mk_encryption_key) + + # Verify result + assert result.is_decrypted + assert result.plaintext_key is not None + + def test_password_multiple_hash_types(self, benchmark, get_file_path): + """Benchmark trying multiple hash types for password-based decryption.""" + # Load masterkey file + masterkey_file_path = get_file_path("masterkey_domain.bin") + masterkey_file = MasterKeyFile.from_file(masterkey_file_path) + + # Create MasterKey object from the file + masterkey = MasterKey( + guid=masterkey_file.masterkey_guid, + masterkey_type=MasterKeyType.UNKNOWN, + encrypted_key_usercred=masterkey_file.master_key, + ) + + password = "Qwerty12345" + user_sid = "S-1-5-21-3821320868-1508310791-3575676346-1103" + + def try_multiple_hash_types(): + """Try decrypting with different hash types until successful.""" + hash_types = [CredKeyHashType.PBKDF2, CredKeyHashType.SHA1, CredKeyHashType.NTLM] + + for hash_type in hash_types: + try: + if hash_type == CredKeyHashType.PBKDF2: + cred_key = CredKey.from_password(password, hash_type, user_sid) + else: + cred_key = CredKey.from_password(password, hash_type) + + mk_encryption_key = MasterKeyEncryptionKey.from_cred_key(cred_key, user_sid) + result = masterkey.decrypt(mk_encryption_key) + + if result.is_decrypted: + return result + except Exception: + continue + + return None + + # Verify setup works + test_result = try_multiple_hash_types() + assert test_result is not None and test_result.is_decrypted, "Test decryption failed - benchmark cannot proceed" + + # Run benchmark + result = benchmark(try_multiple_hash_types) + + # Verify result + assert result is not None + assert result.is_decrypted + assert result.plaintext_key is not None + + @pytest.mark.parametrize("hash_type", [CredKeyHashType.PBKDF2, CredKeyHashType.SHA1, CredKeyHashType.NTLM]) + def test_password_by_hash_type(self, benchmark, get_file_path, hash_type): + """Benchmark password decryption for specific hash types.""" + # Load masterkey file + masterkey_file_path = get_file_path("masterkey_domain.bin") + masterkey_file = MasterKeyFile.from_file(masterkey_file_path) + + # Create MasterKey object from the file + masterkey = MasterKey( + guid=masterkey_file.masterkey_guid, + masterkey_type=MasterKeyType.UNKNOWN, + encrypted_key_usercred=masterkey_file.master_key, + ) + + password = "Qwerty12345" + user_sid = "S-1-5-21-3821320868-1508310791-3575676346-1103" + + # Create encryption key with specific hash type + if hash_type == CredKeyHashType.PBKDF2: + cred_key = CredKey.from_password(password, hash_type, user_sid) + else: + cred_key = CredKey.from_password(password, hash_type) + + mk_encryption_key = MasterKeyEncryptionKey.from_cred_key(cred_key, user_sid) + + # Add context info + benchmark.extra_info["hash_type"] = hash_type.value + + # Try to decrypt - some hash types might not work for this specific masterkey + try: + test_result = masterkey.decrypt(mk_encryption_key) + if not test_result.is_decrypted: + pytest.skip(f"Hash type {hash_type.value} does not decrypt this masterkey") + except Exception: + pytest.skip(f"Hash type {hash_type.value} does not decrypt this masterkey") + + # Run benchmark + result = benchmark(masterkey.decrypt, mk_encryption_key) + + # Verify result + assert result.is_decrypted + assert result.plaintext_key is not None + + @pytest.mark.parametrize("iterations", [1, 10, 20]) + def test_batch_password_decryption(self, benchmark, get_file_path, iterations): + """Benchmark multiple consecutive password-based masterkey decryptions.""" + # Load masterkey file + masterkey_file_path = get_file_path("masterkey_domain.bin") + masterkey_file = MasterKeyFile.from_file(masterkey_file_path) + + # Create MasterKey object from the file + masterkey = MasterKey( + guid=masterkey_file.masterkey_guid, + masterkey_type=MasterKeyType.UNKNOWN, + encrypted_key_usercred=masterkey_file.master_key, + ) + + password = "Qwerty12345" + user_sid = "S-1-5-21-3821320868-1508310791-3575676346-1103" + + # Create encryption key + cred_key = CredKey.from_password(password, CredKeyHashType.PBKDF2, user_sid) + mk_encryption_key = MasterKeyEncryptionKey.from_cred_key(cred_key, user_sid) + + def batch_decrypt(): + results = [] + for _ in range(iterations): + result = masterkey.decrypt(mk_encryption_key) + results.append(result) + return results + + # Verify setup works + test_result = masterkey.decrypt(mk_encryption_key) + assert test_result.is_decrypted, "Test decryption failed - benchmark cannot proceed" + + # Add context info + benchmark.extra_info["iterations"] = iterations + + # Benchmark + results = benchmark(batch_decrypt) + + # Verify all successful + assert len(results) == iterations + for result in results: + assert result.is_decrypted + assert result.plaintext_key is not None + + def test_password_key_derivation_benchmark(self, benchmark, get_file_path): + """Benchmark just the password-to-key derivation process.""" + password = "Qwerty12345" + user_sid = "S-1-5-21-3821320868-1508310791-3575676346-1103" + + def derive_encryption_key(): + """Create encryption key from password.""" + cred_key = CredKey.from_password(password, CredKeyHashType.PBKDF2, user_sid) + return MasterKeyEncryptionKey.from_cred_key(cred_key, user_sid) + + # Verify setup works + test_key = derive_encryption_key() + assert test_key is not None + + # Run benchmark + result = benchmark(derive_encryption_key) + + # Verify result + assert result is not None + assert result.key is not None diff --git a/libs/nemesis_dpapi/tests/conftest.py b/libs/nemesis_dpapi/tests/conftest.py new file mode 100644 index 0000000..9fa7b6e --- /dev/null +++ b/libs/nemesis_dpapi/tests/conftest.py @@ -0,0 +1,41 @@ +from collections.abc import Callable +from pathlib import Path + +import pytest + + +def _get_file_path(name: str) -> Path: + return Path(__file__).parent / "fixtures" / name + + +def _read_file_text(name: str) -> str: + return _get_file_path(name).read_text() + + +def _read_file_bytes(name: str) -> bytes: + return _get_file_path(name).read_bytes() + + +@pytest.fixture +def read_file_text() -> Callable[[str], str]: + return _read_file_text + + +@pytest.fixture +def read_file_bytes() -> Callable[[str], bytes]: + return _read_file_bytes + + +@pytest.fixture +def get_file_path() -> Callable[[str], Path]: + return _get_file_path + + +@pytest.fixture +def blob_without_entropy() -> bytes: + return _read_file_bytes("blob_without_entropy.bin") + + +@pytest.fixture +def blob_with_entropy() -> bytes: + return _read_file_bytes("blob_with_entropy.bin") diff --git a/libs/nemesis_dpapi/tests/fixtures/backupkey.json b/libs/nemesis_dpapi/tests/fixtures/backupkey.json new file mode 100644 index 0000000..0ff87c5 --- /dev/null +++ b/libs/nemesis_dpapi/tests/fixtures/backupkey.json @@ -0,0 +1,6 @@ +{ + "dc": "DC01.CORP.LOCAL", + "backup_key_guid": "7efa51b1-2523-45bf-acba-2e15ecf4f1e7", + "name": "G$BCKUPKEY_7efa51b1-2523-45bf-acba-2e15ecf4f1e7", + "key": "HvG1sAAAAAABAAAAAAAAAAAAAACUBAAABwIAAACkAABSU0EyAAgAAAEAAQCZge5gkMCqRYmw2zrODFhcQ8aLMCaAZ8fTjm9K/wtmlRmn1EiG+yVzlvmoIn3QdVfIWw3MniZ35G8q/u9ALstcCRonEhmVRQAXltJyOjV4hNVZ/DN0VmNGhUtb6EYBbtfFFeCut0xLAxh76YMd3YF+xwn4Id4Q2SIz2o10C0X61tXWXunfs/dhSRJ0AnGOFwSCayljut+NHNv35zCbA5TIAqREkV+yeB+gHE7z/DZZE0ZWnipwQBHsUZGYfLxUKwATEreEZegGymINhghkphPIs5O4Nvbt9rdlCCIvH2ci8MH6/S3mAOLHAcFb2Hu+4hk6L/ur3Q6PKuVplzkXIcTBx6EIaQv6rYbqEr5kKu+rbAOrvlKEZMSKZHw4Dl8u1M+pv55vgh45EwNyQw0oymHcJaauf9cnBAn4v5tgJgOhrD1j0MA6q/EWgmPA5bLtTuNyPasCe/LsX2aZNtMzkE+jebIqdRJ412REYbdnzrYLOnrZ2t3sutRy7pg6DQ5sW+qfwY04quJmPUHdGogTj1iLCNDyr5oXJlrOpDEUpxhOdyOIddICoPJYZ/Y3F4mJtjQVPjy6hCpvgzmG19Cq6lXNl4oqDoyMJ5SDXpCB32jE/tr3ZHBixJKbmAxVU47Yyenl7oy43UJH4M+PotsL40aozbnq/oz0ekDChL+UNxKp09XGaBpft/rZxWF+LWxN3RnNfsBLi0W2qTKqFKJIbyoDnyiwnf5vNl3w/Co9113yNS+hucxxqkFWlUSsmIWEbWqoasrUd+DzK3+HLO0SEJgKRA5sTEamxays5wl7hjs2msRYZ5IrszsXJBM5N77DTqp2oYIBCkSAS0GxgVERsi9VodFyRl9I0LpQBUcUY82F+xRM143BV/QRYDCmPCSBtxCRWX6m7RdSVPSnnTxrKbFh0VjLwAgjDR7ai2ahhctru8XxvfdC0+VcNpEw1K7H3jVubHn3WZxfJEJodrWnvNisjQRRkSjQeDjtClB4d2v04XbiOMiLF3HixuURMiiGyYKfUI12Z6sKhE44/cgCFRR0gyF8bnokjLSL63hhfsz8YB+NFB+qYGvVJxfkVQv0yig19IqjQxN0vSP5ZwRTsiPaWk9mEIeX4LEO9f3RODUrG8JzBN/ZaU/N62n7IluWN7vCG0tCDArlgKmv4r3qihgXsf/4PYRGNHEkDpRHercyB4FVnMs0oTWq0RIjjX1bjoY8pcJ3nAJQxYqFxDePOeZBQ0O4MRNS/AR32q+akK/PTCYS41/FMcVTeKpfgbW3kfL0npAFVo/g25kAKk9n1W2o0VGo2d/PffL1uLZwMKUWoUnOkZQmeTXZUmmaJNGkeEvuVvbo9p8tN8QNitKFYO4a+AmMwV5fQj/2fwcQLpVZIPU1kA6ydSheW5uI/sDFPGTWfAG96AdkieXGQH1kOPmO6TwXFbcy8KYYYrPmxUZloEC2VboeGsqWez6XBBLcONGoTHYYPtJs8QLq4diphiIpdAIV925kcIWqmEDn2Dfai2x+olkrGfsjMfRbjJx9ZRo=" +} \ No newline at end of file diff --git a/libs/nemesis_dpapi/tests/fixtures/blob_app_bound_enc_key.txt b/libs/nemesis_dpapi/tests/fixtures/blob_app_bound_enc_key.txt new file mode 100644 index 0000000..434163c --- /dev/null +++ b/libs/nemesis_dpapi/tests/fixtures/blob_app_bound_enc_key.txt @@ -0,0 +1 @@ +QVBQQgEAAADQjJ3fARXREYx6AMBPwpfrAQAAAOHiUvcmF0tFpjIHGNlMpncQAAAAHAAAAEcAbwBvAGcAbABlACAAQwBoAHIAbwBtAGUAAAAQZgAAAAEAACAAAAD1Kvlymv/bt+Peb1fIARobHztzSvpFCKAMNcK/T6UqngAAAAAOgAAAAAIAACAAAADIfZgjr3pI9m9XsiWBLmhPSVjg/XWYynTfv4b4dHHcwHABAAB9KGS5+4Onf3iy5HTxVsbJKGtX1WIV9qFQ5UPQ5iXGeBqZ8lQtcAYd43ueolO3Q1YGdR+XaKRoNL63SeV9keS/Kkg6kGyg+pjs6UhGUJcQmIsb0qNzmfRm9twdI1EMu/i9YZg6TZoM34ZAVcCwTc/ZvoCXtD72t7vTv48F/zSbkGztC5ROaG+H3MlIvo1cOQJe7iMdGpIDT9xRq7jz3LoYiQAqf+hWVG/WsSA2D4l5v3Sm6UcYpCqZF+evYskHC88vRWkwAUeKcLj3iSnqP9bECMsm6IVMmsw24qOCM2qeYZKQEI0coZHZOI3tgttPkic/PelJGpKP2+fPeJv60/Sa+e6W1tvquX+7b9FzLdB/VYtUdPY3y1cPm6WTCWCBub4pwkzRe19+AkKJjiYl12VToRibmtCueBxBnFGq3LSb41ZVVnoX03z8dGekD3wR3D2Yol+xlzDspgKVL3JpVtq0lKfJGpqpk8LZEv1uWUfCK0AAAADd8cUt1enNlYgrLCoOmZgadjtNV6kzXxFVSP/lc4P2d6rM6mBKSqRPk5B8wwemP+bBNfeogKjPJ+r2ditgjrbvAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA \ No newline at end of file diff --git a/libs/nemesis_dpapi/tests/fixtures/blob_with_entropy.bin b/libs/nemesis_dpapi/tests/fixtures/blob_with_entropy.bin new file mode 100644 index 0000000..32fb665 Binary files /dev/null and b/libs/nemesis_dpapi/tests/fixtures/blob_with_entropy.bin differ diff --git a/libs/nemesis_dpapi/tests/fixtures/blob_without_entropy.bin b/libs/nemesis_dpapi/tests/fixtures/blob_without_entropy.bin new file mode 100644 index 0000000..5baf7a1 Binary files /dev/null and b/libs/nemesis_dpapi/tests/fixtures/blob_without_entropy.bin differ diff --git a/libs/nemesis_dpapi/tests/fixtures/chrome/v133after/TODO b/libs/nemesis_dpapi/tests/fixtures/chrome/v133after/TODO new file mode 100644 index 0000000..e69de29 diff --git a/libs/nemesis_dpapi/tests/fixtures/chrome/v133before/C/DPAPIUser/AppData/Local/Google/Chrome/User Data/Default/Cookies b/libs/nemesis_dpapi/tests/fixtures/chrome/v133before/C/DPAPIUser/AppData/Local/Google/Chrome/User Data/Default/Cookies new file mode 100644 index 0000000..75860d8 Binary files /dev/null and b/libs/nemesis_dpapi/tests/fixtures/chrome/v133before/C/DPAPIUser/AppData/Local/Google/Chrome/User Data/Default/Cookies differ diff --git a/libs/nemesis_dpapi/tests/fixtures/chrome/v133before/C/DPAPIUser/AppData/Local/Google/Chrome/User Data/Default/History b/libs/nemesis_dpapi/tests/fixtures/chrome/v133before/C/DPAPIUser/AppData/Local/Google/Chrome/User Data/Default/History new file mode 100644 index 0000000..364a549 Binary files /dev/null and b/libs/nemesis_dpapi/tests/fixtures/chrome/v133before/C/DPAPIUser/AppData/Local/Google/Chrome/User Data/Default/History differ diff --git a/libs/nemesis_dpapi/tests/fixtures/chrome/v133before/C/DPAPIUser/AppData/Local/Google/Chrome/User Data/Default/Login Data b/libs/nemesis_dpapi/tests/fixtures/chrome/v133before/C/DPAPIUser/AppData/Local/Google/Chrome/User Data/Default/Login Data new file mode 100644 index 0000000..6b95f7c Binary files /dev/null and b/libs/nemesis_dpapi/tests/fixtures/chrome/v133before/C/DPAPIUser/AppData/Local/Google/Chrome/User Data/Default/Login Data differ diff --git a/libs/nemesis_dpapi/tests/fixtures/chrome/v133before/C/DPAPIUser/AppData/Local/Google/Chrome/User Data/Local State b/libs/nemesis_dpapi/tests/fixtures/chrome/v133before/C/DPAPIUser/AppData/Local/Google/Chrome/User Data/Local State new file mode 100644 index 0000000..3e906e0 --- /dev/null +++ b/libs/nemesis_dpapi/tests/fixtures/chrome/v133before/C/DPAPIUser/AppData/Local/Google/Chrome/User Data/Local State @@ -0,0 +1,300 @@ +{ + "browser": { + "last_whats_new_version": 111, + "shortcut_migration_version": "111.0.5563.147" + }, + "hardware_acceleration_mode_previous": true, + "intl": { + "app_locale": "en" + }, + "invalidation": { + "per_sender_topics_to_handler": {} + }, + "legacy": { + "profile": { + "name": { + "migrated": true + } + } + }, + "management": { + "platform": { + "azure_active_directory": 0, + "enterprise_mdm_win": 0 + } + }, + "network_time": { + "network_time_mapping": { + "local": 1.682360456417751e+12, + "network": 1.682360456e+12, + "ticks": 40407932879.0, + "uncertainty": 1779668.0 + } + }, + "os_crypt": { + "app_bound_fixed_data": "AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAA+xZ9ELLickar8CmfElOt+wAAAAACAAAAAAAQZgAAAAEAACAAAAAmycokS5nRxdM3kgPOfqo5kXYalZKcaYS68qDgK85NLQAAAAAOgAAAAAIAACAAAAChUvSCW4xWM0WYKnKUfxZkNWjk7jIQZTNwwWqz+Vc/4xABAACSrpIa4qtdF/JBkuPSgZaGRANJEflhNB/aqXRdiPXq8LHL3eZ7NIhn2vElc44/0hIbne/bfSDKELqq3vxFNTNrMD1XWpadfFo7fSXXS8CgjjZzTqFt1JITxJ1nJY3QmHiEaWCjb5vqtduNjEaaqi8IxNwoxaC9cwlIW/hbbd2W7YLutT9JoAm/v98n+oyGTg9aSTkHeKyRlBEgKwAYBKHMXuPZnKMcXXxiksqzTMbJy0ztLHR+ksrbEOovRsePLAlEg/75agJ1bhyRp6rRrwphWYOAYHzYa6LCqB/5FK/9SbCV01YWcuo8+ajCgCGG94GqZ41e0tbB18N5jL9xSCLJV2dugGwZUq8P2eccVVsm20AAAACJHezojBFN6PdZuNIx24RV3dnBk9L0IQ3FhTmLf2GLUANrMLmd0Ru9YKAGQvtXXwM+L4Di87zqRjKHV8uxqLzc", + "encrypted_key": "RFBBUEkBAAAA0Iyd3wEV0RGMegDAT8KX6wEAAABggpmrnelxSI9L2SKyhIzmAAAAAAIAAAAAAANmAADAAAAAEAAAALGXebmjhAjFMxSXBiRdPCgAAAAABIAAAKAAAAAQAAAApQtkBNuoqu5tSBs9XyfJXygAAAAHIznP849HtrQwkKY1o4kXJSdooID3NGrmFlE5SyGpT5b4wg85bjKTFAAAAJsdnSsl9N9rQuZuv8zoDxWiHcTM" + }, + "policy": { + "last_statistics_update": "13326834055645500" + }, + "profile": { + "info_cache": { + "Default": { + "active_time": 1682360456.041276, + "avatar_icon": "chrome://theme/IDR_PROFILE_AVATAR_26", + "background_apps": false, + "force_signin_profile_locked": false, + "gaia_given_name": "", + "gaia_id": "", + "gaia_name": "", + "hosted_domain": "", + "is_consented_primary_account": false, + "is_ephemeral": false, + "is_using_default_avatar": true, + "is_using_default_name": true, + "managed_user_id": "", + "metrics_bucket_index": 1, + "name": "Person 1", + "shortcut_name": "Person 1", + "signin.with_credential_provider": false, + "user_name": "" + } + }, + "last_active_profiles": [], + "metrics": { + "next_bucket_index": 2 + }, + "profile_counts_reported": "13326834055622031" + }, + "profile_network_context_service": { + "http_cache_finch_experiment_groups": "None None None None" + }, + "session_id_generator_last_value": "1809058498", + "shutdown": { + "num_processes": 3, + "num_processes_slow": 1, + "type": 1 + }, + "subresource_filter": { + "ruleset_version": { + "checksum": 613759234, + "content": "9.44.0", + "format": 35 + } + }, + "tab_stats": { + "discards_external": 0, + "discards_proactive": 0, + "discards_urgent": 0, + "last_daily_sample": "13326834056055511", + "max_tabs_per_window": 1, + "reloads_external": 0, + "reloads_proactive": 0, + "reloads_urgent": 0, + "total_tab_count_max": 1, + "window_count_max": 1 + }, + "ukm": { + "persisted_logs": [] + }, + "uninstall_metrics": { + "installation_date2": "1681858552" + }, + "updateclientdata": { + "apps": { + "dhlpobdgcjafebgbbhjdnapejmpkgiie": { + "cohort": "1:z9x:", + "cohortname": "Auto", + "dlrc": 5957, + "installdate": 5957, + "pf": "d0a84187-f0ee-42b8-bb23-0e880ba2b831" + }, + "eeigpngbgcognadeebkilcpcaedhellh": { + "cohort": "1:w59:", + "cohortname": "Auto", + "dlrc": 5957, + "installdate": 5957, + "pf": "44a78d38-9e88-4f6d-a07f-ce1f1e94a9dd" + }, + "efniojlnjndmcbiieegkicadnoecjjef": { + "cohort": "1:18ql:", + "cohortname": "Auto Stage3", + "dlrc": 5957, + "installdate": 5957, + "pf": "76aad475-2c7d-4d4f-b1ca-fee689df3fd6" + }, + "ehgidpndbllacpjalkiimkbadgjfnnmc": { + "cohort": "1:ofl:", + "cohortname": "stable64", + "dlrc": 5957, + "installdate": 5957, + "pf": "a57a961c-847f-4b59-b71c-4dc41375a139" + }, + "gcmjkmgdlgnkkcocmoeiminaijmmjnii": { + "cohort": "1:bm1:", + "cohortname": "M54AndUp", + "dlrc": 5957, + "fp": "1.e66ec4166eb005622f590068730a0b2f19e608035a4428adb50fb236f84ac358", + "installdate": 5957, + "pf": "4b998626-7e1d-4588-8aef-9fdbff6f00ef", + "pv": "9.44.0" + }, + "ggkkehgbnfjpeggfpleeakpidbkibbmn": { + "cohort": "1:ut9/1a0f:", + "cohortname": "108-and-above-all-users", + "dlrc": 5957, + "installdate": 5957, + "pf": "48c7060c-c26e-4935-8486-7b89489d9ebf" + }, + "giekcmmlnklenlaomppkphknjmnnpneh": { + "cohort": "1:j5l:", + "cohortname": "Auto", + "dlrc": 5957, + "installdate": 5957, + "pf": "260d9c86-7eb8-4552-aa92-18e9b96a88ef" + }, + "gonpemdgkjcecdgbnaabipppbmgfggbe": { + "cohort": "1:z1x:", + "cohortname": "General release", + "dlrc": 5957, + "installdate": 5957, + "pf": "ddf81b4b-cdce-4df8-a08a-56678b1c275c" + }, + "hfnkpimlhhgieaddgfemjhofmfblmnib": { + "cohort": "1:jcl:", + "cohortname": "Auto", + "dlrc": 5957, + "installdate": 5957, + "pf": "f0f47170-9c65-486b-ba02-e5fb3551d551" + }, + "hnimpnehoodheedghdeeijklkeaacbdc": { + "cohort": "1::", + "cohortname": "", + "dlrc": 5957, + "fp": "1.6f6bc93dcd62dc251850d2ff458fda96083ceb7fbe8eeb11248b8485ef2aea23", + "installdate": 5957, + "pf": "1d737fa3-8a7b-45d1-bfed-1bfb7caba9b1", + "pv": "0.57.44.2492" + }, + "ihnlcenocehgdaegdmhbidjhnhdchfmm": { + "cohort": "1:15j3:", + "cohortname": "Win (Including up-to-date)", + "dlrc": 5957, + "fp": "1.aeedb246d19256a956fedaa89fb62423ae5bd8855a2a1f3189161cf045645a19", + "installdate": 5957, + "pf": "5254d876-be90-40ec-9a51-5c6e0deacc99", + "pv": "1.3.36.141" + }, + "imefjhfbkmcmebodilednhmaccmincoa": { + "cohort": "1:1iaf:", + "cohortname": "windows_flatbuffers", + "dlrc": 5957, + "installdate": 5957, + "pf": "6e541b3d-cef3-4d1b-b1cf-3764f2a241ed" + }, + "jamhcnnkihinmdlkakkaopbjbbcngflc": { + "cohort": "1:wvr:", + "cohortname": "Auto", + "dlrc": 5957, + "installdate": 5957, + "pf": "1083d394-de36-4533-94a0-19a3572d6335" + }, + "jflookgnkcckhobaglndicnbbgbonegd": { + "cohort": "1:s7x:", + "cohortname": "Auto", + "dlrc": 5957, + "installdate": 5957, + "pf": "60d9d973-a32e-40ff-b35b-461906b8de55" + }, + "khaoiebndkojlmppeemjhbpbandiljpe": { + "cohort": "1:cux:", + "cohortname": "Auto", + "dlrc": 5957, + "installdate": 5957, + "pf": "d8d510ed-205c-4b7b-ad41-4794b4bbb42a" + }, + "laoigpblnllgcgjnjnllmfolckpjlhki": { + "cohort": "1:10zr:", + "cohortname": "Auto", + "dlrc": 5957, + "installdate": 5957, + "pf": "7a1ebf82-2f6e-444b-8a8c-eac432b52e06" + }, + "llkgjffcdpffmhiakmfcdcblohccpfmo": { + "cohort": "1::", + "cohortname": "", + "dlrc": 5957, + "installdate": 5957, + "pf": "4e4e7639-8d68-4fde-8132-ae4bca46efe1" + }, + "lmelglejhemejginpboagddgdfbepgmp": { + "cohort": "1:lwl:", + "cohortname": "Auto", + "dlrc": 5957, + "installdate": 5957, + "pf": "f80fd110-97a0-4d20-85a3-1478b2182d3f" + }, + "obedbbhbpmojnkanicioggnmelmoomoc": { + "cohort": "1:s6f:", + "cohortname": "Auto", + "dlrc": 5957, + "fp": "1.365a32b00923f8494e3c710f505983b29bd19721be363093e02d818c994259e3", + "installdate": 5957, + "pf": "67b1fb4d-a5e6-4fbe-8f0b-86b660ece09c", + "pv": "20230410.524655861.1" + }, + "oimompecagnajdejgnnjijobebaeigek": { + "cohort": "1:1bk9:", + "cohortname": "4.10.2557.0 for Chrome 95+", + "dlrc": 5957, + "installdate": 5957, + "pf": "d3375e32-e815-40d0-a250-759a551cdb48" + }, + "ojhpjlocmbogdgmfpkhlaaeamibhnphh": { + "cohort": "1:w0x:", + "cohortname": "All users", + "dlrc": 5957, + "installdate": 5957, + "pf": "cbac6067-1397-40fb-a3ea-5f400c52e061" + } + } + }, + "user_experience_metrics": { + "default_opt_in": 2, + "low_entropy_source3": 591, + "machine_id": 5025582, + "pseudo_low_entropy_source": 4717, + "session_id": 1, + "stability": { + "browser_last_live_timestamp": "13326834085949992", + "exited_cleanly": true, + "stats_buildtime": "1679687029", + "stats_version": "111.0.5563.147-64", + "system_crash_count": 0 + } + }, + "variations_compressed_seed": "safe_seed_content", + "variations_country": "us", + "variations_crash_streak": 0, + "variations_failed_to_fetch_seed_streak": 0, + "variations_last_fetch_time": "13326834056751624", + "variations_permanent_consistency_country": [ + "111.0.5563.147", + "us" + ], + "variations_safe_compressed_seed": "H4sIAAAAAAAAAOy9e4xjWXofpunZGQnc2GqVVruzrX3Wvne7qs77IWk8Ouc+drpmuqenHzOza0GlS/Kyit0skkuyqrsmxkaSpQCRAiNB/EccOQ+9AltBDBtBnASwAydRHAGW7MRxjNgJnAQRLEeI84cBA1Zezu+7JKtIFqvImumdBMGurekiee853/3O9/j9zvnOubX7928nB984uZ3eevd2+ubJ7RP29I00PL2dfuPoThr47TQ8yfb3b4UY0q9/3e2H2Hsj2d9/eufR/sntd99Ud+7dSpJw6+XP8a99rWnq9d7xg9svb/zOC7UvhEajHA7b9XanPTp52B2Wg3bRab9bvtEftQ/b7xajdq87dM/t/uFa7e6g7OPnXpNff37us1j4LBc+q4XPeuGzWfhsFz67hc8en7+/9uEzedjiF3zxC7H4hVz8Qi1+oRe/MItf2MUv3OIXi5KKRUnFoqRiUVKxKKkgST9c+960bBVHndH1T+y62of3QmPUPq5G6/r31L+0sd647v7R2nbeGzTK5hvdvbXuoMZvrNn4T9R2Jo23Wuu3vrle6/d+YOMFzsX2Vz/z/Je/5+A3/6ff/dUXN372Q7XP3y32y1vdVi/Ue0ejBwft4f32qLzdG1RfpuXw8ajXhznfqf2hrFvUO2Xz9eKo2zi43qz/aO2Tl95cu7H05zu9btbdfa32xVNFXtoKHvHTNy7vaPf12pfOFLeytc0VrWW1z10uWvUEaOkTNy57wrz2+RVCnbazeUk7934C48bc9ldv/DOcq222rY3124xGUbxQdrce3h//85q41h+JF/qjrXiv+ufuA3GtNRAvtAZbSRDX2iNxrdsR15qluFYOxYvlcEtx/1PPbfy1a7VPLO++fHKr0eti8D9Re+nUm/YmZrAnJFNMXV/4Nel1R4Ne5+zXGcer7+4sOt6nNi7te/dW7QsrxmJ8ITV14/Kmdk8t7sLxOGtr89K27r20cToYXo0H4+A3/5u/AZf6D1+sfQa3HGPwBodFJ+kd9nvD9qjd3Y+D3hO45ttt0mhZ+9Ttot19cDAoi+bMRXuCQXOcmevP1UPtk0uvuQtFD+DpGys72j2sffHSa+a7W9nexuUC7b5bk5c2EbrNswau2PcKZey+Xbt+anszDa9W0qqHmjHgn9p9s/aVs/i/omHY0eaN1WN0r/bVmai/Rpubq9ucja6XPt04ul6ugLnourK1zctbG7sOJ9fRhk9dp8pGv/t87auh2STHe71XNB/0HpfdB72k0y67o/tFq6yeD03dK/u9wQhOFGvXT4MRDTkGHUN+8yqtUBtzZjNpY+MqbcyFuJcXQ9zV2ipr8szA1r6NurlxlW5aNTVjdFfqZ/MK/SyAjv/9t37txY0/da320dB5UpwMHw7LV9v7B/fK4YP2YTkg1PxDtR84l2muL3w9Ga/F3PKVRcW/tHFBPxijT58peekldPuNi27/I7XPzCjvwvs3L7h/NntYOXWB//xv/vKLG53aZwPAVW+QdcpDaPRWd1QOigY9EJ6/A4WXA2jpy7XvG6MwKKdZv1F76aKb7n2cwIMZgwdZ9Sj0uEdk/l+4Vvve0O+/2jssn73qJ9Dx7tthOOmDbGZO9csvGav+gtvnVH/x/ZsX3D9WvagUwcRU9X/6T8Is/85ztR8KR6Neq93phMagNxzeag2Kw5Ks8qvnwe/HLrh690drnzqzrWVXQL6P3bjg5h871Q5Z1kV3by6/+97HNj7sDZ5NGWWnz/Zzv4/I+nPPI79Ob2k2B2ALdwf0qbxfHJf487BP8ZTNmFW9vrn6nl1b+/5Xe51mvWg8PrWYtW5EV9Mb6Y6N1XfMZd4VF08y76om5zPvGm1urmyTDMzbagy82LaTQfhPf+aXXoS//czztU+fNlCFhrvFYFje7RSN8gDaGIfA7dr3nunysyvvoOtP3bL+2Y2V1895rlj03DUaeKP25fMjsfxaavDGygbvng7t7Dhc3OLmqhZnQYY0U1f4q//ir7y48a9eq31qejfBk3HITHpH0OBJVgw6JxgCed7fP7Pqtt3bpzDpTC3LL8UzfObGqubunGp5RikXt7e5or17n4FKQCOhEikR+W5M46C2EwKz8befr908a6R7XA72ywe97OlogNZeL7v7o4P7+LscTYwfivoMqONscqju2XvQ23u9190vB8gTn619cvkV95GwRtUlH61tnF2StodLM8/UZIvdVxZNdnvjSmLvtmt6yUCtvpG6unG1rh7VzLJBXK+vzSv1Vdk88zSkip9y0p//NaS2/+4aYMWkqbQclY3RvfIQDKIJ2Q4nseN9gi+1OCif21jd5e790wB8NhIXXk2N3lij0Qe1r53X+aWtbq5udRxRxghKn0aUv/AvIbn+5Wu1T0zvHystLY9HPdx0azg8Kt+nYpfNmFzW29yMyWUXjmdMLm1qbsZkVVubl7Y1qz/jpvr77X8A6/yrL9S+NH9rUgyaYTAK3Sb9hcTaPGqM7hTvF6X2FlX5kxs3lnZ86xBQceOT53+bEeXMvMc/v9UejI5Ax3HV7XJUNItRMTeReHFH44nEi3+fn0i8vJ3Ny9qZnRS49NnGkwKXXjI/KbCytc0VrS0LBBfqdT4QXKz+pYHg0lY3V7c6N38ht93YkP/b/wuG/G/PhNnJYx43IMFbje43yk6n9+RuMTqACZvz6OJza9x5iZKWXL1MScsavURJF7S6qKQll927sfGHZnKRMjdeIOf/6oR3/o1rix5/uxw0Doru6I3+6I2jUTYY9AZpu+j09qGuHzuvrq+sff/uXo1doLQL78FDfuXG2h38VI1fpMBLe9hct4d7nyVl2gq5MbXN3Y1TAxSTCbSNvz+DaifNIea174/K/q1DmoihLPTTz9U+Op0ve7s9OjgDiteb4nO1Hy6r3/YO6c4h7txrnF6w8aHR4KgUn6x9bObXottswyXKvdGos3FNH85i5OVCLMXIyy+dx8gXNLcUI1/c3uaK9sYY+QxCnU2a6Kmif+6FM4w8buRO+WQSzYgJ3iu6j9vd/dDZp9nOg8P3l7H2FzPWWxtfm+990h9BlaNOMc9KhxtfuuziZFA22yOSfYiO1AXjcmkHkGjrxlUk2j04xd7nRmxlT5tX6ulix79QDcsc/2KdXeL4l/aw6PgXXjw3WXU6T/jX/hxSzZ+5Vtucb2Xy3GnZPOp32o3KYGB89nzw/Pw6t+4+PM0Li+pbdjme6/M31mn2rdrNi5R2Ububa7QLVX3Iu9lpTunH6WbjV5+vbc03cP+oT+4OOe4e9LrlnaPDejl4MDjqPn5w0n+/eP3HF112Z+Nq3e8+PuWMi5pfcSd1duOKnXVq9qLxWKO3zav1NksClJ4a9F/+dZCo33i+9rlpWzlN7HSb46CcD3qHtDKft8tOk8bmh2sfOxuEuUsXpxLoxze6nRN8/1LtI2ffj6+e/HKlyYefAnZbGN4vbKwj9+7bp3Z/NqiXXE8N31ir4XdqW+cHcEXLm+u0vAB0J4P1e//6L7+48eeuzQ/WFLrQFGgv6TXPBgvqmiIOzhiCzxfWuvNCdV1w/Xl1XdTwheq6pOXNdVoeQ95qUYditrYEefkkBv2la7XPT9u4td+tCk2Oi067OZmhe6M7HgFozJ8P2F9c7+bdb5w+25nSLrsBz/bFG+s1/c3TsqcZta1qe3OttsegawJvJxOT8zO3G78xQ6xw04AmZ14v6mUHMt3qtspB2W2UK4nVhXcuJVYXXj1PrC5udCmxurTVzdWtzs3iyplZXMPYRFn/7vfVvjJt5/Wy3X+3vZ+XxehoUBLeR9gal4mNNQSl/fxzs+sNT2fWbhoNGqkpoHrYRcB7q10+qX3y/km3MeF+SIJFY1SVioEcU5Tf0Jf+jKdJjoaj3uHdYjjsHwyKYflwSIsAJMfZOsbTjZVybHxn5JjL5v/Cc4vx/v89wZYugF0gwsIC2AVXXbAAdkmbm6vbnJ1luvRBx7NMl14yP8u0srXNFa39s7U/sp5oF40BOrE33uPw/bHaK2s+ymW9b7633mepBD9FXr/0d4G8/scZKvF6b//ha7ezYzD+Ic0Q3C9oAuAeOL5bwFDTrP6WXERX02qa6pc5Z9KLvvT5jTV6XspELr58nolc0uxSJnJ5u5trtDuueJHTipdqYvBvXat9eXrn7XZXVoihQsSQ4PVeo+i8Wh4N2sNRu0Gg6eXzaeyr6zewW5zy0jN9rboJT/fVG+t3Ua+J87pbp4/NtfuYmYuRxs5mOj4Boxt/4VrtdLqbcCshsnYXQ3K31z/qvz8at7Voq5/YuKSvpTP95y+bn+lf0szSmf7l7Wxe0s7Y21k1j2X81Nv/xC/C239vBroTA0qKxkF5azj9ih6AQO77U95lJOmSTpei/kuun0f9lzW8FPWvaHlznZYvWXSFdW5ML68KFMIQkbsqJpqvWlRcXBf1jyy7mq6dq06cXLux7NqZIfie3S8sDsHye3Tth89p/Oxnuu3GsttM7RPn9Tl/3+aS+8baGqNXfkop/+P/4VdffOd3/t5/+U8+vPFLL5xpYayjW/Wi+/6MsbOoiT+68eXzfUwKGNvN8uGAaN4IbXb3Nz46y/zoujHZ2/jY3MPdiuHOhF8uC76repkPvquuXh581+ljc/0+5gokl6pgUiC59LeFAskL79+86P5XTu9fMMszTeP+j9+4cBh+vPbZCwx0voXNi1pYSOP/x8/++osb/8XzNXl6OcVZQCuqcx5S1RG6e608qfdowbXbvN0DyyppX05VWPc+zDdfNF+98V6E2H1S+7HzKl3/fur4xnvq+Gnt5SVDcbWeN99Lz7OzjcJPR/LP/+1feXHjD56vbU9bvFcpttovNVdPg9Ze7R0NJ5OYFSyb4cnsqg3Q7Wf0lm1c9fY5owiLRnH19g5PJ37P7GG9W6m7G1ftrltz561g/f42r9jfbF5mbq5c5x9cO1sPuFeOBu3yuHzjuBwUMC5ae6nqpoc0F1rl/LcEhv7HzyPyrdrXrtDK0gW2Ne6bX2Bbp6OlC2xr9rR5lZ5mp6K0XwrQf/75M9pyv99pj84Wt8bDNEyKI7R6NKyqLt9HlPzRRYf46sbaPS/N2atums/ZK7tYmrPX6WNz7T7mKtROS9t/66dh8n/6hZo/baZyGiq6eWMw4zDQMKSjKUYacrp4vPhfMX9d4+eZ/ylV25t44OsgIHt8D464J/Y4Ruhm7cuX3XavbPT2u/DjZnX112pfuuzq+/2y0W61Jxd/ofbZ8xeHTmfhsrmJieniTvXL0uWdvd3XF+3oRzfeu+Z2//hztXjesq7aDElx431I8XPP1ZIlxvdexNh872IsZ0qVef75mRmoyQri3V5vcLvoDmeKulcuZl9869IppIsvn59CuqTZpVNIl7e7uUa7VWSd0HfrZ8tQ5CSVbfzW82drQg/7QwxGcRio+gr/yQ6Ldift4b/VOQR57cbUQfq94WjvEGF6stWNS0CRL67XErUzpaLL29lYr525uG0X/W3dVpatdV12w/xa16VNL13rWtX25lptL2yHI+a78e9fW3soL18b/P+jvj5OQaOyfcZh+7Nrqr/4/Nk612kbR6MD0FkqCikRilqd9v4BsuX7nc/6sUUr/drG+l3vNk5z/xLVX3QXdXLjCp00T7eNLhuEy3rZXL+XufqN0yL4aor7F54/W2gFPcs6vXvlfvkUMsV293Yxahy0u/vVFrMZEvW5Ne6hO85408yugovvWHtjwsVNLFsRvvDq+RXhixtduiJ8aaubq1tdCCnVRMXfff4sK+LGuwVtD6SZHWCt+2WF48ej8cnax8+M/07v9MLhHmfXa7ufq316pmqmWsOfTenji+bqcYBPYTRfL/qTFj5d++EZp3vaL7pTASYXfLz2Q8sQGv20FKKJS1eRLn7YpRDg4svnIcAlzS6FAJe3u7lGuzNbhC1nU0/73/49eNp/dr32cWrgEM/feL149ySnFmgv9nhMf+uF2qenCZ9+zoDLmkPJGJuciyDU9efEn/hQ7XpBERdXPSnrw/aoHG78wfMHo1F/+CM7O0+ePNk+6R2NjurlNob7j+2U1MrN2Z/3e739zuTXQwz4wiX9TjFq9QaH26Mn7RGeb1kzy6950m7ul6PhXGegPeA9hyA/RXVR1c7LVJM8dxlSxqjYhz4uEroFkFXv9R6Pf+93jvZxyw7hme3+QX+NS4/bzbK35rWd9uNyzUv7xf66lw4PikG5Vz8ajXrduVuK4RBq2+63aft3iSca6+DpaIkeRu3Ho2nD1a/io7XvQ/SH8+6fbNQqy9jrgHmJ769976h9WMIWNj5ERlRvjxHHUvN70Kus7eGgMxyH9suuGm586pJLQnO4+xdfqH3qnClT49+15O9a8lUteV3DXWWVG6sNe/efWzyLhiy13l7Z9HoCriHADAQqzm26uFiAs00Xl3jluU0Xl7e3uaq9RWqy6vnPqMmqK89Tk3Xa3lyv7UWIeOmAnEHEy8ftHERc2erm6lbHEJFNIeI/+if/1mTO5bjXbt4rhnCy9IiW/bJOQXNtNHUzbMB4OxfPuay8dR5wrbx8ArhWNzsPuNZqd3ONducKK/1cFepkLnXjv3++9oWqoYfdbkkH7hWDk1gi6pcPu3QyS3JQNh7fH6/xZ4snE0kg2pf+q1/5nefqX1qzlZ9+rmpm/nCiqpm/WTWzsX4zM9Hg7vKzD9dpaf7sw3XuuD4++3CtxufPPly39c31Wr/3cSrOHs8jGBris/q3d37/r/z1v/X8Tz238U9pU+JCY3eK4/Z+paek6DbKTuf0dM/3MZvwI4sD8JWNdTue39S03j2TTU1rdjC/qWn9HjbX7WG2NkmenlLzC7/6y9Wmpo+kvTu9UXbcboze6IZ3xgV2UDevbcz60xZTW0xeFy/9r/8GXOEHNr5/4Q4yelH7wVM+OXfPP6zuqS27Z66EZnNxnM73s7tTe+l0PBZ+oxtunLuB1T5+pt8ld2wu3lEFb2anwfvv/cVfoZNkfvf52qdjXq3D3S0HhDxJx7eLLtDX4G6v027QgtrPPld7aaK28Xd71R33wTXN9Sagfa1Bn/eG+GLjOSNu1H6InGd/0DvqNvdmf2N1u7LD2kdi0XiMp3tSDJqnHc2dFrOihfFpMSsumj8tZo0WN1e26GufPBNxyTOglY/eWP50P3J62hMJc8G9m0vvHaec8Y4RZmdTjp7W8v9ZOjCIEOz9flk2DhDKRgflsD281R31Jg9VHYO3zDlqL/3flCc2a5952K13egiE823QNNK4CbJ8tDGbaU7b+KdVGxtrtTGXZuSi96zRyFyJ+6qLxyXuK5ucK3Ffp83NlW0uTLlNXPI/+kjt+uIwY3B+/0OnPjgGUHvVPAz9f379+8XN2hfoULgHvdfbx+Wt7mIDt7r3y0avC2rwPHdMfKr2URCaPTChsjusJtEmzHeyP/sjS6jw95z38y/XPtOg1LhXdtr7k/0ge0UL6GiP6NsBmOGkwc/Vfhjdo7fRXrv7aDw9tTccr1yd9vpS7fpk6/gQ4HOPuj27/VTWvUPKC/vluds/W/s4bSHYG54Anx3uFf32uUsuDkxC7NS+Ap7cKPcmWXfU22u1u+3hwV5nDIMh+d5wosVrholP1DYOi6fgnS3aVNPcq5+Qml4UjP4nvlL71Plfqe29/qBHqW3jezkTii41tY1TQfda4z02w43P3CZuf58OZO7RFqfOGxjDAYJ6OZ4cbWKU/vBRhU+mz7lxrdurx/PmU/uhfFCW75ZvH0A/r5Vlv3KosvaxKcAvho+HD7stuoj0v/uTtR+Ynqo2NjLD2fUXXvo/f+O3n6vHjXPtbyxvf+PC9s8bMz8z5vy7xvxdY36Gxvx7H5ojUVNj/sV/57ef+66lfdfSnqGlnQ9r4iysvfBdY/uusT07Y7t3feND3kyx49/5D36dsOMfvFDbWmx4YpDV7t9m+bRsphEIvTseXwDL9rJtJC/9K78OvP5a7XNhfBTTfkm7027N3goDHsPY2ucXL3owKLrDYv4qAvbtZbtQXvqTVVcb63S1sXZXcwz8JxY5xLPsbX630epWJ7uNVl+4sNtovZY312p5bqp8jWecTJWvo435qfI1295cq+1q9u3sJAnBx7b/M3+p4k2//nzNLNo+/p62WX2eWN+d3uj+CDGm+Wo5OQj7U7UbS15MMS5Ko5dPzP0+P5XKz72cYlkR8Xz3Ve8ZrUotSjxXRLzuTeMi4rW7mCsivkofm2v3MVdEfFp38L/8y7/y4sY/ulb7aCxpcfNhn079Sg7a/eH90VGTZpqW1ePS6OVVlB/fAHV/pfaFpZfdKztV3n3QO710WTkwXUrHbR0MeodlOC7aHfr64mnXC85w+fziOP/gxg+M+32ATEgvzaGZUFm7cTYXsfgr3XRjyU3qdD8dzTYsu2vz/F1zxR5q7pynf/P52udjG087GrQbM3VZNKtaaeHsBT1+IR8IbsclVhfcDzlp1xUVsfiF+D65dWONW1fWWq1uYnYhbeXV44W01Y3OLaSt1erm6lYXFtJ+7T+BW/zMtdpGpOmi2+1mO55MlPH+VgiWbd4838fc5s3zP483by65bW7z5vL7NpfcN2el0+K/n/4rfwpW+o9JB73ecHzO6PRNH9DBXHXX1Dr1Hrt+4U/84p/ERT+NdxvcqH30/E+cna/2XxEffnK5+s893rz6z/08Uf/52+bVv/S+zSX3jW1Pzb2+6x8/B8e9fz8rhif3yw7AQu/9vkPjc4uPvrFxroddfrqGgVS38CPdcuP8LeI0mFLqWnLP5rl75k6/83NnuP/mtdr3TzJas3yje6vZoZnWt84BYnX9eXG99uEmssvJVru7dTjceI6LH6794HAM9ree9AaPy8FWr9s5GbMYWhdaaHn3S+fQL9qlxaDFC2c0+ZNLV48Wb5hdPVr4bbx6tHjD3OrRkjs2F+9Yflrtn/mzv0bI61/76y/UfqC6oUJt/YKOGCK3/cG5Vxc+99I//1//9nO7f/93r9W+fO7qicbvj+if6X4ABld96dd/9XeeEz+PmzbQ0HExBCna6hcnxKmGG//wf77GqzKeuRKsraJbdE7o9Aeq59k5+/RoeDNlOkTnYmKYTl1gSgmXuxiywAL+l7PE8zSJ2qVW4qNI8X/Oxzx3zOF6v7K3/YK6EZlMXC6Fkyy30aVpmtvUp8EFlXNtvXOe6STILMltFpTIUxF5lgeXJ0aEPDurYdoeAaQOt0fHO1Wx10619DTaOZaT8qad5thWdoojJHBY6PZhX92MqWUh51FF7/M8zVlIuc+MFzEqzrIsCSL3EMRlibA+asEczxNcok3CJRdn/deHW4zK0Q73q8crD3uP2jvHYmd4vL/DW7xVbOG/pdnG55smzyLLWDQ8cGFkyJ2JSsgkD5nUxjLPY6ZtkkbtE+ZTE0OacmOSGBQuTvUVe225qtc8TRMXI/4jZWDeW5frGGBC0WXoDChEu8hTzqLkxiYeo85iGrhyULdJ2bq9KsWq/qBLE7IohHKZ0THmQXNLdpJawZVn0jOndGKlVY5luQo85FHboB10YqJbvz9b9RdTl6DPmCimnUs4TFUnMsvwHWwqcxg12LOVmmsjuE81yxOnEjqzg2XS+3X700KPR9HDQnKTm2BZYlmaYJRYVMwKGXkeE2/wsI47nimvmEqdsHnGLT7KqLN8TdsRVoyHL81snsI8coXhizl0Kq3xCTzOeh2s9yrNmOaJCxEOKQOe0sMtfZpYj6HP1u3OqKo76EhmOudZmnCnRBQuzWE8mQ4Zz3CR0mkINk+CkYpMyLtEs4RJJRCQgo6z3c10RmVAj08Qa9qH4wKFcbGjQjDodW9CaQqDYhKpjMkZyzh8Eo0Lk6jgjMxTowO6EFnqpA+OMelCHqSHV/rI5VmnjWZ3u2h2n46DTfOwjzBw0m3sHdHxTPhj+2B02LmpYoA35JznuUBQcU44Q2YLJUcBRcNdEqO5FFJmXsNFMowl4JiVLDEqmdFpc7vZO0JMbnTwfNvdcrTTKpDzet1t/OeVovmyZOyp0OyLRXOv3nu69zLHX7gM/w4Pek9wAf5ov1u+jGvoupsxOoQAh84yPL7WQhknMqYYS6NAOLA6SUQqMMQaw4Tw4dKAWG3ThCU6jTPW/Kh4OgnBRb89CfX4DoqvD3cefeuoHJzs8G3gbDH5tH3Y7lJwji6aLPcWSvdSBKfwl0hcAg+OlsN7WRpSHaVWPiUa4xTXwQcp80TEZHY01hQBiXNBBIuIp1gunTQ5giWzOkPKMDEGkyANJBQsBSxPKc9JQIQRpxmcIMthEUnyHkTgCyLAwBFARMq49ylCvshymGduDNfwSMlVREcZ3BqZz2UxS2CH0hiX+zRHWhPvQQS5IEIuDRIUAo03PMcIyOhMaiPaN4m2ViBKawbEkZiMqzQoRqHcQcJMRQcZryyC2FaLtiCZxcBmCNm5zzniATkkcnIqYxqDVQaJMjEYD6RMWKWxqVLByywnOJmYq4kgts9LwHlEsoiIbGkapOMml1KliTBQu9CS6wivgJdxjELGY6pdMPCJROsMSpJXNAUJCRYtwcLaM/hZAsyVK4u4gxDhEZJVZBQTcvyILA0EEb3JtQyZDSwyhaCBrJ7xq0ogz0kA9JHAyWLiEI088E8AcoLveyAEHZCIUg8UlSFEOERRWATSey6sRZw0SqvsqhKocxLEEOHcqQI2SGFfkuOJXSqDzlP4JX5SklOGyKyT6Bzx04kkTRGX8tQxnl5VAn3eHwN8DX4ltGY2yXnCcvgHc0YAU2XBJQZxCSkQ6QKZNjMpMqhJEorcuEVd0R/ltjkflBBsBPPKqiB5Ah/zSklkKSZgd9ZqOGREsgAeSLyATyZJkiuRwF55UHHtUXhS1lsgQvBHsy3M9GMlQO6AKpAbUhYygCXntAiKG6Et54kG6LAUnpAIQJ8ga6YZAKtMmFEmyLCuM54K8J3p+7DfaDdndoPsHPOdPnJoe3hQDn5kvxyND0u61XzlcXnycrj1bnH/JBjdyV59mLXfvTW8M+rU7/S2OuI17poPMOrdYXH7JvBO5nyeALvqCKvECAjvDVBh0JkBmwSCigo5FFklCQBwAsAb3ov0ohzn2ax8nXbrZLt3NKoPCow8idjo7/Tqo36lCDwjcjTGHQkCbWTA6nBLbaATwaXJgg0SWToATBDUt54BvCQIoamLwL1nHfXb21Vno6PmWBE3LQemESLRwDmKAa+DmcTMI5AJC6VrMj6QI8Qgn4K3GOBPzpCZ8QCISiGda3tY7tM7JbbbvUrDNwNQhEYQMyncIgnBJ8rrnOWQGQBWCK49w1A62LRlBswE4CuB91rADDRuZxsfzo7f3s6wMQTea2wNR1Qgjy8eDXcev7xPUnTq22V3O9z+prC9N3d7D29tv7Fz+HK/cwTcV+4MRi8/Av58me8ACO2U9J/B8OXwav+NXs83h+88eVfss/0Ht5tv5tlrhf1m763j0Zsttb/TqE+ap3d1NvfYK53yZUhxE/GO2cxbG5lLvCX2ajAKEupz4AAxRku7/QH7HMAztwhUliGrc0AJwNwZDb4L41bCbB/3tg+HZaPbrNDdsDFo98Eqi52iDboPe1AxFYYDhWgPywBCt0hHSWZUrkFI8JNlkes8zyMYggUt8Am0zCxzxFP0meHVt4ejrYNiVHarLUukQ9rMc1gMHk9371R+COQBVp5lGVCBC5mJyEZIvSlCjw65FXh6WKJSJnIgxwyJg8FWED4hXDjDh3Uvtk+Kg14PnW0/6lN3w71BOSoGcMF2d586QyQVSKkShiLxUCw3VsF2wCEVInFeEUkHZcOMPOcuApt4GLvIBAOvlWdUrl6MtuvUKD0YfaDGNVJYBjtksDe4jw/CKx+Zt0C4UKNUzniNsIrxovmHFNAHyJhLDBpcMJw1PuoMtqtdTqODQe9o/6Dq5ajbpm0HRQfW/wqtLndO9trNl9/+xpGI7xzzm4yDVgPOJg72kWeQH11zaAlkSqhokL4zODf+BY4Bs8zwuN4x0J8kgDueEZ3GdrM9/NbRkKgHddw93UG1g4+HGLb6UbcJX1H1QjXBXwrX0A6po84K2fANDKYRTVm3pBRnHRgc0IwXQBhaSDyxS6EC0HFgPB15EhHkUshL6jAS4AfRx+SgmDzTfh2pOr2j7n45lUq3Cq4K37KWS6cbvOlKBJgmb3AlG043q+wPCIwABqEymyWCs8ABMBUyG6CeVpErA1gI5q1ShJPMgyKxXPvgOdAHAs3VpQJsaTrbarmmN9Aab2rd8M16yRtFy9VlNYkEHC4wIBYAEeEpi0zA1GE6EjEzFcIAnCtOTh4gv0QW4oinCrTJZjTNstYIDkcnnXI4Fc40Xave5N4AdYAaNZiu+zpvtQopLZzdbzeGECvAtODwSAwJAj9wu1PwUsUAin2uYclCITvylKC68xAxsnRcRZEKlcqri+UapixNq2gIU4KtNL01DVmvw/3rzbouWSVWKlNQqMwiI0I9LsWIQQ/eGe6t09EEcKiYskzBfTPIhAgqDEgguFXGMrNiDAcl2XrZbU7Y/bzll6VvSFkXqoCfW1Foj7EtmnVlWvV6IVUVDqzkViTgDzz1pAuENgxgAOawOhKfURwWF0OeChdSnkYJbSYmRUyH3Opq8k0UuPA12DASYYEACsk4tCqRbb3k9Tq8QtXrutJkDlLsrU98hr+MAklNMsREmHwC4G19NFFDcHwVgFG1kzlokXaETymnnyXSBtt+0h/jix2zTRT0SX+r3W10AAigwiH+r1MMh+3G1uigPCyHFRAlCaRONQPPUKkHL7bgPjB/qRMJKIw0hxzBpUiBwMEDkQCdkDkiaaYMMzxmTl1BAuhytFMVam8BHA4KYONKd6eyAGXBiBONeASTUnmUEnQtRpDUnBvk4IxIAktBSokkuUCTCpmBTXkkMuvXlOXR2KYAaLYGJWL5FJQjkDKVIMPBzXgK1idE0BKK4EjqgP4GgR1hHcEb0mB4hCREYxBUDSfSuH73E2Yw/mfrsL1Pm1+nYniATwVKiD4ymiYCNUhSExzCDPAn8ryLRsOXtAJ7dSHGDEwdmMOCs2gEhvcoxik10ToiNGdAbxbwBxwMeodlCJp+JDgErEHTE3CohKdZBgOiiX5itBDDput3X82yl+PXlc19oLn1crDVKfeLxsmpdUQreM4MzdwFuIxlnqVS54lAuDYKIDelKUWfASEJxOmonUeG5wlcmyagrmAdc4Lht9nPp+JYzS2SBkZfQB6kMZ5wEYzJU60xSMHjp0Ro4BCb50kqEyA7Grg0BEZAeIk4QE7lqF80Hu9wvu129iDTTv2o3Wnu9A96AG2Tf07tlVEeBS0GMUByYgYgH/AeODQVgIhIoDHAYOFGAF1OguqynMmYA5M48I5lI7UgAXnt5IvqmRFzwFmYlpaLqNFjktk8IN5naYKg7zAuAHIIYRmQkJAwG68ymyI9hSTF+K3u8bDXPKJo2hiflL9FazA0JPTv1jjS0qNnPgK5KKBW+EkK/gymDmCl4BaBWRXBRbRIfdBMaAneDGxm4UAcQCPHlzMZiCZ5D/v9QY/qMLd7g30QieMdxqs94EhoVf3izjEjMrdFCz9b1eE69d7TLQbbqea1DK25AAECACJu5UlwGSMGgDSJHCA8R7ZGSLcKPNeD3KbBIJTqDFDXMP0eZanoymAqREiQXxIgpBAVBj0DfkIkS8DBpIsy8ejYhRwsAaYC+wR7A9zOUyTomME8/fsX4pBgFB6fM0cEHvQzS4HuBRzTk49ak2GcsiqEG2QbLUAwoJAEzDGCLsI41ZwUDTCWdtkpnlRCdHr7Pcq1FSvsjfb2j4pBc4++Ha8UWSQkLTKkUGAlJAcFW/CRJvg5RzCXicscHCWxNOdjckkTqwFQCaYDQsPW7bnfezIpYD3rO2OOchSiMEKTzDmCs0RuAK12SOAA0xjval0TRgtjBIpjMsUwpUQLpfZX7Due7OH5T3unCRJQH+RwcLIoAajT1GSwcoTxYJinFYicSL8EbFAIh6AL0Aq8IEaZ8oUnf1p2h2MqPuzz8QqHoXyMlFhBSwlWkafgTQxpF04O/8/REDJy5oVg8H4KALlHQoAziDRyO9dBB0yqAWaK0Fb1Mj5wY6e3xW7d6r/ZeSrffNhB8FR37wz0g3vhfsvctm9uP+m1WuKmMuTeGZFTCSO28GcW04TrzOc2oeBvYWwWSZDB4RGUbY7nhIqRUI1hbE1B7iwI8uidg/ytb945iU9F/1t3w0QYArmMWeKVAGUAKAr4hMGhPGJsoKVQdG4yxCiITPP9ITfQdxQ0q+jkvNcf7reblRRtKiAZToY9NBsHvXYDEY/GGQxXAhdpkUgD2GOQU7gEMIEbGQYohJicWZiYDOAuUmegFrS44hScErYeL+2QPlf/2Tsqqt4CT0UMJrcIJhjNRCB1gFAwrbIoTGBg7oS7kI0pqwEoMMuRZl0GUMhBkOZ663XLYXu/W3TGhtV8PNx5o1ver76iqtH76WvZfYMw9srxy1zj/6mbMkkkqI0JQWWOI8DmiKIhCA1qnZANULjnIIuw+MQACTBDS0scNyU8V2bt/tE3xU/jEpgtsnMkJAGEAzem5buc4hUCiscfLPGR49nBpDNahY0yDx6gKMXoXrG/V47LwcsV9ni2PU8n6qZkZYsOZCF0WTGVuQ/HoCq9wYRdDbcbCplKNFSjLlrOw4AYa0HQ7f13b6ZGWosujTJwvCRC83lqA12SC3BVwEBIGjgSPaCo8aAuOQI8onAmE3jBvIQHvT7NiVaa2RkNyscId9tDqm9tDWiCuCWkUKJk9Ua9MHXnSq8aAlAXEBskFFTUuqKuIdx0OSG3AFtIpQhLCLIaDpg7i6CfM8DRaGC2NNkjQSdykSAVJxHsBrwBxB7Bwl4s3fjvBphxk6bPDstB0d2ffr11+s0WFeuws7UFA/QeMkBB6RCxYL+Z9kkGviYYHsOAb2pyVyTrDPQUQMCoiGypwAJDyNcTp9po0jsabT0p69PqlKLf3+6ARwxHNE1q6qphfVk0LVPCO1YXvjkm7yDkwqdc2QSxKRcZlY5IQDkBfXAjEVGgfx1Aj5nxwFXwsyhYAnCXRDMzu/0+BQQXdprXeZ2JFq83ilKJairGG4pYoHkIPWBiEp5ANQQqJlTTEh3ALS0K+SwA3qQScTACmygAHig+dfG9yzf73RZBUgqN356XuoXwBEAjmwKGpb1pMldNOXjBlXaZD0DbRoksAFYI4NMsM8hUeW5g1AaxMzOpVjnoAs2950mGuGoB0tyzlppeMTUY10d8+05vet5zb1wf++27xQmFiW/f7Q1Hd4/o7YjD8tv3yuN2+eTb9w/a/X67u48/en1cSP9+u9HisuVNOaMJhqxiMWqtli7IST0SYbXAGAGrMmdpaT1HrrBRYJylEtF6mSqXIlSnAigEmskRzkLMkb+iEyqjdeE1/XGlJo7aCyPnTKOJvOkazUYTf8pCu1bFdxkDq1XRwt49zQqDVFvYl2aSC6bBd5DKOSFnmJpx9BY/RtUcHkOXAmB+p+T1LVHo0pcNOKG1ukSmtWMHFvAHhvwOcgGCK3IR8AR5YoHmoomAZEiWgKM8d0TKGNCfDQSMcpZq5t+HgmeMalZQRjP2ddl0ouHAEnxZrxSrY8jA0jghfyMSFpG7kyCFjshvdMg/cyZlCoAceZwWhTVNGlKlGpUdifSZy1kwYVVRZ7ZeYmQboDJirNCYAdpGEfEg6JmSrYuI0pHmDnOZKasSgDzHXeoAPoA9oFWkFoyBM9DqMxH01CcnbjdrufAx32BMqFbRsi0rGlyN56uIVDMrQW2VjKm1FiICDiPuhBTWDENAWgYYZakMlub8YckiTaBz6F5/p+VuFr7FDDhOQfmn5UpbFpXCQfXSIPNIczOMK64t9AuAnuYKIIJJnXgwJGBro4FgLWg8zBqZCeEdKCDK9y744KhLZyPOhQWlm+ChLWbLhtCtZr1hKlihEZqQi6gcFZQxjTqFhIngSYbgTetGjurzaB0M5M4KPEGigIsMsCqA6Puw3jEwW0w8daWVafl6q1U0OatLY2W9mgkBj7fIOy7hFnHBgWxYAcAGa4nSKq8SmiKK+P9aOMSLVGQMgVk6mpPKpHnWcpbWAX35FveyrmC10hdyPOjgC9COlVyBwlARIxUCE7gFtAWhzHMYJ/KAE9zoDNZuTeqCAFSi4lqv1hQUWJYmdkcDZEJksa2i3945Ztt86S/jVVA6sBXUNs/QF9XJJTGxVoE9xtTQQmyUGZX4+RRw14PnIaLCw1IN5JTpZD25Jh+muqOPe2V1XFB11GHZLQdbRjearF7IVqPpeMGbLSlbIDysbOh6o+UarGQe+FpLwN9SeqPLhrei2QTQLEvk5yom5CZBIs1AE7xCZDXAF+AXDsFMpwAeoIcM1oyf0tx5a5yVwKiCUxW2sG7NbLbkafBxr1/QjP4UyW81JG+KVqG8EU1RtjyrY/SBFHRZ4slUC30zUfe2SeVXDVoslIURvu5UXbJmUR8Twmpbt2fmJlEf5snPAlwMBNFRXTMtR6SJkAZ27jOMTaoBXgNVW1NRlXERvNEF9l4f7fRxdqZ7kbechaTe0wSvQ/gSLdEwArG37puuJVot0xB4VtlqWYSWphKmrLNmq2RNXMCbRVkVx3MEOQpwgOFpnuUmRTShIttorY9Kx9wzWpQEMQQDhLuCVccsobX/qHO5Jk24+GGqjdKTJ9oqgWm193imEoyLmEKjzjkTjYaF7NArrUoWzBcYtMK6Jtela9GSQ1kaBWuteA/COAfBSSizWFnlTGZ5LpHaDZhqNdfEYX0AKZHWuhwCEr7L04RZp8V6D1Rh3PbTsjPcOhwfCLVD3AshY6c+6D0ZloPtw16zHIxXMlIpU8RuLplLA6et/tog6vnUMsBSqxUomQYTTAwoIZXiVCWJPnB8SMWaofESkYZFt1nvPZ0RSdJivEYkt1FZJ+GEGZKGY9YjeOfou6oS5mBfXNEsj9MIfwLD7sFaTbbmsF8okl+iJZ8b4YB3aOWdJ0gJKXi0CBwPkIGSSulSqpammlxgd2C4XAkGoojgGDMv12Qul4i0REvWCpVn0mPouAqS5QrGA8uKGmRWALEb56AT5zWuA/nD0OLPGHLruBVzE6qPhtuVBK1OMSgXqtOoIG2reFIOweR31LbdZtWKx+zXZ2uT6AE6UDx6qnFSMoUcIbG0egsX5obLjAFyC5r9SD1CbMpEFo2DfdNq/fuQib4a/zD5fmtaTDcatRAhq6tuWo1RA43RYMIIl4BJniUaENZFSKudr/QT4AOIJVkCV9TEFhyTQOB6fuLhmQlIc6WnEiJzBa5T8KsIROcTkHyXi5h6mogJEQA8l0xTHpImZC4zCVAf8JXMTQaC852TUJyKCCez8DSRgr8LCGQcWC4xVMUUI7hKEyEwfOZAbZBlBaROwQYSmulHRFxXxMnK73iyf4fPVsmOvztdDEan1oOgIpjC3dBH8MxmDsamkAqc854WaQ0sUeYK7AQRWNG0LcATLXtdTaCltapMGlreSql2TyW0kyKzmeSSFtWklZmjkkFgtEhTW8qkOXwhwLgIY4Ke2nVF6Pf6fUSmR0MqH8dw7BwdNqdfTrXhUlqAVJox5NskIQpsjfWgBtCTQUYWxHJlNNaoDLQ5ZJRYVYpvo81nQDlizfapwg+n41EVTE87UyHSnGySaWBZ8ABQ20wK74J1GWCtMABSAB6wWA/1aJ4xJBfhafsTy7V2qztTZ51FIYRBokQ/CU140fQJp5VVAPvUVEVJADFULU5/RSA2quHKaAcYHDiPqzvTZ52BLSgjjM2YxnD5AGQBUk6bPgBLgHJ9miEPIpJF8FsjlEQc85m1tIXPqpit7sycTYiCWUtNlfQaVpPGFAkYlA4qdcEHKjCEm0uqgbXg1IYAmwb0pnVzWufP/IWdHbXH+wymnrN1VOH5PHEZ7o8SkQ05KiQUauBDmtMWICU4ckQGlBio/F+gL84JcQnaEZHomdrUxnC8t3y4fdAbPSoG47paSZUOORC1hNPnSVUBGwSoFfgU7QHLqcLOJkBwSgRP1Qicdl8HGAZB7LPJguZ2p90tt6raIioH7e7Q571RsT8uYW7sDMpOWQxLKrntVGWOeDAABg5/F8CA8PYMmkySGKLDWAGDRpN7Yi2CTIalGDskApCW1CAv8jPC35RSd46O+kf7wxMx8chq8p0EgQMOmtWSY6+71ae3dw4BG9qNx+e+nwwxrdrcBD/nREGBvlO4JaiFzFQKhCVgvynIXZAWWhIyKJMJmRgPBJFDcWCqMtozb2k2tiebfYfbx+3hUdEZjo6a7d64vlvsVPyNtjFEbsnD8yAMnhlwEygFwAHtI62B5UpQ9yzPWYYcZxHEKUk7/BOUyM6mEJqH/e32Ichgfziuym0+3sHno3aFag2VOYSIgUWjtAMiSlr0YwKxMJV4Njwjoj8AJnIZLSpIjIuO8JckROvPUlbZbQB7b08qySb5KGkPGkeIhPf7W2FQ1Ldir9PcagpbmqZ2THuYKoa6KZ0ubL1oqrpiLTtZf8yAIgEewQJzB+bAAaFzBERYIXxJww6EzqCXhGEcuBMhjd6mnrJEThX2/j1I1iqMJp2Dd8LEXVEvoAjwVdFqMtnkjUqym8HDp4JlSAnwNgKKiBkwCfBRFRWVPlC5KdVYAQSDrkUPw6i+0ogPIbuqYL3HW+BjXFnJ67psCjDmhjaGNyzkbTYEAt1EZZojE2TQpkg5qBXLE+GTAIvhIcsc0n6opko8WIBCkLUGOVYERD6FxAYG9h4ka5hSWOLATVU2mTF1jJqkSZxGacumk2OVJTnCP83dpp4IQJ7SQwD/R64FzT+7AHwLW1cUprMEg54z2o+aKmRgb9jaglXDaH1Zr4t6sywQhRtlvcRfyjkFGt5QoIBjmZQ2MHKMdtAJmYwBcJMWcDJlSAVARKnVSMdpwmm9VcG5YGU5KDqRJxCbq8nUKnmj1eCqbpV3de5swzpQTdGqW9Hkyk1GkGVU8JpzZCzQ70gF5XmaUXVi5qu9OrmDYdJOYWQUxEmqAQcchnQ0ESriFYTC4KmyaLWaLW9aqmFaHjnTmdLLpm76Rh2pYyKUAx2PMlAwDrTHMuUBwoAM54C6EJApYG04IW0OdzQtD6/mXHn8QcViVxWqhQxWYNRKLWFGUJHC/1oYGUXVw2OhbkKDKUsFxEEwShPljEl9TJHvrXJcMwfepICUcqpcS5hnoAUJrf3BE8DK1doyJSeDzngIwcGadUCiJqeJgrJs0ZptnUNOOKRUflo3gXjMc2+pAglUF8SEOSRiOGIkmgS3k4jiUgZEM6cz6DNPVMiQP4TPebq+XZ1JZkut6o1G0VIaKQixSxDfdrqpG00FcDdWGUtpLh8ALM0ldxHwFrFAwMKpQjhBkIVJMQ9HAe1EHIwyoSMGUtoahxzv1nfCiWAYS9NCLmkBj5Ql8HQJbFk0CdC3mhjKUjbHggkXwIc0bAtBQRuoygBbxwS+qZDReFSG2dyC3XmReppQMOBLTiMpAGfZ5D0I1mjgzkKUxpdCNwD7oDBEDKe1YaxRTHNQDAFGBWQDA4Jhp7TRm9ZJ8gSUToArSOiQzjzQQHuE7dIUxiAhNOK+4mFtydLyuBhLpmUdYQFm3ELEL1qm0SxZi7WKutFNAqhTK0soIcgIEI1Ijr99hM6Qm3MqyEHclPhfniuMa56BYmbA0N4TIEFI8YleW7KvD8rH0+zYqnPFWcPpFoK7QU5qGFca5pG4nZATyQC3BUIko2kxkSnEc9hdmtJwAgZFoBoHImV1ojULivPUhwgf4YxmjZRd3/4nkpHOmrqQBWdFIUzdyAKgVzdKZwUghYQJTsurqIiKNn9oKt/HkHMPw+eANsyamDiTAl/RemgetKPibEYF/95J3BCSKN6DZA3fLEWDwY61Mw1e1G0dERZPyhHZ3EQy5G1Fu0Bp0SIS0GYCoU0gjHkk7MQQN80QJJDdRVU4zyTjXmc0FeJkcgUHeLWsD8aD6esYtxIczxWy5ZsWPFc0G7LRAP10gpcTlUmwMx2Epu3doaoCtA4hFUgMclB9hwdBDJxGOOZULBY0M1kAJk0Awfz6of9MskZRMFmqRt0Zz0orTNminZMN24TH2tP4n2ag6bCdTDOJyBUMwWFgH/gmbcWAtWF8wVMiza1k8AGEGEVZw2kVxPq4dSIYJSauWs7VgQqbWtfrSjUwkKLp60CiTJ3i1iQDeYpwSk3lekiAJqMjTDKQT5kSYrfRKprSy4nJCww0kgVMH/IjzcYrqwySNZ2gs0o00nnD8YJKFZotpxgvpPTN5iTMUpWGBmYmyBxob2p0qYipZAr0TQNBmNRlAmhVgtpFRbkpBbrlwmW0LmjXFexBe9Qpt2IHLGYLtoWooWDQSJ5agjo0CiB5JoRBlBXTaAYOi+CONBgAAhPBQXEEreTgGUKek2cQ/WEG0QLkHWiMRAOhhrjI+3FtrD8RjcwMyKtlBbzANG2L1Rnt6QJM9NoUrq6aU0it4G0IqJnEiEEeraMA4Ao0QWFh+TSZATKCRA9P8pmWaYZ4Z1PkJoJn9izOtsD1DsvedKNNf1qY2BgUTzrlYFzu2u/u3wzIvs6lNOUrONwyKjoSBRxcJaD1FNpANAQCvqUlfEAfxDPAr9xLsFETz5BN9fCLO5cbw+ErreKw3Tl5+Y1+2f3a/aJLazWW9tiFQNyfrCAw5pFZIjwQqqeqd+uAVnJO+BcsP2O0VVfmSR6inynwXrvLH1GMUWHKzTRJg5dpyjLgO801fAN4zXnNMoK6sFdKvJILzwLiCvKzth6YTtHRAX5mcXLtrr/YbA9p/8XLwydF/yY3KsdTsmAl9C2pypIWcD3Un9CuNGcTqjQHCYWfGuo8C5YKY4mx6MjW7v9er94b9W7S+TsZSI+nJT2J5qhYSpI3IhEJmcANoXWfBilMoKV4m0sIQsdHBTzz+qM77u9HwOZvkq71RN+MitUzmrJQVMllE8SnyGHQSdAenDYkqcRHTpM9HorO4Wg6VRGpUuZpXH+ox/1/AcmBsS8IPBxrj4pOu4G/RfWNmPlGVt/ImW9U9Y2a+UZX3+iZb0z1jZn5xlbf2JlvXPWNm/nGV9/4029sMhb1a/c7Rf3/+/Kemi9t1fjiWZVtBj8yAFzWGBud9BmCZY7YntCBYRJ8MaQI98D+OncsUSlounE0FwBWJMLMXufJwI5L8idVrNVbD3eOudh52KAZlieD12T79Qflq0f391rHb45uP0n6mr3WvV3wb75u32Zv6u6TKaO1gYwsQeA0WYypoZ1VPgWyohUGOiGN6q2zLImIM4pTBU9K+3Vp/hLSXS5Xpxj1do6F3LlvjvwTFW8/fHD31QPz8K37T9p3v/6m/JZusul0kgWdYbnMwKa1pQOZuES4jNYgWINceEU/ZJLn1XkhVIApoU4nDC1OO762HCdTOR49VU/eab89ypPGRAaR5lniHT2XzDWn7XZ0UhocnAmaCLe0TO2iRmCns91yirZRIy9zCIz8cqkMhwW9spdMpCp+5tLvtDoPD+59y4ze/Waj82a2u/VWc3/r1lG7SId3GrcO3nSj6cYD4RGCYqBKJS95ZpDFIBbBywQ4JnUc8A+ICr8gRDhLhwexqmQs0EbBJH+Ggn2T1vzooACTRELoiLJZqmntiaUhS0KSc5grHSaTpFwBAEqkLtrESSeKZIG2hCXsXFieF4i+KwcDOvpO6J3dBw9ffXRrn++1zcg9Tl597RCBVry1+8ZIb715J9/vJ6OByZ5sjQVLA3e+OsPMZ8hK6E9gqKA5WskTFuQUyVlECYyDSA8qhKtyjHeknWwzmxSeiWBH3LKJYBlXVNbnJBBBYHTglUszhWH0tLMyg8sZK8E74GSg2zmyKa+28RsIy9OZ/QTrCXZ/UTD/dufg5OTBgfPfmM5GKiMtnUKWAw0lztGO2xyjBJCBbJICc6ZwOgU0zuAFgdGsUsSwWpVJ4MBzqW5Ooi4i9hDZ/FF/51iJna3ciNaj0bdef/eW2L2b7L8Zu0/sq3nvyZNbt0Sn0X28FfLWYP/NwfHb7/Sb+b587Z2n4fbwtdv15p1teg3fhO8jN1stATMRoIKgGTHaAklbFqSDtTNvORVJ0iwF6AZiK52bB0ygQDPk+ez4HRR5OjmntPacToh08JeU1rBSjGzMgY4d8EtQgHPe0zZRBOFE5Q4wThkvaDrN5+Zyg3y2Ik8xNVh+oFo0pWjBHuZJ9U+A8hj5QPgyBx4UViMJ21xZB5ugWidaNzcu8UF8gCJPSR3IozG0jzCnw8QUTNVGmuDMgwqOihJTOqE6UKEvbazOhIQJwfPxmXY0fZCGMZ1KNoiZ0Wc+R9RWdMAhVJl7cjsWfZ7zhPYaIbFoHWh/mxE51zR3GQWdRsEuT3jPVmR/5n4ORArk1AvkQgAA6JubHGgFiJnOlfMwdga243NAHAI4Euk5ySiucSk/OJE5O53tIo+LTmVEYCKifg4EaWjWKGrwpAB+Vs3V6QzAglgTp1NBaE8/OFb+AWqZ84nIwDvGSQfQI6MQ2kE6CdJvkoyOmQUapLCWVNtYldJ4HlrywLgwgCQTxeUo5NmKLKapxIhIm/NoTY2BIuXSps6aPMZECZi0ciCqwkhasHGpz8HMJZFKb/AnMvYHKPI0lWiPnEvn/yLpOjrpjVnNDFUqmcxxFjEIJvisWnnNkgRZRStbregiQmvxAWY/Pp2F0TT9QkYKgCyo3kvTRkNEDwAqn+Ww4JzWdBgiNH5kNARQP001ewdzuRx0PVuR9ekEPeNIDyLJiMkkOo9WKJ7gI6eTrBKqYg0aMB6BWjBNO+wEIHXKUkRDoNwPUGRz6n6agQRIoB46KgFEB/iiWmDwXLgoHWi/YQEhGt6XB3yp6UQsMDrADrsCqD1bkafZDxwpp2OcAh1aqpUXQaRBpjaGFIAoABELntBRLnT4qk0kJE0hM3QMHh2yDzJiTLOfoOPgqO4F/6XCxYxOi868AduFMceMZlZgs6BZGmwno5OsE4STDIBaIWhkH6DI0+ynRYg2wBSc0BCRcl8Ky/Ue7MbTltREWPAbCVRn8UhUjBbpuJBgOOxoZtvTeiL3l4j8Ztlm3wJW7D7a6r8T7tx50m99Kxn5fnFi3rkVm8Owe+fWwVvZkzdnogZAJeJEBvPIUquNdioKxOEcNDcFbxbI1yL4GMBBLJV+c1YdzJtSBQ0dWfABiz2NHHT6dJIaFum0EiBklZsMiYTmUDMMg5NchkQi7+RKZOR/LiDLS0Yru4HZ8AGLPY0eMleeUL4MqaIpFSOso2MwpE3wKHQqvLfa0tSpxFAwL2lKWkUWtAU7Vh+02KeLIs5VE+OKBQRq5uhYYKSOoNMQaXoxAoIoBJmQSWERXJBfXASpylPrAKGuCvnfr9jTKJJg3CNtFabjBjSVqDivKQl642Eslg4UJFkF7duytHuDi6o8CVZPU8QfsNjTSOJlKulEauALT/X+aRaQxQ3VtjOqlEoCwqDUkbzR0nkXdHwJbAceyaju71Kxe/2yS2LvHEu1c1ge3v9G8fUhF+Z2+5v9GLYeHr9df0ccv9V9J9bfqD8Sb7z1zZM33rg/UM23dt9+uD/c3+InqltNWYCFWpAlADapgSS8iymdCCyNT5AUqb4NdJV5BUSKNMNors7SomeErXvuL7fl9yvmq6di0mEfiLkZEDunI0GAdhynEmnmrEwCkDRnnHZdgHlrROvoYShRUs2cAGS9HMktinm8SswH97cOj9j9ROtb0xlWZoWztEmW0ZkK8HhmQgQFjZDZ8jw19I5C2jWXZbT4YOiMS+4ylYdIlaOXykflyW0ST7Cd/tN2POm7x8du99X9/K3B68m79i0+rJTkVYYYhOEMIYuS6h2p7NHR0f4SOTbRNP0FRRoBypGaFJ17Ar50HCECWfq+hPgmf9rJm2L3zezx1HG1lbS5P0ayFh9oUgmDFuhUPCSjBAFfK0FvWzC5g+qyNIG7GkZH+GR5enlSulSc7JE5Lw6IU+IzHQ3SpEqZzmISqcgmmJBXm3MMsj2H4VDtJp1aKWgvER0uo2mG+HJLv1Scrz/w58WRjE6Hzqk2GIEXsI0KcBwtulZnNAipFEeqplNZE4FgEYE8wE9yOrkOkfzyaLwgTjYrzu5uq2x081e/fnc6JS6rai3wB3qpAcbBcYDeLKOiLmJGObIB7R9SKiB2cbLfgJjLAxUq0W71S0UZVAs7O8fc7byWv9FJut/KjrzIB/z2YfbQt2JsqHD7rulMp8Gr0xeUIjqTA3rlitOpMxFDhYylaLU80FS8yKRjzLCMaaRY4TWLXPqUX27E88IczghzpA6fvvbag4f8tf0pOKGl1tQEJS0gagJ7UXTKKih5VUnDgRRhLfB4yaGXNKGi4MBS6AM5Ffl/LUEkW08rCYiKzXXKPVVc0yklmeTAIMFltFsDOEPR7gAHPoVAnRk6dzlNMk6l40zpyznWJcLcf6jPC6PobH3GFcs1o9MbEZQ1QCQdSJwAuwk6vjAyKZOcUD0HRVUm4YSXDCB1dnkwvkSYtx92lggTpaATsgPVjymqZlYIwQDnNIlt8iS3IXfRwVrpBSuJowFEpshyg2io4nseJghzHJ5UcVdUJcacGTpigvb1OW3guqlPFE/pPAQXiTlktGbBI9k1vMkbwBhAdJ9fRR+Xm2zQQPZ0ZrPmVDNkg6HjSAMdgEuVEnlFd8G9naNzpqlEhmoztc5yDkq2YgnpEkHs1yeCHHffPR2WDEEkt1SbCtdNdUplEjCLJEtyBLmIOC9zhlSgODITEiXCHs3aIhsp62fC26AcdsvOfOVG0WzuTM5vpC3hW+3D/mD81t5X6mXR6HVf5jctFb4BFMAhoW9D9UcIZ7DYVDiq5E0ygcBi6JwjLhgyNkgzFZSnVMjhZiBDuz7zhpb5t7O80qi3ui9/66g8Ku9W+w5vkvoRTtE2SFiQjM67To2D3engBO27lSnAis0AtVgERuScVs9ETFOVxZmXCc31ul+OjtrNV063E9BS/6A9Ksf7FfBxZzCq09E33fLpEZ1rSLu7DotR42C7GPafvkK/PB22my9//uGt9CbteJOaZSKll3CkDhgqBAQXn8NUFRBUCqgVA53QYyQV0UvgCp87GIuzs4tCczIeVS+xwjjU282b2gvFfPB08jOeDOlFalg7wkFgQOdEsJGEUwOglrg0InjQRl0v4Kg0oXg2wTKu0NluHjbGh4zHokkVO+ntJFR/vlp2ZrZu0bQHkAO9VcNHR+/5MdWxBTKDfeUmoRUYSIa8FRUD+ECOTZHJQDlziJSfdftouIV+q70qTwZlZ+IENBkquAbpaJZbXIjqKJqMDunIA0F8Y2lK1wFdAUrkBpdr4TSd8QJjk9bhEjo7j4CqTvKYU04XK/qUzJ7vE1gBoEqltNCY00t+NNUSICMmdPKxqnbbBi8gi8pFTttTqKxMyWiMR/qQq/oU+nyftOW9OvBB54mttvCa3NPLU5gQWZIFAd+m8xWRgbIMaJde+YOBTTPEwCxVblWf1p3v01A5kM9DahI6Tj2RFLASZenYzyQDeKTXpyCapTynKskkyUkjUG3Cfc7Eqj5Bfs73CdOkyR2mPe21A/01OQdyVdCtp32asOkM+ARALKNj/4SJntPhZDTxjXSYrOgTEWCJbtEImoS6Mk77smGbJvU8owU4oESqSecJSC6dxcpF5qIAv+UmhYC509Kv6tMtGU9DrwWxOaAEnBUQBiHL06mmGKpMCo5oXL00hs6elbR+AswXExcQuKiQf2aX+PI+HV9it4bOdjQmIBlHOiMTSI7TSi46dwK8lyFl0fvIsphLWrvxtDoC2Yjc2yRZNZ5UAL3kOQ3oqgO1oKOxPRwPwRY4jXNgOyrNjhJQAJASEU8rRe9AAHckP8k0KMqq5/SGne8zykjlcz6nN3qgA3A88EkqrqR1EUPHDaaWSgy5oyN96f1MjnZlCjgzrlIr+uwOtob9Yot2z58e0xuE0VkaEMQkkheaSalejDZIeapfCzBR2qQFz6GTwI0nhARCiiHQRswUCT8abiO7HSKbVoG3qijrdas3E1RH+m1VB7pXRksH8GJgmOH0qgqV0AHVyGIeGs807VxDh/SiDvBNBnexMGVfHY9LGzzFWRB6XGy1tme2TVedTvYlgsPoba631fTdLkNCBOVWq9iqD4puc7ilGBvPZNA7hxSnl0rRzgsJU/a0BmWFpzNCQeV4RktY9EIGASZlNYI+wLRExqfXNz5DccalvpnhkvbDcAmz4o62gDmJmO1SBCqes0AFyiJj9G7HFLgsKquBzZIAo9Mzk5vPRh5x03AwFwHlAKiDIYBHwuJh42CfAnYJJqx1CirBaRoDgie02yI6evEKvVLtfSto2Ou0m1t+MlyMISsgvNKMKZAzABptT8i91pJOtcuqOrEkULkr4gWdJYOkTZgeQwatqWcnzXj/Cxye3EYzQKFMAQom9J69BPA95AmdmJIGH2ijsrdB0BsQ6CRIUR0YCjibPVtxBOQBPAw5o8k2JmHbXACV0VZlZFiXegc0STvivIF4GskDPAJOjxCiOFz8TD2dFjdbo/bjUe8xHUewPf4TQHrraOzdvfqjnbPft0ZPJ592WkUHnj95gUP1371Ou753TCdRcj4+LZ3eqZoTvEGmVpKQOxwqlQjeild7vqnQ3PGU0amJ1oP6SHrdGx3kapGkv/NS0l7tAK8bpxtYNZ2m5pFwfB5NFj0tqFuQUnwLGOcxxIomDEESDKO34iQzp8w+UyFnBNSeVk818DYzgo44sMQb6bTOSNPbnIqVPZ2xxwFbPb2ZIaNKbIDntDoQ/FTAw6I/z9rwRatNJ53TFOcB4sGe23PbjaPBTdrL5QwoIPpSFvifTphPCHTEAGiRskivBeFBE4QO0WjFGXIItJICksy8UPaweEqsrN7rjYajQdGfUrTTL6oXxNnqzJPT785eDEGvispsIhJGFUZc0gvjELARGBEKI719FXnK0ipWmtL+OEeb5hCzWBBIPO7KYjxalIKytZIq5LQNKTN0YDajt8168NSQwLyr404SWkCDZ3kfkUoChaBUG3pPC9DfSiEWzgqRlx0Bk9FLc5CYLO2DQiDiSfS0rycCphC+t7Thnwrh6UiTEMFWaeOnjDTPAtnekzCrz37BVTc5WYwT4ICAguBQyOLAGtpwENdAb1VD2DYOFpVnTNILEniaAbBmtFcqZ4l9tpKdHvpCosk8y6iUigyHYXiUpzP0csScHAG02tFIHkUnCQnaWclo83gKUIa0S8n/2YsmTmULGDyRQTdCgcEkno4cSmiWSGf0brSUXrflgQdSB4DvqTgF2qRqTXphlZ3hE2vKpi+1LqR5hYgMMwfQSGR14FxkWoLLuMRQHWWuq20OYACCjtxOCCozTtRRzxDkKwiz2rpw1c0EWTWhdQKkjxxeBtZjg6PVEaUJH+Fv7TiSh1OJoBMoNL2qzoKqBHoRh3y2kp1aF4kGiGwjOJ+TAjCIQFJkwsqYCdqGp2h/UAxO5uBIMKq0ysmOXgOe0eLLDIN4ZqKJU9kc+JrRiBJ0IFNCp/3TeSP0CgIv6BwEkJ+EXooX6TNtwateNZd6MBHyzeyqsl16fFWg96lBEJonMBy0C0MkQprnMKXcZZmikrdEanpDSUbV94zOOHKM3g4nQRreizBrnluVCOV8BqITYWagQ8IisdHxMLApFTLCVSyY6jAtjfFEJqAXdFrkV2US94wlmz+wCjBYJFSg6oCbqBKbEqxN6S2VElGDXmzOcyoNtWDoUXjalYcMrTRx1fTK1nWlk6oQST1CfLQ0nSToUHFJ51JiDB3AXiposS6jymAOSgn1SqV0Bhqq4Ltspk7qsLF9AvhRPh3DEjry5nGxUzTBK0fb++3WTXpthInAQCm9WJ7eqKKqA9BEGumtbNxrOoKbDB3uldDmRY0RA03P6ThflV/QUzVbO54O3aumEveqQ0bp/UM+QdMRGRXYUIGUCpUE2hRsHHy8ml4DecYTMU6bzShCgx1A916amXeeHR51GwePYf/02sNy1KuOd+FGnP3wiI51kywC5JFW8AQY5cjpsEghaJkWmIuOtaE9SIj3QDtgqMbRS63BSDhtwTvtrYfWi/2t4ckYb44nW7cG5beOyuEIBE9LBQCXe6PpHa/h/2HuXZbkOLYtMese3pF0ZTLpA9R3cLsqy9+PawajuXu4H74fB3wdTsoiMyILBWRVFjOz8ODgzGQmyST9hPQ50kif0JrqH7RWZD0SIAkWSBx2n75NAokiwtPDfe+13Pdeq1GsGbwmgNGIlkmzqC+DxIj0RzNGVvUK+uK1Ug76JIkWZ9ur9Y7KejerCP++sQm6cXTb//Hp/hj+dD1Tw2D8vJ9cAWXmHZ00LVUSu9p17DhKdFSSDhORePgvmVoUVf/BRqPWsiP/62o9kFd/yEj2ErF/359LYB2qsR8np0tJ8TJpKbZAZCBMpqdTB1IOFhkDHooIYCa39iIB6FjMjCkk/o8eaef3j0Jb3u0szY2RnXGR+qJ8JwVk11OGpVPY24rdIYhMiTZPFg8VyQOJJklLikwhskOZgF8bxn43T38AlnG6Gc/YaXuqZoNXwZlwe8+bNCg2PVQA61NXrdQd5iZ1pWAjA1XSKddJpAltqEcEbBmoV22RjpM4FKG9mm9nuxfnF3t7zO140V/i0ads+8UuOzthp6hGnLKaspiCxObk4rsf5Xm1n36wF3V+dHV59i+X/cX4aL05PzsSNBysWAc28QQ/0og4INrVZvTUwOO1Fh1wm2JbAnaKpiBrywHhmW1q90PjNczsx2uMaLG+vjGpoNzVuNmsN0c0kuXZXNSMpRP342EIUA/lzcGcI0KNt4Q9LCBXjk1oWBaRiwix4f45q3432UVNzrV7L8+bBkwak959MLl+yJRAnLDqNeBejkZNOpKd55FvYpdXowM0vUYsXjh4Fjetrc4xbfqfPZNzv9uNe62wA045WyPu4V0MsyeY1J/WlxNLPZcBQag/frqdCSwIs8C3nQc5WjnMsUyn+BQzptYWQwtThBAwsAa84HWKhDMA+kFN+vVORwRBQFhwaUoYRkptHBCgh4/wYFRmcMMwLI2SoxjUQoBpLvdGtbayBRuvV1ePjYKopVOj2NOkBkhBZ7rnOsTWIgEQscxKxht0SOTuoKx/HyZn/fHFPjz3J4sjSiwKFsNk1p3kQOcZcJdKQRHsh6zwhqRRDZlANnBvj8e6wCN5QJWi7/nvZhzONzSxutqsh9n48mrcnE/uKJv19WRLeyB81r/Yznr10zQrL6dGc4UUg6HY5Eph9XTAv2gqRrlykEA6WclJ9hFkEHkJkQqvDIyKPUUIqvdbcrOdLa9XK4rvvro5clqcXCHhHSG8AiyAK1dned+oeTGtMXcyRd7/RcCzqZ/PJ0yfTNJi06uudIGaVeJ+yW9ne7uo26Z57Ha81fXT8xNpaE5ysn1+dgJOaCZvH0wdIHCqk8RCZZNpNU1mFRBo2Y9WPf0XveEdqPN0Oph8iQGuW7LuoP5tO3t1G2lenJ+82i2mtdFYBwGygGiBLyaykg0Qt2JKeI4XpWetRMl4QOT9rhSVx85YM7zVPKi32vpZPwx3e/fGBAsv8Hq2iMtROLFcjEu/8EoMzpi91iA7DpOgm0nlOSnNkiRmr/E98QtF1xwrwTwZSkg00EsSjJg+mwf6kHtvZqx/gJXz9clqveinFzjuBTGx75TwVLPLxbJWjN0KBQAJARp8kpr8LJYO2rOHoRqnCtBXaLQPPziQ3I6L6814dT3vh+3sbDasr+ercbE6XzybNsPZ1e5k/4cI3VerU8BPLRTX+43jiOStm6CYesqF4h2KtX9ANF6D/mAzFMv2YcEKb1ODoqGlSlhUAEvuD44iTKP4YDF/pHkigrRyJJRBfPZITY1+BJopE9OMRY2lpTEQg5VusYUcD420MKlx1ngmarD2xB8cUXx9RNroI9asIY8kFtwAwFm2HBVdJA2DWmBbMciznCwjkWYiCwOcsREDah0LR+5HdNlfzTCAm9S+Op+tLrbH/WW/eoVFyQ7wLV0Bb4+tgN/o5s7SdNCqYBAGETFB9zqW+SGPl+ARQRuNUgoVTdngSYuGLHh4fLBCtqsbEYH7h+3LDiavHMXORrBKTCYPDRF3mufZU2B/tPWRlycWabWWRtKrGlYIYGtibRgWwsFzdv3i2VW/e/K2YzoemshfOS0EqeYFCnISJRAxmx2NH/CNwCJplTPFAp6jqEIZK9q2gewmBpwu2JR+z0h+6cDQSsU+yMQGDMuDgQyKDfzasPd4pmytq3QN6liDQvQdBHukPRZKRgRXDxnHfxbe/W7j+ZOp9/sa3D+Gfb/P0f0DCPg+sd35Usyerfrn56/29UNrxJXdOBmggsSAzN1HAWftYiQ3Bm1bzKmA7Mab+CePXJdNo/gr4jIgiWOjK3AE7Xa0pyp5BWLRHQg0MlZDzLMOX6J0qQReLL7r4G7uNWRYLOfRyeVioGGF7u1C3A0JzClWxxJwMANgDwojWESpNgmpYqyIAoAKjUKyViBGdrYBE6vIkpwgzRtDmrDBur98dXNscXl94B2LPK2ACYUVYdKTz9bQlLS4TrMGTONXNLpnw6+TgnfNoN6KihaS/fce4Sqx7A2czPsu/fajCecmy4FQOoq28qiYUgv0YQGBpyhkBD2aPAZKTdrRb9tnKmVLlp5lXsS59IAnbS9mytwYc4I2t2CyICPDpkjgL4XVjcmTNpSWGvYCmxnwWrFzPMU6eamLBGjxBh7wMMDGvS4FqMH5aprL6oMVPG8xtSka6mLYSTV2IABfROr+NGQfZwm6kgEGsFORVads7g6g/wMeuuwXI7bss+mqsHLigMEFRVoMIzfrXpJhh7ahGDcQRsPCzjzFt+zcMJ4iUZh3B0z9Ls+dOKKaQBbiCr4TDcmpfKO0Au/FysKHiq6ndOTW7GEGr0ZWNZUCCIDICesrO/eQ93n31FtixmXEMmFkbZqxVwCmUgDqYqVkgUH8YOks4IJiJVRmaRAhFDcTb25TPFCrecBjXzzpd1t6+BC3FIlwgSew+C96YbGCI/WcCxB7SZVS10AqWXmmT5mpVl4BvKjBnFN+M2zgudvjQ755/WLSsVYgOFiYXbJKdEXQ2x30u2EisaooKIsg4RLrYRRrHAvBEZC0lNFQD1G9+Zh7SfkbHLbHSPt60xtkcPK875dzF9Vc9dS7BdPWeoEIOXqQCTBTBRaJ0OO1izHqI4nJ7QJIdxUIP44M1DXqvyNJgouzbwhTTw1Q27LlHX/G+IDysOZYU/SzMe6LQm8EtlcD/m9iS0jBgGVYzxbvEryWLldUKFdddh6bOldBAiyEnVoFEe6rBe2ukeeUqr75mDexMiZktxl71uWe8keut3uPRQfGxDI90TDNk0uK800RhrN5IIRAvVrRLIIgdh3PHgCbAaISVsOBpe/NU385K9yabdkxxCXgurT94OYLK4b5/D5ThQL2zDeL0Ag2imREmpiQoTLQmg7Os8yQpf3GylQtQmiRNCsF7arlZ0vuVxIUd/Twze58BZo/AsIsDLCYNHMzGPpN3A1HiFIdaKLk2ZZDLkxNAKJZFnrYxIobhNFMKZ2qMVf4kwy01nkeDirQwAcO5/zs8vrqlOcx29loEFcWbhlGp+Z0llxODmT78QDAgiyp6fhRUSYGyZP3jxT7YcrEABDckdAAMLBjMt4sUnhomC7B4omHjefWyuu1cfVaDoM0vVxgYEuewNn710bV24RPgGVoHxpiVJGlmAYDzZTUAaoGgpVB4Y8zaUnVVFQTiY629vXA+Eskb1js9ddTo9EsNp9yEXtB0iwspoS/2YcAFOhLA4jI9M2NFBtMbO/yFtuJMnhvPObGc348nijj3uct5zAVoTQ8ILCBg/243oGwYswgEzZwC0qAgeZtmTylTeWhMbC7OtAj/qUn+Pf7hFeXw/newXOGPb3abp5PD3suT3jUefXkiv4lJjmwMEt3F60kghbWTs5JdmRGFTPnKWOoDb3MkKkDBgWIjYWT5EG38W5cjbf2B4drBs/af3xEtuyT4FVCq63gLwO2RHiw9KOILgOtA3hKT8npmi0RJ7AyS6IAgXW4Px+73o5vqcp6AJ85wq4kXlMgA7RzBwfFgCq2sKauS5KI1YjYEfGssXHK0f83UypDEfXF+J6GMlWrgRXgtbpKE9RGL/aEBOoLkGijFUWmXUSp2MUgVYGimLlDtGHRsAY8leE9jkUddSID5SsL5ouo5jxt2WPw7MXtRMDbj0g/loqSrPpVCkEvC8r3gA0g0dwnsefPbs/P6afH2469e94HAPVHlQJF4NJWujqZItIEGpjBaFODtSZ7mqJUkSmErogZBAgbyGXmydzBiR8GL2f9YjsnHJlaOq6uJttg9lHssPCG837yRr9YX66no8zWTLIaxBqUqwPmNRnpVHRsGJS5IJN0QlAK2wtHz+UgiJKcYNkg/WbvF+He/v3Gomq2vt7NN/355c3R5uQMP4y7cbE7uXrJS9EPFk8QBAGp6WbaRBXMmzQ8sIpHCexsQa5K2G+gGkjqKuPlAsE02k4jYVA2NJg3nj7bba63u6vz1Xr3xnnHc3uyu5rd/NTPi6TwTYF/TaSELi1BgNU0CyCciAZ4DmQj0gSWdqUO/AQwnXYGycgG/nfQ0PPixYvZHeaebmc2J0egFmAzoE902cBkdhapRiqJDJQKZhEPt81p26mK6Wb5dswiUqUIvArJ9LW//RfPsu5/xyNcgM4csIwc8Vei5UxoISeaReJ/TVATsWT6FbLCEYmwKUAx2nQG/Hz87cddra7P2IF53Z+MU5JR1iJsYUqwgkwS7K7GRDLp4nXxazo6C9BfSWXks8KztUSpVSBTKoe/4yPXFxfjZnHjAqiRDYTQUSavq3LsMQUeAr7xFOagN3wGsqxsHDNFG/LEgBHprMHz8js9eXV++WzvagIimvFlefVpFF9lLZ4dvpRWialzQD6s+XVCgDoifqVCmaCYJEIYy/V/4bG34WH35MR+0pt+85edHcy33enTV5td/Kw9cV/vPv7k66g+Ozs9/WF9+dfxp0+fpmkwCJYeTB9ftfA2jp5ggJ4Gq7dqDUYHzsFmpAjEl1h5Z5Km4RPiqCvxQAnoFwczXPzV95/98OTqm7+8/P6b8Pxv28/iZ3/7bjV//J1YvVy+fDr/65O2bQhGX03Ey4Lm0X2sAkx0TmA1TK8EsZkFv4inXfCWMgGFx2AabCzRGBi8QFeXf2n1HQzmx08+/uYvX3zpXtonYXPx5cWHXzsxv/xWuvXpeHkcXn6//ubrcZua/XKaGR1jixrENoISaCOmMAd2lFVpRdmGnQ0yz3pIrwNvrmMGO2og5B5A40Dv634wIGS33jrT6ujP8NFtFwFxBbeDDiR9iNya+uWyIq4RO3QWGT3SaVYl17g9kPB5nsu7U+Ok8F6X7N9YlAclr6f07rnc0iv2lD+DD7bbk2ePXv8UUXC2+Fudb764eGHxVj6dfTf74mR4JE4220fp8/D4y+XVNyqop+vvu9X82Tfz1We77z79bv3DpZnP1dnJxaNxdXqB4H3K81cEeQX+HwRtkOLUAIykD5DYaS1bR6FkabkAeV4BlueRmgDtG74m9bPTe/gyn/zthxdt+0k51Z/90pdZf/vx5tuXL+fx9KPy5PuPi3262LWzv3z35NsvXvxZX2a83ozP+pPFqr98diJFPFn0293pdjpynBrpgN4Ssva+JhgPK7QOoR+ZKmBsylNQE4EiulYKIhRiRFYCWBSxDUm++9UH31xYTg0R55dnJ/tVyhR/Il8e/O50sV6tN6dGvZRuuJruZydjh2IMW0IBLjAVml0v1YngPRBNaLXrChXZAEQA8opHpgbHrLUgBUvx4DHx9vgamR8Dun+5ytwMo0ugi01zenkezco9BBEAeu86HUENWa0ZeBpVMiXSgbodgDmLqSNov/7VYWzGRX+1WzzpCbnUBHtOTZie6QEuaFHURZrGAdZ4TIL2Gd/bNYDr1lLHlkpgcExIp4GyTBOW64Z2sr/+1e+feYc0d5+nhYo//LC5WnxR6uZ5ry43Jj+O9z97iuA1wZBkrMMrB/YPTWggSzALQ908JNCI5OlYkNx0A+AlC2qNBpXgqS66DvjkvYxqu3u1GrenpytGEd5LOfoZYKXkAr6vQZRDZr0KhcMwgVbb1kUKHRevAFaASTnMyjN8j7j/+qB4mNKfbfqLwwvp+fXlwFNn0qL1SWUtRWJt26frs7O9naGiFclCqKWXcQ9xaDQSJ61DILHqE08T0mSJA9Lgkfi7LkT2NragKUmr8Q0SO0ZtqVn9rjH99XzxZDZdUwnb6/lchrkT0xSBHrGbxYKrVGS1wC5r1mrR/xCDwkssgYmoqwlvLPKUMapM0w5eVanu9w8Hc2PM4Eax0AO+/f6YF5gUQMh1Fck3WApsVBoEAcqC2bIgxLJXmP7b9KQqQeVaQLfo24098bsHk7Gu/guan8fdJ5yeIfbDsAQ/n7vpwjcBZRRusDpZ2ZgOUT5owHKEhFaplk25Uqp3mxQ7xOvGQwAD8IxwA4r5+8ZyPmAHbu5maPS8ojEId/1+huh1FX2zFCgBEXC2ysm4MlT2QCfB/4GCIg/h91XhxVpfsuy8UY2lFH9kVPVyt3l1tT5nPdl/wWPDu/SjWgar52JYTEsdwVFQ5Ze0P+qEpS6aZ/ypjW12hR4tWNw8gs88vDBS8H4MmyOxMzT+zpFdXK3IPU7GPgwi9P3cuokUCOB/G5WmMprywBAIzPSSdyxTD0oZO32oY7VSKYX9oAC/Q+W5NxaY+QPDuVtZTril7MFKxMJNby+Gjq5WHuwogQrQRtQbz1gOKARaK1RFmsv0xkPAFkJizRdFm3vpCsLGuw5qvDz95jFnB0FJA0jIYQxTxRuejtQOBNR8AIJAtqVUqUO4AvQ3gEXAXjQXF0WC4gfXIby3aDXAUmCh2rsO5Mv16tXyfLXaTitnvuxHMQ5sq9gf37P9ouHpWCqUeNXehRRA5REbOyoEIctn8H2neSORk+BAJR3ma8qp/KHBLJWaS/pJLf1Uymt4KQGq1mkwV4udhDhsncfzQCHwEigUBDbHQg+Wm7JCdKopcPipdtgg/7sG44el92qIwyLsZ6ZEgGHaA0c68kWw9YDvrBAYWXBh6UmksMuVzzkaICKqT1B0LzQP8PKug9le8VpnX16rXp7OF0MUCzmY4CakdvLmB0dkrh3L1y0SV8fqrZR8pliv1gjYiZfislrsQefxXXj31QE98pY8e5HeeUHfDHDf2IgROmPmC+PnmnrjHOGbHxxRiEUjOkaLIOl1TNjwih6QTjWFRccbyY5JRfP6JTg2wFFlQWC1Z2y+3znC5wiZa47QLHr26vtoBz2N8M0PjlzkHTfXe60g4DSaqcjIVHLHbySFUlgMhf9AORZKFvZrdqBEsioEhtfn8NX6enc937P0m1+fT46767PT8fl4ufugX+0ePd2uL//l2fjqUfrop/7xq/TFaftYPV79+E34yjz+un746V/K+erF6d/iqZQ/Lr4LRyo1D0QQM72IhbSC5iPg82yg8p7mtQiulcc4GUFLAkk01jpjBdCk9KDG8qfZxbrfsRBusVfXYUEkP1piVkfLwldenoFJnNx9ui+EQhBU1YtSuhJ5guYbdgq2h48IoSlrzhP5Iuh+1zmp2OdQKkCMrAi683/9p/+69Isn49fkPFf9ZrxcvPqn//bLEWB7e/58/LJ/tVpjWJ+eb3cf/zf/9E9fbkbW164H/V/9u//+f/p//q9/99qHBh/+z29+aPHh/8IPP/2n/66skSvXq8e7fr4a1b6kTwuFn/g//4//+9/N//Wffz6Yf37AYDz+gv/1zecGfPi/vflhxIf/Oz/8T//vv/+nf/3o8up694t//c9HqP5H/Bf/fHX7w8dXNz/9z//ff/r3+7j/5x5+vu1p77lssJ/teKQ02z2/OZzfdxBQbajfbscdAOy47K9XuxPa8bAfZXZxZY4oq4Bllk3Gjmgdr0kkldFUptAw0mti975Fpi2K9pFK0LcOP4J8QrOr++fPt8fioN9hX/P8XE3lDXIpl/0x/jm66a4AORD0SuSp5sYBTyB2GcCFBvLFzqYoKYJcOl7ri9i5nOjC4Qp4LX64s+/41GXY31B04Cg54x8aPDxGH5plE7MChsfDFGJ7yPSMJ2TxiPiApJlKjEjoxGAPfaoxYnqe4PktSKIyrCrPmSKOnuuk84jZbP4QwdhCEY4AssnbsUaPa+Qjp92BytVvPs9Pz8u07EJyKkbYEIok1Si6UtGNFsUBbw3r2WtLMRMEada6BhBrNt5UfXD39hvPQw7av8WIFdJYxY3Q5kWH/DjZzXjFdgDePnXTiawEOgTu4DE+iJHHbzWgUHvg2lFe7V9fh/jdYXk0g9eXWcQKrBWRDQPSS/IxGhBVyj4CV6WmE4E5tmWkowRefX3o425K8zFHulpG4CIpPaaAf7F4qk1VVvyQsdREbyXRoQlLCACBUlrasI8tHXgJ96/3ASEAPHuFWHN+sZeKOVmdPxtnrJlfXx5h0gxeiivaANGCDknsSV51ONZhTE1YFhlU0awGeICgKfBaPmJXxnwgWdWzrPJedmy4uHpDlG1ft2YySxbAk6mqQykPFRyXLSY5K0x0oMyYlVppXak5IJHmo8fu0KI4Uw7mdPhZ2cCyf86yphn+8UE/PAISfKms+Jd+OJ2vX54+kvgVfgz/3j5Zv8AP4BfnP42P8DP8uaOcQSMAe3iTqZylnTN1YIwQXVbdpMxdVEf9LIvXhPAROmBZy9uTYg+dFvun/cs37d/4GSZ+vj15+uP1uHl1ImdSzNTN7+5u80IGA4wUKI5aJeq7OFVCwQ7ONHwKokudRfpmMxASUDAsZ09aU32+HL6NBw5BzsQbQ/CIeMCoAAauIVgKsC2kDJcz4CnSQGGw5H2NMVFygAgjwdL1CqC/xYO++IcPQb4xBCxwBBBqA0UAeuC/2rA8m3OS2thamowHVWxrZL5QcwVl1+BjrOhsSGvqdwxBvzGEBqgOOuNSpO4OVmwGrfMUTXR0uQF8T5b15MVVyW4dwVAeqKFiAOjbu78INTNvrgW2cuhWaUvNTqCsuCGRkzsN8sDDJyTKAoYOGsGSROep+hZ1bdaJeFAc9aAhqNnPRyBlRrLIiGxdlzQvgTWwdFEO06543ZCxK7DLJN5CpbgfGDr9UaytFBl+x6WgMYI3V4JPvHEOXdFUvPaIO21SP3OUkERMaPhDXmvLmCMJZao+iSwMggZpqHzXEeifjUDwngHftEyH9sA/kwWeUREIgRrFnqW8odLnBlGUpdRKNPqCC6wga+q7jsD8bAQ5ZWzuzgAbAKmzrApLvdP0oKmsXM5GS2YIGqng4VSDU6XrEJdaF8SB8u4DR2B/vh+TpyiyUtYKX9gg3LA/RHAKmIrnaQ5xCSkQ6YINk65jGwbpBA8lrHnH/ahn7udBCcGGBuzUxpMFeywa1lUEtu6CUlka3CFZAA+UqApNY9ipWrBeZTL5wW/hpoAG+9HNlLv97TSAFoAqAs1IeUKtQ7AUnnGKAivFAnR4hickAi04VvBoAFZdhDMu6fTQzXg3gH/Msy+uFufD4dU0uO4Vcuj59sm4+bezcVdW52C9Hw0fHNBdZ1f1w2/q+U8fbT/freafr49X6hMZhq/x1i+3/WdHkQ1WsRVgVxbEsGKYN5RAhYk9b9g9DtkSOVRQEBgADoS+YPeyACVIWQ/Htzpfvnq9AmdxdbKe7672F/Ssobd470gQvPEDVse2tM5N5ZiuJp80snQCmCDU95FVkgUhtAvZHDRKs35petjuethPxJGXwDRKFQucYwTwOphJrpF3nh6Tbrn4QI4MW+HBW5ygFiUyMzteMcnda3/3djybek3P19MMHyWgCNvRP9ix0znFYqJtomHM08GYtFHQSApr2gsHZuKolslqg5bxl/vDv3x7+P5OT7aLLfDe4vjmeOWU1QbPHp1xFKv5bLycpc9+UH791cfrbz6afXFy8ehqdQ3cN55sdo+eAn8+kicAQicj/8Fr6Q+vvliv47D9/sVP6kycff3Z8FWrn/T+h/W3z3dfLc3ZyWJ+89eD547DqfhgNT7CKI40m37rpGMSSvRkr45GQ5g+6hDmnD2FyAD7gmVvIK/dBLK6tFSoPih17H/C4jbKzZ6vZxfbcXE5TOhu35aJb3vSn8/2p49TaypQiI1YGUDoHumoVEdxdmHxR9TwoUFQBkPwoAWxYJaFF4E8xd4vvPlsuzt+0u/Gy/62YoNlUNSOOJ1f73b72ggsWMQSNmfVSJUGOlzwABS8plM2Na/w7bESjXFZBjoKGNoJNIRPDO6gOmke1exV/2TNqsrZ06upKPh0M+76DbYgG22mVgujkFI1ForGlxLNeYO1Aw5pNBWISCQDJhvLKEoZMlvNsNhVVQK8Vt9TuXm/m82nhmdWlfVTRNEWKaxiHQqsN2yfmFQ0rNzyQLiYRvabR4uwivfF84cO0AfIWFLwHVvwvpZgvlttbtvsN+vrsyf7Sv/L80mYcoXV/8H2+upq9er0fHj03d+uVf7+uTwSErQacLawWr7VyWuTRVAFZIpFnIatU5hjU4FjwCwrm3WCAP0piV5Ld49fzIbz7Y/X29uOrsvx5e6mr52VVnht++PGmZn3ZgB/6cPC0s57Lnq9iAu8TKcGPZ9KdYMPYHBAM1EBYVjF6+LQYQp4tU/1L5oPUDiZFrzZOw3wE9hJRHHYauNDRrVaX1+ejbejsstemj7SXl0Hu5BDGBFgBrmQRi+CnW5mMiBwY6eyqjTHkyJJAEyDzAaoZ02WxgEWUu2wQzipERRJ0KQwSrb/xPLuowJsGYJfLsMQHWZNDtYu4jAf5aJfhrmeDpGAwxVeiJf0qPA1C0WTKAXImmunlAM4N5KbnFJsGllIIp4aS58pHrM86A3ub/NvB+eGsJwPMjqgDlCjhbDzOJfLZa+1x2aP+/u+JGgKTHPywsI1vEBDBQsBUBybxUpWBtmRRSpUDcAQs+jA74QCZjEH8jEPHlZYuHF0y36h3Ai2MkTvFtMFspoPczuKG6WRDhSqemRETE+gvzjmIQYnow82uwQOxfJKFitUjAkRVDmQQHCrSuHotw9rs68qvBxu2P3rK38c40LruTI99rlXvY14t/0wN245n/d66pzXXkuvCviD7CLngiKGkhr1iC+ZfMawdiOn1qmQOtllzZsex14IjNu82/huJvCNj2fssFI9AihGJjGr2lMXWM7n2BVmPrfTTLapej6WyKs3Z0BSS+XFlmgFwNvTZ81i4PgoTdcsuoEW2UB8ypx+n0gX4rZaf3EymYSfvLg6Pr9crAAItlNH72LVb7fsfnxC1d17ZcSpyD2xha/yhJ1dWuDjrLepNF1oclJFVnjVAQkwKN0QSatxwslcD8p+f3sEU93zfLVePDsGONz0wMbT3N2NBSgLi7hYdqiZZFrWGnQtZ5BUtlVFPxlVig6klCQpJB4q0F4TYSIfClS+fSxP92sKgOZ4MyKW32kbGyNMQYYrNP0pvFxLVtPNGUkd0J8e3vScCI1O2OComojGIahSkfig2v03H3/DDPb/Or44P9sgV98OIwJ8GlBCPKPymAjUoHTsQils0EOeD+yv9pVK7CaknCuYOjAHvUmtPhDMfrdh3FETazPVRyjGC/hDJXLPlaF4/Eg4BKzB4wlsqCK7WrGAeNBPRoth+O7hj59O2afWjMvda7+5sVxYjWf94tXd6sheSXrgsYwZW8aLKDptJz8rMAMaCvBIcdI/oFJftiEGeiFja/MA6h1Wx2sDw58d/v5uOJ5aSoqy5QrjEdTXlio51zpr8ZIS+86KssAhvrXS6QJkxxfX0YQOMOcXhgPkNO6u+sWzEyln4eQUY7rRfrp6sgZou/nX3XqlTDu+WgMxQHISDiAf8B44tFOAiIbqIViw2EYAXTQlBeoSOlOyIYB3/NKbemME3LU3H0zfGTEHnEVY7aXKFk+kpHhihXFXEPQD3ktgWRdrF6mdj/Viqu+QnlLp8oEHwa8+8b7t+XLXL3bHvIOZOsapgbSPtFMNZ8xALgaolYqb4M9u0jUy2BaJ4vLs9lNdTBR40+DNwGY+TJrggK/A1vcD4SHvxdXVZv2U4jrUndnsnp8IqTT+PxLaVFNz8lyQzB3z4gex6+zJbr5+eSxmcn+uxRK7SSG3BMStVlKoggwAaRI5QEWJbI2Q7g14bgS57ZJDKLUVUNcJ+zvHMtGVze0g2J0dCxBSygYvvQI/FXoFlqBD1iXiwSz8sgVLBeuT1XkptA4JOtPqOv7xQVwQRuHrS8HudAf6WTuge4WNGblHPf22KDSEEO6QbawCwcCEFDDHDLqIxWleG8VeTm/V79WAWDJ625t/st6dnl33m+GUn+5vijwSklXVUIGiIDkYrIWYecAvJZ15C/0nqVfJMx/XNA9WE6ASe6ywIx/65Kv1i3EDljh/df/sKgJzFKIwQpNuEsFZIzeAVk+KSng8DYkaf1ewGIHihO4yFYBBC7WN7/js/OoU3//u6TwgqSxfxqtPtP1IbELAKkcYT05E3kA0kn5N7UOEQ8PKXYFdkLPu5Bvf/OV4ud1T8e2VvOnMZz6mIDKhJdVFWseLfkd/PFYEUDNYUu5NKYHdzwBAvatSKIefD/pr+YAVmNQCzBSh7bAfaX0sPvro6qvVS/3VNysET/Pl5xv79V/T46X7zN9ZMDlu70pyqrGIPR1nqb1ia2y+MPj7qVrCaoENj6DM5kN6ZyChOifEAwfy+RsDefr9k/btD5+/yi/V1Y9fpluVe8Q7ITx5JUAZAIphjzk2VESMTbwKxcNdpcVoYgcfS74w31nxVDHo13f9xdn5cFiuvX/taVg8WU/tDXzPYLi6o8F80Q6wxyGnSA1ggm3kBPvBvKweS4xdaZIelogtXaLrBZIjeMxbH8jfT/84ve6npyVJBzTXPIIJywkV+7Y7GobUrFwSlMcklleKWY3eXF4izYYKUCj1gVYAn7a+HNmM26/2C2t4tj354nJ8PH30JZ7/uPukPp7ahJ8/khb/zxzpUjSoDfUJa5COdf/ghNReBmHnGphk7EAWM+tngQSE49WSZB0Pnawf/Px9bSo7t7FskZ0zkQQbcKXl9V1jvCps80ViKTFLfHcw6cpb2KxbigBFHd7uOz7vg+fj5tGEPd7vk28P6m7JCmDNbiS6nJjKa7/Zt0nfsKvtbMEudLUwi7lahogFJCZ1vNnZT0edo3gmlVUc1S8y/Yk7z05m0RS4Kl1pkPQkEn1ltySoS0OAL6wJKtgFr4/wyfqKZ6LTzJzsNuMzhLvZJEe2F69bKq2MGsV8Me/dPIQxmoUC1KUZnZqDivrQzy0Gd3ud0DwVsADbO2AbRDqqYXkE/SYAR7PDsuVhjwadaKogFU9F15q9i7S5978+uv2vJz0GHp9djJv+8uz24+O7T46psC/u7xaodpEqoKAOiFhYv9XGUsHXlMDXmOpduV2RrCvoKYCAo0yrnuwYD2ri3jqcSV12fb1ji+xtdQr7TNnOsd3xmNTNzcLHsR+8MBQymas47Mk7CLmKnTS+IDY1VVk6oikyh/mQTiOiYP5t0qy0jMBV2GeZMoYJBMQdnG7/wQGCCwcr53Iu1FLOF/1oVL+vemTEAs1D6KEnGHYCawhMLqxpyQHglpdCsVL6pdOOer1Ks2jX0fwh//7xHX52TEjK0Pj310e9RHgCoNEDdcBsdIMI5kbAzthQI2UBnFE1serZ0ILCIVM1Vq4bh9hZXWdNA13g2XsrlWXS9Nl836P+6HJfvYSt/vfP12kY6Fv36XrfcP/3L/tXDBN//3K93X15vUEe3I5//+v4/Hx88ffHT85p6nmGX6yv8IP8998XS6mX0Y0HMyEoPYS3tlzanps0IhFOF4zUyanB82q9ZXY7KrxnbVSmJ50JHUJ1p4BCMDMN4Yy1vwBQgS3MDYn8Pc3E9fkbby64xYC8GRbDYsAvdW/3xdBeTJKj2WO9R54Kg1SznpZ2akpY8B1JiUwgZ0cTUqs1mK6ikKRoXY7pHzXeuFS9HeO4wCb03o7ItH6/gRX2g0B+B7kAwVW00QECKp4qzC4DklGyDnPeAkmZYLd0IjCiRaSIf2CCDxbV4UAFT+zneghqQRfkOM6nibU5VbA0SeTvVBEZubskzSZzxQuNIIKjhpe1yOO8FLY8NGSlGsuODlpN3tc4e6G86efCz0e82QWojNpPaK6AtpQgBh/DrCLZhowonXl22HQ13lCkLcjQBYAPYA/MKt342IOOWX0vA73bkzfb7nDlYo/FhRDKLPulX3q1kGZ/XkVSLbwGtTU6d957DBFwGHEndVjNWAhIywCjoqMeo+ko9qroP2Qx9/YfPe6hj0v2+Zqe+WcZRj/204TTvS/plnk2IyQ1aDG/AOiUPvad0LZEMCRga2eBYD1ovKOVJFhjAArI+vcP/FbF5zAsGDuAhy6FHxfKLof5Yi+lYhGakItYjkpLn2w7jJB2HxXBm/dGkzoE78HYnqDwDYoBLnKOjoV/ZPXe6te8Hg7mxhq3jPPlsh+kmFPLdj6dhIDHT96ThYX1vMWrXjk9OShqb6IpPCLK+D+rAuJFp6pAYNaBZ1JVu/c9ztEHoK+4lFHPDVatjr3ev3TwBcyO19IoqrwiNSbB+hNBFRwP6IbFiTwQlHSW3SmeEpuKklSSNjAPHCiwLA92bzX+qJt98lxQ6vMX/mR/C2q9JLVtFc9inVzJxXsD9pg7x4vYrCtL/OhyB3BpEMlAMIB5gZwO1crfOq6b39zOHX+7r8jfnq7Ot7vxctwcO7sYxLzXy8UQZC+HpdZLEB4xLiwVCBdiFBH42mrA31FHZ8dF9GoYADTHEfl5igmNbU68cJTRILI64AvwC/ay2M6wlygJrGb8Ef1yvQteA6MqySps5cMDs9kvfJtJo7/nif4tkj9eaDmoZW+iU4Mal1HM8faBFOw44puZJZ4t1Dz6geVXC14W6t6pOA9mrsXQz/eEkEdoIgp3ROojIvdZYoNOKXRCzbyOoLy/wzqPFe+G/qEpsdqaRVUuZPDGQBeE3/fV7r7OyXLsd9eAUcfBY6Qx8oA3IHyppVo4hdg7j0NYquXSLRS+q14uPULLYJQb52JYjmLAD8ihvxHLQJBjgAMM71ptrmNDU6MBnI/Z2NwijXvU1OPcsF3BqnMtvPvPtukH0oRf/zI8uDu9+UbHIzCtjRHfaQTjIlNYzKUUarHwGDvmlbeSvYg9XlrvwyDtGJa8chhHZ7BaJ96DME5tlcLM4vWUM4WXTSO1OzDV6axJYvUBpGTedQU68XaydUX4cKBV+9YvNGHcSdbm+KK/BNLdnJB7IWSczDfrF1ta9K6HcbO/yej2YnBSi9AlSf1EdrXp2HkBWOqtASWzYILFgRJOcpYsSYw0TQLLeGBofMuQtv3lMF+/PBiS5mW8RST32Xi2cLZKNWEaSdrW8OypSpjWrNLwlCdYhD+F1x7BWl194Gv/1SHFX5ilycQXeIc373Ly26XhYKLpZwUl1dTDUCA0Ggio64DhmlGC4vjYfDS6+sND+oVZ8l4ZKl/i1Um6WzeDxYOVlS3IrAJidyFgTkK0+DmQP0mhTpVT80F69dqB6tPtgaLiG9Vp76J9jBiEt4JH5egmxxhaXKbieXuLLSwddckBuRVPP7qIENtNUsMB65u39X9gTA/UP7Z4a/SQo0OTcIKS5cUCwgZ6fdkQp/lJ2AOIJbTAo6hazkFoIHD7+sHDexvg6xrIli0FHfhVBqKLxdCgXtFfTgle2KnctLDMQxStCdXRQBz4StNVHUHwHzbCAx1k6+g3oTrwd4UBuUArZjBUI4wgXOVBCBa+CKA2yLIKo+7ABibvBETEhw7x5uZ3f9h/Ig+rZPef3V0GS3rEgKAimGK7UbcxCl8DRfCRCkKI1B4LDitRNwN2gghseGwL8MRrr3cb0C/WqgrteL3VsXbPFHZSVF+11LxU017XySSj0U8ydYghXaMQMBYXMSboqX/oEK7WV1d7jQk5A8HxJ9cXw+2Ht7MROl5AGsueWFo0KcJEzz5USbPhwAYZkJ3svDMVtDlVJlbT4dPsDywRFog1s7sJv7h9H1PB9O3DKImQTSjVAsvSNFkLSqaG5EOl7YwDkALwoBgBpsdK2nh3iupwlCax4bcfZu4flhXVTxzNmQsPvHh8InmzCmDfuakoKVCixky/ykBsrOGq7ACjRGH+7YfZ+4eBLRinnK/C4nXRPrWClLPpA7AEKDd2FXkQkSwHOtEajTgWq/diclQ+0OH81Ye5+wNRMGttWUlvsWq63CEBg9JJCrPHxAJDbHPNGlgPTu0I2Nibz3tz3vPX+KsPuz7f9xnc7pzj6wnPtxLYppo1IhuNSwtDTaQXK1uAjJIUpAVKTCz/V3gWJS7YO5fZCnFQm7rY3mpGPlnvnvZ70d8j6k3i9YIQYNO3MlXAJgVqBT7FHrDGCjtfgOAMHTQr7zO8dAkLgxD7/rBgmK3OL8fjqbaI5aCX1DQbT+khNJUwL24FYqYG3qnMEV8MgEFivytgQOx26uOVklMOeFfAoJmGhZn0E0tGdHh3SAQgLZ1DXpT3hH/Q2q6ur6+uz7av1KFzzI2i72aYrhzXl8fYf9dX1A86Xzz72ec3r5i3NkdUOicFBfruKOPlWTvfAWEprN8O5I4O2TQZScZVpYuLjsIxirpOOvv73TIsZneyXs/Pt9f0Krkezm8UO9XJxN/YxpCl5w6nXDO+s6GxLdYocqBDWgPL1aDutTVRkeM8gjiTdMC/klH1/ghhuLiandNe6Wq7r8odnp3g99d7cTnHMoeU8WLxl7IDIrMdH0ASsZBK7wXfEdEfABO5jJcKGu/FZuyXkrI/sGQcLych95tKspt8VM43C1poPb46Tpt+fpzXq+F4UH50gw3CRkn1dTXoYHs/7wczN2Lpb+4fK1Bko1eLbQHMQQJCN7p+FvAfZLEWla2YlyLoxh5U6nL0XWSWaKywj79jZMveWc45eCeWeOjnPSYCfFUtB6EHubf/OkrR0vWUjvDYbQSKiBlYEuCjJtMqkgLkVMJGbKEtSY5YGNNHFvEh1Xcd2PrZMfiYNF7LuR0HBca8sM7Jhcd4h4VCoLuZMstu9IrZVJ0EtRKtqFgS/dJTrQFpP01HJREswCDIeoccqxIin0Fi0+H3jGzhRuXJgQczDsK5Od6a5iHOYvTjEPR+ykpD+OfZbUd/uto6fgng/yyt4vlzSMC3lAVhmK4FL70J9qPSf1ZHJx48sOk1+jjO52o+jD2i8GKcjwMlxIMBDV8YUMD9mIx1WOR424lyCZM8kdYecLKjRB8QUedtpFmy5H2rweaSNHupLEgvIDbvNqblKBfLhTRzb2KYy+AXPoBqquXcq0GacPMGRWXBKy2rM+h3ZkF5o8uTYnU1e3VawMJkpzAyCuIka8ArVWk6HoSq/A6DwsszY79cDsvolmbhlhE5M7gx6sEO9KhQ9mZQAXQ8awp0YK4U3RESBkOpHkBdDFBQKVZTgrxrgcfynjIHJuIXLBZ710EtkcF6vLXRaiwjTJHB/5Z4M4bVw/tBHdE/SXQKw0Ew6ih57rqYO0H94iCtCOBNplH3jiRGRAFaUHj3h50AVm4ePKbyarPav0JwsGEOSDRIHhSM45J3tnOJcWJDahNv6yYQj2WLkz4YqC6IiaAiFjZiJk3CttOI4lonRLNgK+azFZMq8oeKTXYPX1f3I/OjNfPFol8aixSE2KXIt4Md7GIwAHf7KRMdz/IDDfi0pPu3RCxQWOGsEKYlOJaUiNgoNHYGDdaUyzcdW+OQ48PDN+HNwPAu3RK5ZAk8Mo7A0yOwZT8Q0C8HvMpRD/uBqZDAhyzW1mQwjalywNa5ePq3I5pm4wRt1hPFhSIPFBz4UrBICsBZvvyOgS0W+C97Nbo4KrsA7MOEIWIEa50Qi/42B+WUsKiAbLCAsLA7NnrznqQVUDo63GlJp+dsLdAesV1HHz+NQSPuG5kePLJufN7vR2b1HGEBy3iJiN8v3WIYxVIs+7mzAwHq7SorTAg6A0RTWdFgEJizNumMsSgHYVYjExq811ZBMStlviMBCULKoRfmb43sL5vx2W12XM6lkWIR7BLB3SEnLVwYnYhI3EHpm5EBbiuESMFjMVUn70Hnu46vEzAoA9WETM33Yq1IRtIiImOPSMFTI+Mfvv5vRsY5GywljkTfKzd3ugfotYsxeAVIobEEb8urWETF5g/L8n28chnpVQ9oI7zLJbiOgrX090o2sDhbsOA/Bo3/IJUDJb+Hj2wRh1EtqC5ng1vIfu7niLB0x0VkCzcjQ9427ALlpUUm0BYKoU0hjEUk7OLITSuCBLK7mgrnhRYy2sqjkKDLO2yAD8f5Zv8y4xzvbQTHC71exsGD56phoRcL0M+g5HgzZRrszCZl2d6dpipAH+jV3QrGwfqOCIKYJN9wbiwWS1a4mmhUCAgWHx7670e26HuhR7OYBxfF6JUbl+ycXPgBO9bfxf+ugqZj7VD/CJErOcJhYB/sTbZiUI7NS/CUzLMV+nUjxBhmjUCJyYfj1puBMTFJswxhDlQ4WDufG7OgPNUQ50Ciwtzh1lJBnjI2pWW5HhKgq5QwqYV630TsPnvDIz36OXYKLxrJAktf0Ikt53eeMoxsCIpaJRbpfBFkz1KFYRmMkL3WcRhuwiyrNCwwMyFzYm9qDp3KnRYG9M0CQbguVAW0qkHtsmFu6oBupQqV94L+oQP7+ny3Go/zCizmGGsLUcNMDoSD1aAOix5IXijlEGXVbTQDh0VwRxpMAIFFSVAc2oPxUj+1xp1B+iMcogXIO9AYh5apkpuR9/ODsf7N0LjMgLyWXmEXuMEvxVywpwswMVrXh7kZbiG1wW5DQK2aDoUJ+SErAK7EAwqPlc/DDJARJHpFdwCru4p45zvkJsKzAyvoJbjexbi+bbS5unP/3PQvVuNmX+5KvayE7BsC/VqFkrR/MJREAQc3BbSeoQ1EQyHge17hA/ogngF+tajBRl2+RzbTl3+zc3mx3X6w7C/OV68efXE1Xv7Hx/0l72o8e+xSIvfnKkhCRGSWjB1I7ftENAG00iTxL1h+FWzV1a1QjO2gwPvBj/w3IwQLU4660qWou05U4DsrLfYGDQ6iFfTrBtuuTLyaRsgJcYU67j7SK4TSAfHgcvLBj/6X4XzL/otH2xf91ZF0pjn65nmN+dassuQFbsT0F3alBV9YaQ4Sin3q+PBKF3P8BzyRz+LBz//rer7erY+ov1NBeiKv9DT+OhZLae5GJCKlC7YhZj12SSuXeBXvm8ZAKB+V8J0f/nb3z/s3sPkjzrW9mW/BYvXKIwvDSi5fEJ+yxIIuib4LLZVO47eShz0RE92w0WxnMlKlbl1++KveP/8/aBpx/QeFLyfOd/3qfPEfKEEu9v+8+0RPn+iDT8z0iTn4xE6f2INP3PSJO/jET5/4g0/C9Ek4+CROn8S7T3zZD/U/Pl718//yx3u3fNmq8S/3VbYV+4iqdd45n4OOFcGyIbYXCoZp8MXUIdzTja0FUUwHmu4CzwLAilQ66HW+ebEHis77utrNyXOpTr5Z8ITlxeYTff7p1+OH149Pl8+/2n32olxZ8cnlZ7384VP/nfjKXr64ZbQ+cZEVS6nUnDvHzqrYAVnxhoEKaay3rrVkxBkjWcHTsV+X55cY3dvHtep365PnSp88dtfxhcmfffP1lx8+cd98+/jF+Zd/+Ur/aAdxe5zkQWdE0xVs2noKMklNJWnvEKwbzcT5B1XLNumFsABTYzqDcrycDvLB43h1O46nL82L78+/27Vy6xuvulZLDPxeNA9lux2V0rDBheJBuOc1dcgWgZ3abo3RNlvkZYkBI7+8dQwXPd7ROZfIVPwsdTxZrr558tcf3e6nHxarr+rHx98OZ8cfXZ/33fbzxUdPvgq728YDFRGCcmKlUtSyOmQxDIvwsgDHdEEC/gFR4U8QIoKneJCYSsYSGwVLe48D+2HyCDJYKCUToSPK1s7y7knQRSuVyWKbYjKlk6ajHawxbOKkokhNbAkr4mdh+fUB8bNxs6H0nbInH3/9zYdPPzqTp+duF56VDz+5QKBV3378xc4ef/V5O7squ42rL473A+uSDHHSMIsVWQnPU3hVmDne5CkPcorkrLIGxkGkBxXCTzW878xOtoMmhfcysGvpxc3AqjQs6wuadmyCglehqwavkfKzvmLLOa/BO7DJQLcbsqmc2vgdBiu7g36Chw3s8ZsDi9+tnrx69fWTEP92exppnPZUIWtAQyUEdtw2vCWADNprAnN2kR5kSQlJR2WeKmW8Vm+qpnDwW0d0iYi9RTZ/enXy3KiT4+bU8unux09/+kh9/GU5+ypfvvAftvWLFx99pFaLy2fHqS03Z19tnn/3/dXQzvQn379Mn20/+Ww+fD6T4pa7ZuRmGmDKDgEqKZ6IsQWSLQs6YLWLSMfIqeWE7Bqxlbp5wAQGNEP/PDv+A4d8ezhnrI2SCpEB+6XjHVaHN5sb0HEAfkmGQquRbaIIwsW0ABhnXFQ8TovNvX1Bvt8h32JqsPzEWjRjeGGP5cn6J0B5vPlEfNmAB5W3SMK+GR+wJljrxHtzF8qhoeQ/fsi3pA7k0Tn2ETaKidFzy2cecLZkUmBRYgew7hMLfdlYXZXGEsLOx+/Z0fRnLozbo2SHmJnpJIyobShwiKlskdtO5NiaLOw1QmKx1KDHHKsmLc8us6IahXh7wnu/Q4732y+ASIGcRoVcCACA+ZauAa0AMVNXLmKxC7Cd2ABxCHC0o6kp45o8sND5hw9ZirvTLu64HEwlgcmI+g0I0vHUKNO/PlGTnWd1tgJYkDVJqoKwp1/QfeBPHLK8tZmzIJg6APTorJSlP6cG6af1B7Ye0CDDWpnaWOmQ1hKvPPBeBEASDVr+xCGr21TiVGZzHu/UBChS076jCnee9NTB4QKIqnKaFzahiw3MXJNURtfohfVnzvJtKrEROZf6v0i6gUpvwlvhWKnkaqD7MtB6inW6ea2lIKvQkZHnQ4jQVv2J2U/ensJYHr9wkdJNhfVelo2GiB4AVLE2rODGOx2BCI0/FHwFmH4eNceA5fJ20PV+h2zvDuiFRHpQpZLJFNuyV0YW/FZSyaqwijVZwHgEaiUsO+wUIHUnOkRDoNw/ccjubvtZARKggXoolQCiA3wxXTBEqULWgb5uIiFEY/c1Ot9YKmKB0QF2+N8Aau93yLfZDxypUcYpUbTUmqiS6pLufE4dAFECIlayUMqF4qu+aIy0w5gxx+DRqf6ZEeM2+ynKwbHupdGZryKvUcooOrBdLOZcebKCNQuaZcF2KpWsC8JJBaA2CBr1TxzybfazKmWfsBSCshgic1+HlRsj2E1kS2pRHvxGA9V5fCUWo2XKhSQnOzodv+OQr35hyF+N5+JHYMXLp8dX36fPP39xtfyx7OJV/8p9/1Eetunjzz968m198dVB1ACoRJyoWB6187RZNFkhDjfQ3A68We0NdHMCB/Es/ZZiEubtWEFDyYI/edi3kYPq06VzIlOtBAjZNFeRSHiGWvEagpY6FY2804yq3H8hIctr2tOAX/n0Jw/7NnroZiJRvk6d4ZGKUz5QBkP7gq9CVfjorefRqcarEFHzSNpkkawHOzZ/9rDvLkVCmA7GjaB7hwiUBUbqSLZLmceL2dA2sdH/jt6FWNI6ZJCq1vkACPWukP+PDvs2ihS898xWYcoNWJaohGiZBKOLWCyegoIcq2Lflmf3hlRTeRJWPY+I/+Rh30aSqDtNRWrgi8h6/64mZHHH2nbBSqmSEAa1zdyNnnoXlC/B2sGOFKz7e+uwaSfMYZ881+bkYrx4/Lf+L1up3GfnP1zldPzN8+/m36vn315+n+dfzJ+qL7794dUXXzzemOHbj7/75mx7dixfmcvpyAIs1IMsAbBpCyQRQ+6oCKxdpKkr69tAV0U0QKRIM4JndZ6XnhlrPcr49rX8R4f54d0wKfaBmFvp+k1JEKCdIFkiLYLXJQFJSyHZdQHmbRGtc8RCyZo1cwqQ9e1I7s1hPv+tYX79+PjiWjwu1n50e8IqvAqeTbKCmgrY8cKlDAqaMWYvW+cUXbOqp8AlhWapcSlDpZMOK0ffOj6WJ9OR9rkSJ1cvz/Orq/Dsefj4w7P27ebT8pP/Vm6nSYqmIgbhdaZUs2a9I8seA6X9NY3wLI+/MJFOgXJ0rsPDI4Ev5QgRyLo/NIgf5MtVG9THX9VntxvXes3m/py5WmLioRJeWqIqHpJRQcC3RtFtwbWAqas01AW3oIRPbd3bk9Jbh1Ofup8PJ9PpqNrskCZNJ2zNJbPIJrnUpuYch2wvsXBYu0nVSsVeIorLWJ4Qv32lv3U4f/k6/nw4WlAdurE2GIEXsI0FOIGXrpNGg9LGSKRqqrLS0a9kIA/wk0blOkTyt0fjN4ZTD4fz8cfLcXHZPvzLl7dH4nqq1oo0A0IUSixlsa1WFnWRGTVkA/YPGZMQuyTXb0LMlYmFSuxWf+tQNtPFzslzGU4+aV+syuWP9TqqtpGfXdRv4jLnhUmffelWt8fgk/qCMaQzraMTlqTqTMarQsYyvC1PPIpXlaaudBizSLEqWpGljp18+yJ+fTAXB4O5NhcvP/nk62/kJ2e34IRXrZ1LRntA1IL1YqiyCko+VdJIIEWsFux4LTEvXWFRcBJdmUxmkf8fNBAtHjYrBUTFN0vbO0EnUMAlLYFBUqjs1gDOMOwOCOBTCNTVUXe5K1WydFwY+3aO9ZbBPP7G/nwwhtr6QhrRrKB6I4KyBYikIHEBdlOUL8xC69KI6iUoqnFFEi85QOr69mD8lsF8983qFwaTtaJCdmL9mGE1s0EIBjjnIbZrpfnUQg5YrTRYKYEvEJmiNodoaPLvfk0YzPP0Yoq7aioxlsJRYoJ9fcE6bN0uFiM76iGETOZQeWchM9c1dlN0gDGA6LG9y3y8fckmC2RPzWYrWTPkk6McaaIALisl2kR3wb1DoM40S2RYm2ltbRKU7DeukN4yEP+Xm4E8v/zp7rVUBJHmWZuKrdvZjmUSWBalloYglxHndRNIBUYiMyFRIuzx1BbZyPh4EN424/ZyXL1eudEPw8mNfiNbwo/PL64oPXK+vvxgPvaL9eUjeeRZ+AZQgA2J+XasP+ro1IYQEljJW6pCYHHUOZJKIGODNLOgnGalLRxAhvP5gUPL6+4sHyzmy8tHP16P1+OXU9/hEacf4RR/N0hY0oJ6150LWHc2BcW+W90BrPgKqCUyMKKUvD1TuetMzQdmQq899WzcXZ8PH9y1E/Cqnx5w+34F/PZks5tT+uZyfHlNXUN2d130u8WTWb+9evkB/+Tl9nx49D9881F3xI43bUVVHU04ugAMlRKCS2wdHc662gFq5USFHqdZRK+BK2ILWCzBH14KvTbG68nECu9hfj4c2aiMiClS+RnfDOlFW6z2SmdFoHMSbCThji71JXQZwYONulFho/JA8f6AZV+hMxsuFnuR8dwPrNjpPitp+uWH4+qgdYvHHkAOdNWIOdDnx02yBbrStNDRBrdhZMhb2QiAD+TYDpkMlLNhSO3+sU+3x3ju1KvyYjOubjYBD0OVtCAdw3hMh71JG4EiHS0R4jvPI90AdJXp/ocftypYarxgsWkf8CPUziNQtaXlxpyufuOZWvifPxNYAaDKdLxobDT5sawlQEYsVD42U7dtiirSAFk1tqewrMzo7ByNpPVvPVPZnz+TLe+T4INtxU8tvK5FmqcIpWqpSWFvU18RGahWoF1a/uDFdhUxsHYm/NYzffj5Mx3LgWJLnSuUUy+aAasYT9nPUgEeaZ+CaNbJxirJUhpnBFNbZGxC/dYzQX5+/kwsTR7uCBvZawf662grXQ3mNrJPE2u6Ap8AiFXK/imXI80cLQ++kQ7LbzwTEeAX5hZ/Cf5KTFeV7MvG2nRdlJUXcECJrEmXBSSXWqxS1ZAV+K2k32xp4dAR9FeeGX7hfTragvgGKIHNCgiDkBWpaopXVbWSiMaTaQy1ZzXvTxItv0NC4GIh/0GX+C8/M8hfWLeO2o7OJSTjTI1MIDnJm1w8PCjwXoGURT+ympvm3U3k7QjGRnJPT93feiae8gvf04GuBlALSmNHbDwEW+A0KYHtWJqdNaAAICUinjWGHgjgjtwn1dJ/8jeeGZ34+TOzziyfi42OHngAOB74JIsreS/iKDfYeZYYykBJX/ozBXZlKmxm/JT5jWdebo63V/0xu+fvZHqTcrZ2CUFMI3nhr+lYL8YGqcj6tYQlyiatTFtQdrESIYGQChojq4Mi4afbGbLbBbLpFHinirL15eRMMEn6HU+C7tOipQAvXoxwklYVplCgGlksYsarZecaHkijDvBNge3isZTjJI/LBk91H4Se9cfL2UHb9PTQO+P653Ym7czcertsiQjG42V/PN/0l8P22AixP8mg55CRNJVi54XGUo68g/IqUiMUVE5WXmHRkEGBSXmLoA8wrZHxad/4HoezL/WtTmr2w0iNZSUDW8CCRswOHQKVbCKxQFlVQW/HDrgsG2+BzUrCorMHh5vvZzzqyEkwF4XJAVAHQwCPxIrHGgf7VFiXYMLWdqASkscYGHhht0UONF6hpdofnqDtenU+HMeb1yVEozV944kpkDMAGtsTWrRWU9WuTnViJbHcFfGCWjJI2sT0eGWYNfP+RrPvf8GG57axAlCoGkDBQp+9AvieWqFiSpdiYqNy9EnRAYFKkGoSDAWcre93OArjATxMTfCwTWisbamAytiqjAwbuhiAJtkRFx2GZ5E8wCOw6RFCjMQWv5+e1VK64935s936GeUIZvtfAkgfX+9393r+9OT+z493L29+d7LsV9j5NwYOezvk1fn89DmVKKXcq6XTU7UR3iBTG03kjg3VaQRvI6eebxaaB1odA2f6COqjafdGIVePJP2PHyV7tellvE83WNVUU4tIOLFlV3PkhboHKcWngHERr9jwwBAkwQm64pQDldn3OsiDAdrI21NLU3CnKHHgyRup1pl5vC1ZrBypsScBWyOdGSorsQGeu0kQ/G6AF/3V66wNHyzPqXTOI84niAen4TTMFtebI/ZyBQcKiGcZD/xPhflC0JEToEUnMm1BZLKE0Ck7a6RADsGsdIAkB4ayF/1LsrL5er3b7jb91S1Fu/tgMojzk+bJ3Wf3xhC0iqq+qCJYYSQ1DeMQsBEYEQoz3VeRpzxvsbqO/XGBTXOIWSIpJJ7wzsN4+uYomK2NNqmxDak6CmYLus1G8NRUsLwnuZPCCzTsrBgzUkliCOqso08L0N9vDuINrRD9NgmYStMcJCbPPigEIllyZF9PBkwhvvds+GchPCVNUgZbZeOnzjxnwdh+12B+W/sFP3UkuWKCAgcEFASHQhYH1rBOgrgmuqohbLuAFdWq0DRIkF0FYK3slWqi+Pc7sjvRFw5Nt1pZSsWFI/B6TKSGXkPMaQigU0cjdxSVhBQ7KwWbxzuAMqRdJv/3PzR1N7aEl6cq5kYZMJgSKTlUeEpkK73ROtptReCBLgDgRxanYDZZrUnDKn/AJx44NvvW1YU0bxCRscwBNIqeBOeysBpcJhTHOspmpzYHMABFye1CqCwkqaM9IMjvMJjfXl34qaOCrFp4T4D00bDLwHp8CrwdMZb4CL+2QSJ5BFMUFSgsreo8qEqiEYd+vyO7W10cGiCyz+B8QSvAIIKkLJTXuSq24Rn2B+UUdANHwqLqppwcaANeeflywCDe29DU3dgC+JqziBIUZCpU+6feCC0IoqIOAshPoSle5u/ZgjdZzXURTIR7s77r2N4qX5Xop4aB8JzASdAuvCKVutawlFqo1bDkrWhLh5LK6ntBjaMg6A6nQRp+z2AeqFtVlAmxguhkLDPQIeWR2CgPgzVlUiWuEslNYloW7xOZgAadHvnVuBLe88heF6wCDFaFBaoBuImV2EywvqNLpUbUoLG5bCwN9WDoWUV25SFDG0uu2r3z6nonpSpE0ogQnz2PkxRFxTV1KfEOA8Bep3hZV1kZLEEpMb3aGFtBQw32rjiok7pYzF4Bfowv97CEkjfP+pN+AK/czc7Ol0e0jXAZGKijsTwdVcwkgKa6TFc2GS0luLnQsb0Kmxct3hhoeqOcr2m/8qTptHZ/HHo6HSWeTiKj9B+KBX91RkYFNjQgpcqUxKZgF7DHp+M1kGd8IyHZbMYIDXaAuY/aHXieXVxfLp48w/qn7eG4W0/yLtKp+z94Slk3LTJAHmcF3wBvOUuKRSrFa1pgLsrasAcJ8R5oBwzVBZpag5FItuDdPW2Nv70/O96+2uPN/WHr8Wb88Xrc7kDwrDYAcC06S4/X1CjWDF4TwGhEy6RZ1JdBYkT6oxkjq3oFffFaKQd9kkSLs+3VekdlvZtVhH/f2ATdOLrt//h0fwx/up6pYTB+3k+ugDLzjk6aliqJXe06dhwlOipJh4lIPPyXTC2Kqv9go1Fr2ZH/dbUeyKs/ZCR7idi/788lsA7V2I+T06WkeJm0FFsgMhAm09OpAykHi4wBD0UEMJNbe5EAdCxmxhQS/0ePtPP7R6Et73aW5sbIzrhIfVG+kwKy6ynD0insbcXuEESmRJsni4eK5IFEk6QlRaYQ2aFMwK8NY7+bpz8AyzjdjGfstD1Vs8Gr4Ey4vedNGhSbHiqA9amrVuoOc5O6UrCRgSrplOsk0oQ21CMCtgzUq7ZIx0kcitBezbez3Yvzi7095na86C/x6FO2/WKXnZ2wU1QjTllNWUxBYnNy8d2P8rzaTz/Yizo/uro8+5fL/mJ8tN6cnx0JGg5WrAObeIIfaUQcEO1qM3pq4PFaiw64TbEtATtFU5C15YDwzDa1+6HxGmb24zVGtFhf35hUUO5q3GzWmyMayfJsLmrG0on78TAEqIfy5mDOEaHGW8IeFpArxyY0LIvIRYTYcP+cVb+b7KIm59q9l+dNAyaNSe8+mFw/ZEogTlj1GnAvR6MmHcnO88g3scur0QGaXiMWLxw8i5vWVueYNv3Pnsm53+3GvVbYAaecrRH38C6G2RNM6k/ry4mlnsuAINQfP93OBBaEWeDbzoMcrRzmWKZTfIoZU2uLoYUpQggYWANe8DpFwhkA/aAm/XqnI4IgICy4NCUMI6U2DgjQw0d4MCozuGEYlkbJUQxqIcA0l3ujWlvZgo3Xq6vHRkHU0qlR7GlSA6SgM91zHWJrkQCIWGYl4w06JHJ3UNa/D5Oz/vhiH577k8URJRYFi2Ey605yoPMMuEuloAj2Q1Z4Q9KohkwgG7i3x2Nd4JE8oErR9/x3Mw7nG5pYXW3Ww2x8eTVuzid3lM36erKlPRA+619sZ736aZqVl1OjuUKKwVBscqWwejrgXzQVo1w5SCCdrOQk+wgyiLyESIVXBkbFniIE1fstudnOlterFcV3X90cOS1OrpDwjhBeARbAlauzvG/UvJjWmDuZIu//IuDZ1M/nE6ZPJmmx6VVXukDNKnG/5LezvV3UbdM8djve6vrp+Yk0NCc52T4/OwEnNJO3D6YOEDjVSWKhssm0miazCgi07Eernv6L3vAO1Hk6HUy+xADXLVl3UP+2nb26jTQvzk9e7RbT2misgwBZQLTAFxNZyQaIWzElPMeL0rNWomQ8IPJ+V4rKY2esGd5qHtRbbf2sH4a7vXtjgoUXeD1bxOUonFguxqVfeCUGZ8xea5Adh0nQzaTynJRmSRKz1/ie+IWia46VYJ4MJSQa6CUJRkyfzQN9yL03M9Y/wMr5+mS1XvTTCxz3gpjYd0p4qtnlYlkrxm6FAoCEAA0+SU1+FksH7dnDUI1TBegrNNqHHxxIbsfF9Wa8up73w3Z2NhvW1/PVuFidL55Nm+Hsaney/0OE7qvVKeCnForr/cZxRPLWTVBMPeVC8Q7F2j8gGq9Bf7AZimX7sGCFt6lB0dBSJSwqgCX3B0cRplF8sJg/0jwRQVo5EsogPnukpkY/As2UiWnGosbS0hiIwUq32EKOh0ZamNQ4azwTNVh74g+OKL4+Im30EWvWkEcSC24A4CxbjooukoZBLbCtGORZTpaRSDORhQHO2IgBtY6FI/cjuuyvZhjATWpfnc9WF9vj/rJfvcKiZAf4lq6At8dWwG90c2dpOmhVMAiDiJigex3L/JDHS/CIoI1GKYWKpmzwpEVDFjw8Plgh29WNiMD9w/ZlB5NXjmJnI1glJpOHhog7zfPsKbA/2vrIyxOLtFpLI+lVDSsEsDWxNgwL4eA5u37x7KrfPXnbMR0PTeSvnBaCVPMCBTmJEoiYzY7GD/hGYJG0ypliAc9RVKGMFW3bQHYTA04XbEq/ZyS/dGBopWIfZGIDhuXBQAbFBn5t2Hs8U7bWVboGdaxBIfoOgj3SHgslI4Krh4zjPwvvfrfx/MnU+30N7h/Dvt/n6P4BBHyf2O58KWbPVv3z81f7+qE14spunAxQQWJA5u6jgLN2MZIbg7Yt5lRAduNN/JNHrsumUfwVcRmQxLHRFTiCdjvaU5W8ArHoDgQaGash5lmHL1G6VAIvFt91cDf3GjIslvPo5HIx0LBC93Yh7oYE5hSrYwk4mAGwB4URLKJUm4RUMVZEAUCFRiFZKxAjO9uAiVVkSU6Q5o0hTdhg3V++ujm2uLw+8I5FnlbAhMKKMOnJZ2toSlpcp1kDpvErGt2z4ddJwbtmUG9FRQvJ/nuPcJVY9gZO5n2XfvvRhHOT5UAoHUVbeVRMqQX6sIDAUxQygh5NHgOlJu3ot+0zlbIlS88yL+JcesCTthczZW6MOUGbWzBZkJFhUyTwl8LqxuRJG0pL7f9n713A5LiKQ2HPSpbt49eqJUurtWzLLcmWbe1quueN7cA8pV1ppfXOriT73mTomendHWu2e9wzs6vV9/3fbwiPBMIrAcLb+AWER0II7/BIckkCARMuAZIQ/iTcm1xIQnLDhQTI8686p7unT0+/RrJJbgIfnyVNV9U5p06dOlXn1KmCtYCPGWBaYeVkMFknXurCBpiCGYjQGJiNLC8FuAatNuVlOZNNxfG8JVmuyFhQF7qdlyv4AgHsixzm/anA7pNOodGVT4INkKJBViU5VSg5TP8IjS4rDRWW7Dl6VVhGxoENHsckLUnU3Bj3kk/iC+0kJuMGC6MCgl3AU/wUvtxIZjBJFPA9DTb1KO1SH1GmRhboFRgTFiTHzDdyQga/FyQLfpSx6ilW5E7gG2bwq2FXTZYxAQKYyHmQr0I6HWU+7VYtxwzFCMOEYdfGYuxlMJiKRTDqcmVMWZAE/YGhs2AuyBgJVcDQIDShcDHhzW0+58hWE6HZjVWl18UaPmi3FCVQF9ACBv/lMvEUSHAO8zkXwWIv5suY6hoslYKcwe1TKmC28jIYXpiDuZAvuNUGtNudcvqb/Q2ax1oGBwcEs5RPyfFSMY613cH9rgAjQaowoSwoiXQe42FkjHEsonEElrQk5ZKYD1F2NzNIKW/aYcxGYvGmpmVwZF1RluvpnFyXFcx3C552ItEADalmwJkAz1QGLxJUTyaRzuVyicMSMLeUBae7HAf1k0YPNF3B/O+wSYIvju+GgPWYAzRVKaTwjr8A/QMrD2QOY4qG+siCQs0E2+0m/J96S7AFg1kG8pyCuQS/FqtcYYZyuVRIZ2BRF8pxdIDj8RR9KgjqvpwCt7ucw3NKuexuxm0rA0N6hqpgXG4NQfpdVmMxDR4ThunFK8BmWiUlnanIaIbj44FsNov5auOVFChBWHV49gBmMxhReZAGR0lfs1XvXcEqtpVSs7llMNellNJM1xupeLNeH+xU2SJ4zzizoBrBG4XNCN3EPOxQBbDWEtl0BsMMMbQ/mZLy5RSo0KKExUrB7SoXh0TOZ4PCFd1c6rXa4OarYMI0kmCLScl6spnEehN2d+LxYjkNbqKEZ1tp2AvzlTiYaCkM9EjlMeIG1GgBU+mUE8Ar+FIAa62UwcNBGdzAiN1prWj9Tg3PY7rTahL0SiO9nFXTch0rSy7TCmSsP2DAgrMk0+NHGdPEwOaJ94+Y7Ae3TOgAKHfY0MDAgBVTgJmFLTxbAXbFMXgiWn+sUl5cv5SE1GxKSUVqQMeW8QQuNZg2zHqbh1/AlsHyodlcTs5hKGYSOlrAlDpgVYMFK2Vl+FxAt6ScwIxq8TxWtE3xitHLyWs2WP71fAULzcLik9M5WAsSFgvL5fNAOZPNghWYKVbAiChg3dwcJhvM4/OuTAqWE6bBczVj1pxXp6jLyOq8FQpZGoRSgQay+IAD3+Nm0uCwQp/BmUhlcQlKYAxUMqkirSmdLOOhMdjusiMfsVcLmae3hU2t2WIVPKdhTbe7xjptbF06gkedndUO1i9J5tPghaWwuktClkBpgewUCnmphJ5RGTiXwTSGiSTWMoOdOgudAhMbBCcvOV4b99S2apU/cMoMtMV+PozeciYfx6uESrlSBGJgW4J6SGE9ily6ANY6GJ5SBlNOlwsptDjBVsaQKDCBE9nB+Vi/qwZEZUXwZw7DqkR7TQZnAMu5gw8KHSrDEk5gXpe8BLoaNHYO9FkFH06lsf5vAVNlyGj15XJPU1dotBp4BTCt6TIWQa1gLfY8bKCZIliiFSxFUcByEcUyrGJwqrKYFLNQAm2DQcMJME+l7NPYF/lwKV4AK19OgecLWi2dwbLsuWwG3+KW4lmY/RxsPynMKIlRv7IMSq8Qx/Q94A3ARjPYxNbPWefnWE8PbztY9bxng1F/uIwJisCXTknpMi2KiEWgwWZIJpLlbCqVLGSwKEo5XsBE6DLaDHFw2MC5LODJnOPEDzovTSuNbh3NEfqko9OhZYPxHUUPBK/ZUmht9DVd0+lRZqWSzKcS4FiDy1UCmzdZgO00XsIHg1KhCDtJKR7HVNiZeBprLmfjaCWl4xg2iPVmB0LIyr+bJaqm9X6vbigtzTzapJXhm2pPbfSOdM7jpeizG6ugBMGkxmqmlXg5jvsmFjxIyXiUgC9bYK/Kw3oDVwM2dbkAkwsWTAXLTsOGgWlDs0lX69M9o9/tdVptvec671hPHel1pk2o4SApGCnYv8kcptDFkiBgqyUwACIdzyXBngNnI4dFYLFcaRr8EzDTsZxBPilVwP9zPOjZ2NiYtm1uejtjHDkMrgV4M+A+YZUNYGYpBVuNJEuwA+WLwEVoPFVJJ1IluQzsxvDtXCGewyxF4FfBZspR9zzLGvwLj3DB6CxkQYzSaH/lseRMtpIt5LFYJPyvEseciMUC1ivECEfYCCsymGJYpjML8Lnw5jrt/gq+wOwrR1S6ycipFKgtYAlIUDIfx9fVwEjcdGG6cJhprCyA9ZXkAuxnRTxby2OqVbBMMXP4iE3qa2uq0TCrACZgN4jHEzkpn0mU5TS+MQV7COybDCbmwNrwBbAsy/hwLFlMJNFPzEKPEoUE+HmFkVput7RzrKoJOKIFGCxefSZlnMpyMYMvfDG1Si5fSoPlgzG/6XgcXEfQX/kipgnK5SVQYRiu79GspR56q0dSx5WkYhztpZrJ06Xag5tGLzdXWU0v9maPL+bkuZVa7QFdW1AvnHgwTzsDyjIDnj4MtYi3cVgTDEzPJEhvOZEAjw58DnyMlAOLL4+Rd8l8Ags+gR5NF3OOTECenWmuLWSUuQdWO0tHz59dyq7f353Lzd1/pl2vnom3zy+ff7C+sFrpVkAZ3UcdrxS4eVh9rAzGRCkdB2mgUwK6GQN+QZ+WspkUpgko4jFYAryxPBYGBr8gUU4XvKTP0ZmHjs8uHT01nz6fWs0aa/NrxxbT8bp2WkrrNVWbyp4/qy8tqt18JTVPOZPI5Sq5BDi2OXAJEsk4VXPgHRXkYqUopyqwssGZx3jITCKLN9e5AnhHFXDIM2BoOPJ9DToDDplVW4dKh7ICP1mvCNCuwOWQyKLTB5o7gfnLpTLoNbQdSinY0XNYaVbOpyu4PGDDx/NcvDtNpqV4JpMoFjIuoXSEvNawdo/WxVqxNYSBH7rdI+fu5X8FLTjduL9cN06tbaRgVk5Mn5k+daR5b/yI0b03fzJbnV/uLMlZ+UH9bKldP7dUb8/1zpw4oz+gJet1eeXI2r1qu7YGyruG56+g5GXw/7NxLIOUow+AYdMHI7GUSEiVEiZKllIogHheAV5eBrYmMO0rMEzMn51/GgZz/P4HNird48VaYs5rMPrpWeP0+fP1XG2muHp2tph6sNGrrBw9s3r61MYPazBq31DPKUcabUU7d0SK5440lG6v1qVHjvQhHVhvedi1WUwwNFbE0iFYj0wugscmZzChJiiKXLpSLIKGAh1RkONgi4Jug02+5NuweWFJH0S0tJUjTEpxiz8inXf8q9bQ27pRS8rnpXSzQ+9naWGHYjKJT0LBuABWJPDVSzkdz2YyYNFkK+VSqYgZ2cAQASOvmIGdGnzMcrkIW7AUj9wnvD3uw84PHRpMrpw0u1HKg7tYSSB78TwaI/dAiYBBn0mXEjlwDTFaM4unUcUCpkgHqzsNhjkGU+fA7U/4dsNQG0qn11hV0OSSqdlTS2ZpmxkwLrBEUSmHRePArMkAExKZAow7XQHjulLJl/BJJdjgwJBSAqysZCWeQrnBcrL+Qx+0aVuavZP5hpx74AGj0zhVLBvriqwZyUI1N4CtgfKiZkg+mUrDlIPtn63EE2BZgmeRxLx5sIHmYPNMY0ByJVEBgxe9oEoFC1SCn5rOpUtgnzwtver2Nttqt1ZroxbBe6k01jMASSkUwd9PgKOcLWC8CiYOAwamEqlKKYeJjosZGYwVsEmxm2U8w8+A3uc7hYcpyoqhrDkvpOt9rYmnzugW6UfKGEuRx9i2E/rKCitnKGMpkkZcXs5IOWbiYKGRHM11CJZYOZPH04Q8LYkDTkMGNv5SKZvDt42VbAJT0iZgBHl8MZoqlgvyRfVpodVYnabXVPGUkqjXpWw9HacsAvcIX7OkwFcpw66WxVfWGKuF9Q+hUzCJxSxuRKVyHmYsh6eMObmARTvwqkouXXx3gDfJZDOtxhuJJoyeHfOCTQqGULpUhs03m8IEG2UsEASmLHi2GBCSwrfCWH8ba1IVs3KhXAR3C+t2w5q46M4UQK7+HfGnWjqO7GnmlGZzGfzzeppe+ObByijiAivTUjbJEmj5bALMclAJlTJmy8Z0pZi9O5nPlUBfV/AQIAnGM6gbcDEvri+tJqxAw+aQmsErmiSoO4VxCGtd5TKVFCYoAUcgnSpLtHBltoxvoPNx/B+4oLAPwb/LMkxsKlMsSKVMUq5gKMWl9Kqs9YzNjt7CeLJ/x32Ducyo8nI2lajHmw0q6qAc45jlF93+XCIPoh6vZFD/lCv4zK6INVpAuPEIvoCHF0kpjvdjsDjy+DI0d5E9W+u00fc4oirZZjyrKPVUmjoFcbD/Uzk5gZnR5AzYEKCYsZZ8GsPUs7KcTNEfE7lySpJlGdaDDOZ3tozn3iBgyUvoji1Z6Xh6WVLAK4k30nT2ctkSVrXKgHeUB1cAy4hmkhnU5WAKgVsbl8uwzRWwNh4o7HhcApkvyljmXkoXQW2M2ilVqy1VkTuglBJgSEhNNUsj3qB12NrBAqpksmBBwG6LqUrToK7A9E+CWQS2FxYXjxclcPGz6RKo90oulQBjKYuBaqN2ZF5vby632u0ulZz6sqLG1SY+q2DH9/j8ogKtg6hgitdEJp3NZ8GVB91YwgxBsMsXwN9PJ/BGopCPY0clrDBfzhfyxUvqzLIs1yWsJ7WcoaG8SbyUAFetlADPNQUrCfRwKp2B9sCFgEnAREHgzWGgB4abYoQojSlIA1TF+UD+ojqTaS5nMnIz12xkGWeKOTCGsTxwDivy5cBbz8KYZVCMGHCRwppEMqxyOVMo5JJgEWH2CUy6l61kwHgZtTPdDl7rsPBa+Xyt3mjm4g2pmcymqaV2xP3DYfRcSxi+noKNq4TRW/l8poDJehMJUNh5vBSXyilYg+kMjAXvvkpgPeIteSETz48s0GYH2cNG6GE6maw3kpl6AvONYw/dPxzGRCwJ0I65FCjJTCKXhwUvYw3ItFyRQejwRrKEm0oCr1+yaXwAh1kW4iDtBVh8F9nDdVCZOvYw2VDwrX4ml2omaA/dPxxO5/COG+W9XAYHHAvNlGFHxkzu8A8JE6VgMBQgyGkMlCzie80SuERSWQbFwPNwU+/3+nXmpZt/b9GKu/pKTV1Xtd6zlXbv3ge7unbbOXXz3vzMBaW6mT9Vq8zK1fZDS9n7ktXF8rETR4ut9kbt/lxNkh5qnMkelvOVDFgEuQLWIo5LqTgWHwF/Hh9QZTJYvBaUaxmPcQqgtCSwJCoY6wwSgEVKHTGWF6bXdKWHgXANll0HAyLxp2XgqprCwFe8PANP4oj9KwuEAiUolzPxYrFUzOEJWqYCKwWWRyYHKjRfSCCf0F8Ed79USksyvnMolsGIkcqgdOt3khvmVbCsu611dV7ZbOvQhxOtbk/YXlQaq+oiukIdxVC1xubs1eSKkrqs9Nu98Qdm7yZX1/KNXmud3lSMX1a/0wNF8CY+myCTFd1oqM1TWm0ICWjtmPRoPkluNJGWl72xRA+su8nNdlOenQHE3ZM+/byH3DJo0hdb9MZe2CNciwnH49MpYP90Mrtvy6HLVj/1xK8/tu3sV3/m737uqufGhFfHyC1FRdO1VkNpty5QXlaBmGoU1FVlvaUb3Wxs9hZy47yhYmCz3qw5wNXm0sKJ7nhs9gay3QGgg0Git+Fnx4TVZ693TdjCbuHqXAZ6h4UQpuOsc2/86uPboFvfHiPj0A6MqYgeOeUq9OMQOTBopqwp9bbapN+q0Jcsi9CU5Di0fAc56A8ppSODJmQn6M1kcmiYzu8cG6yRP3d2v1tUBWFodLMS2eMQSf4jokwOo8i2FFOBHMYRh3AWJoRrLJlISRbXP/PRx7chz2+ywFk2rWOtumpoSk9tzuBhBUzAYXKFyaHxWP2WEHiEtmWhfosQAs0JS9zNslD04+S2If55QiKxyRBiJ8jtw5z1pSYGU2M8l5Hn6XjCKenCX42RSYZbMVR1Tl3Tjc0zq6p2rNVsqhowXCbjJsOZnCXkLPBybxAW4nCyaeIIQTgc86fczA/GLZP9Ls57gSGZySAyFXLAzXM/OmIAHcbtOHI7mea5/S2m7ADxVL93anne0BswWwtKF0sjMd0HLL+LXGuy/ISCT/jGm/UJssvE0zsc+Oy9toK2xu4Ggf5OTPqh/xjZ5x6zF77ogw8a/nIs3HHn5DVY8wAFLJ1jQwYl+k5buE6BPm8aysapTq+1Nhgpp67sdR1JmfuLiFdbHiLiBeYUEU8yHiLiR0cMoLOwHdgmJafvZLLx9T8D2fi7MbKnqBq9Ul9pn4ahL5shEItYNAt49Syyx1qIZ2ZO1qqF8gKuLvh/XILVdWMANuJaC9ITVwjA5bh+2M31QNQiEQdM94NCIpMBREr2zCHLg6iI/lTomoynUEClnL3rvO1n30L5fnMRX+QeW5w7Ma8YXbA8+ngBfQKW5qnlHtWCB8mtQ4Jaq/bwT0vF4fbLgVkcHwbz3KXrsAG7mLtPCOnZ7Jy9TQCHA0GR3GQYuZPkkIPXofTEEHpME5pqIW5x/UuvoRbWO8bIoeKqoa+p+U6nW1I7ePyNAz+pM7V3pqXNKY0TLa1/HmYgM6wUDwjicVXt0B7PoAPWhq/zNDiS/R0Jzy6Ru2wWhYPDsA5MRiF7mhwesCoaXTEC3QUBNUMcFOrlyDmqIITnbSF7GauKCgi4voYHSc1jat8AI7vVQAN5iUiqKZYNCllrAGitqa/V6ghcW7WhHZJYvzmYMHgc7p2cYgnBWDvJuCnVA6z67Aw56JBVf3Rg1c2TwQ3M2qYWldNgWmIgrYX9A3s0Ye7W8uWqNrVUXRjrd5m3AuJ6L9lhkllVNE1ts1UNnB8fWKTNiV948+/EFsaFrcmcpdtf+bqfugLQfx63Qop/ptVUy41VHXaHhtpus63wstmj5Eom2EhHvpHs7DDjoKWt1JZby3qtC86AsAVkgxpfvqT4nc4XzNzp/MnwO10gHTGAzsJNA8WbBl9r8irG7LhtJLx0C7mj2G6pWg9Pk0s0+ggQ5/Sm2nbumUf78PnSbIZ73Ar2LiF607MNIg8YGxULG5kcoZEmSTjYPkorYvRWHP5AKpez9PILn3him/BIjFxdxDDzfCff760Cv/cNK95rOZjZ28lOB2Ps36FT105ygIfIDc7BcZCiE5KalSAnDrMykWX9FD58FdkHfk69panNk8p6a4WOyznGrgzdfuOWoX7LN5EbmkwYpjp0v5qq0w1L2CrF17qw6IS2rq24vl2eiuPH/eRmrb82tdlS283u1Eartzpl0TIBY2l5glwP29iGYqzVGn0Dj0OEy5eVdld1fllWtJ7S3bS+7CLX2l9a59Wm9fskEazfsTJErYs2jbC1Z/RV+YYBjvPnPWTc/rmnaE3FsKjVfyZGDuXX9VazrBjtzfJ52J0xDqZK3//iclHP94qGSvlHbiypDRAc9rGq92HKYMYWV+F7k4ybrFZLSk9ZMtpdeiSBjZoLrQLUKlj9uaV2yQ2LrTVQty4LYVYhki0yUbuFJ2yTkQcxW7eXKwjbKG2I0ds4aitJrRbAM6B602QQU2eP2XsjdDaEkhhIyXmK454pdorj/pU/xfHCEYdxnLuM//SzXcb/O7/LBNMRg+g4zzg9JY6dcXoLI3fG6YstemMv3Cpci1cKoKNk2PUT8uTAnEgxlXX2B4/83oeuhq3uXeji6WsdHd8jwPTp7XYe7GRjXmlpvUvb2jwdM7+meMfMD8p0zHyJ8I5ZEBXRnwrbijB5A8ahmFvRw3/4z7AVfX8baBbVWGvRrPow7uUWpm3Bv6+2OsCugts4lZLjpH5YmMDvJ3TmTcw0YHMAF9yAfU8Yd1OZ/XFyl/s38D/0xjknHkeeDBHxb3B2P9ltT10ClTJ6glK80+iNk4nvvemp2MOxGAeUdAN93wMo5Qb6gQdQ2g30Dx5AGTfQP3oAZd1A/+QBlHMD/bMJ9F/AvHMxLJi/vtwc5rxzDVw2e697DYwmCnlyq8Nw9kYDopOT/tNdsNcVNZr9aYj+NLhjeFcfzWN4d8/5Y3gPHHEIh17NxHP0kBKWn5RhNuCnH6a++QvHyK6ioXRXF9SObvQKuLM0jP5and3FXGWdgKTGL6+Pk+tO6CsOEASw1iUFENwA3DXare5ZG4afIrttlvCfEHzSDT5NJgbsGIYXXfDsbIhaw/G4fTb08Scf3yZ8YozsL3a7VFOBIwZUqx3wbOCvRw1lbU0xypjSCZlylOwttbqMK+U11VjBq7fjrXa7CqYiNZoPClFIzZ6xzxVg+sPhYUAHJyMRPkumHEISjbIYhTIz1uPMWGeXDJLJRuHvt5GdpZn56qLRwihBQM03zLPf18bI9mpPN5QV1fxaW0+Nx2SBXLNiKA21xvSKMCatovUMfVDhZ0Su9XptPHbOxvHLeAdDuru9WhNsEzzrMI3h7eRqfJVp1MADUjYpmb1ke8/uSI0RE67osk7Ut5Gt2NXZV8fIeEEHl+GH1q8bvfq1rU77YHfryRiZqPaUnrrcb/9wu3fAq3vXd82+1Fz9BPYJ84w2rAH1lNbefMZ7uMerh1s1XRt062UxQiytdBHd2ePRHdNRu7j+OHRgbXYPuXbgR8BXWHvbJhncJLnO4RmY30T6zX29LjnuUh/bQm4onT06c0Zp0W25uqF0iquwkmHlHSF3DR9mzynn78MiQs0KRlZ0JSvbUGI8GoI8KkLCieB/r82++9xrH3LvHbsF71FzDoInBHMQvJE5B8EXW/TGdh62ZOzrwOd97LFtwre3kpthMLDxq83qptaA5W30+p1iH1QSuDkgTTBbGpk0ORivgt+nNdkB7lQ8MYXXqvJhcnswjRnNxBNi8fq+sBZn1+x7JonhXXxzUoTmBsOTL3l4coT2skRwypfVDt61hGA6VuxzuYuXYDx28RJCm7t4CacnhtBzBlnYXtWnfvK9oBg+OQaWCsLQG+jTLXUDLbwTyoVNGirQvTQ39Ih7Qd4sBLbGXQoEAbJLgUBS3KVAGC0xkJbTK5Xs0KBPvv4JNI6/FyPXl9TuuZ7eOdnrlIzWOh5L/wS5kf4V1JwGJJpLWJ+t5jjSv4vst8DnaAIfJyDukvMK6EW2x6FRzQNzNjD/idnALnDOBh6GF13w7BSDDhh1VSY3OHhNZu2j+n+NkWsHI68YOO44uQb+4hgo3inBV0a3W2kZ3d5CXyvTrJRoEHBTHgTIpjyQFDflYbTEQFrROPDT15PtAw6YxIALt5MrzUUQHz9U30N2w1cWjLOK54fr6uKquga2wOw8ucNeRFJtQVXadf38CYAZxNCb8Vzx5Hhs4o0veM9YADn0r50U5XCKbxqNYiKc4ptHo5gMp/iW0Simwik+MhrFdDjFt45GMRNO8dHRKGbDKT42GsVcOMXHwyneR+4cSHg8nOQTI5KMsGqeHJFkhGXzthFJRlg3bx+RZISF844RSUZYOb8wIskIS+edI5KMsHbeNSLJCIvn3SOSjLB63jMaSTnC6vnFcJKHyFXm3iRLwZvTs+1gQLoze8HARrpn0pfAc+wjVLYf+1EQ/Sgs3M4dSyYSg9sU8HAzXIj2NwTvvfgC2TOvaDoYUwrvSgLF8Z0TX3jyqVi96ssE4WZmR7cuqGa8B4Yul8/3zEMBYcL1He/boT21jbw2wEa22na0+THWZghp4ks6sgr/kUnxI5PiRybFj0yKH5kUPzIpfjgmhRxsUnAByoG7nxmgHAjjClAOpSeG0ePufH02X/PO1+er6843gIboT+PfgeHlODEESyvLWVqfGSO3DCwtTwGiT7NcL4XimfFYfRfZyeNUVcVorM4edgdvMGjBG5o7ZLzdfcjoh5UjNznZOgSAqJPeqM+y7wsYQz1xRU9c19W6nGa8/MSfUF6+Z4zcavJyXjF6LaW9aCXbOXqmerrVbbGw4uXh8MUskXwwF9XzvaraNmM+lfPFVQUlXDW6wpZUPI4L1AeTEz0fGCZ6fgQ40QugIPpR4II92ZlrfPCG6EtbBvwy1GW111hd6qo0EE/rLYBMNHq6Qc8hr+PfZYzXMVDaHwekY/cxvd2sK41zrrca7LFaAOqLY+R6G9fCkSfJ9jVY4bWOodfVGhaVamnWJeEesr1j0qutmpjmfeZNZGcXT/Br9MVuzaDZqFTDitwMHgQXAOMKyvPBsYPy/Gi6g/IC6IgBdJzv4LKSFQ//3W89isvgLWP4RoBO64KiNQubvUu9erjLrRUmhQnoVEHHO9hq9QQMyW6J0/t+QEzv+5Lg9H4QDdGXhusCVzI1xbc++fg24dMxssNkUBWTOsP411vqBn2DPBSQfYMn7GyG7HWvbed36NwNk56IWVtxDta0G1P0wqTPV+JZ9nxFNp+v4NsHExavWrpnaH7PE1i0D3gCI7rNtXPIUnpcxjsQHhLh+LthBie44bhF4RVf5IJ33q3wn9jdigucu1sZhhdd8M51INuvoPBZifDdGB5hrC/qeru7qNQXFQPwLm0ZHHAPd4cw3ASXfmDoK0s/MIzEpR/wxBKHsdhTx4S1/N/06ie2Cf9rjBwwo6XmlW53Qzea3ZKhd5r6hgYtFNcbFRpmf2mcyLg5cZsQqdXZ++1IKWBOBAQkPRmN9ANk2sHCiLTFSLRdjP7+lx6nIbQ7QPmUtO4xTLNRXW/U8+0OxoDsJTsch0ZmpIUE1v9v/uvn+K+y8+tv4dc02WXOhPNpIwP4bQBAdeTRKkc16UT6tLvNlPPrZ9xf086vv2P2yBSC4R59lvZICO1R1on0OXebOefXp/ArbNvOQwnH588PfeY4/LtDnzkWf2Hoc8L5+b8PfeZY+cWhzxwvf2/oM8fMLw19zjg/f3noM8e0rwx95rj2++7PMse1P8DPjgU8D+retYB9ZtG5zXl8Z9ucFyK3zflgil6YTpWezFgq/dGfe3Tb2Q/+z1f8DQED52fHyPWAhmG9FLWon/fY6ZJShu10JX113tAxOZABkEM7nQknuOFCdzoXvHOn4z+xnc4Fzu10w/CiC55F0qI9k8wlbLZ89hef2Hb2NV/9zuOXCz+5hQgl0FuYIWbp7ILaVLESA/DlbrLD4stpazXI8jipi8gahlDo1wEAzEbu36dlmEaXWcAwBRemMIzJiRvPGyekxRvnb27euOFFN7wzrtvdERbXPdQ9Lq7bC0ccwmEmpW10Z8wrkz/4Z5iD93z0HW/ElDtv20puYYHdleI8Rp6c0qr9Bj42Xe63i/raWgttkIfIXgcQ+zmvNUvA6eq5VgeszlOhZITDCMr+3p1ZPqn3MF5p1dC11gWwjKxXIbpBA1tnT9mnO1othDKM/tbJsObxKHrAwAgUxVCKLZKyuzjK2ID69ORo3HiQpAedH7UtcaS2mNme4cx2EJTPx8jOMtbHgCnvg85eAEMfX4BlMWfRkBOyyxuYO4XxAmCnMJ6o3CmMH67oics9pDczbQhfGyO3sm5XlHYbfXB8RdZ1PHVgDsklWJ1JtxLeL4Q3OVsldw54FAaNRCcjEF20Mx8g96JQFcOpMgXDAqMy8rScZArmBy8Hu/6xMbKdETimaCtnlF5jVTWyl83eSW4bTnA1oNpluUhykhwQCWx998md4eX2DHWFc3uGvjK3ZxiJc3s8scRhLMwtlsUzwGRCtl5YP/yuX3vnNjz5mGDgc9V8/pR2Ut2Y0Xpq++j80jNwAuLXEncC4gfETkB8SXAnIEE0RF8arkQ4//j8J7cJ3xkjBxk85WVTX2FighoZ37Of0o52+jNa6xId5aybW7cL0Zqd/S+2E2exLgQDiU9GJP5fyRE3UyNQF6NRZwZahj5WTdtxrdQnf8kYqG+tYWx2erA4aSaBY2q7rV8ak72O6r1a4TcJDwBzk/BC5TcJH1zRE3fhBuEq9njXPpf5yk8DM145RqbL+BCkY7S6qq37YJCaikebBUPf6ILJizqsjMkzceVOD2+JN5I9w6AmEPcI1xeKPcL1J8I9wg2kIvpTobGv1tOt1HRCGpzEywnrJVeM3BXEEvulvM0P7zRmLjhrHM40Zt4gLI2ZDzqXxswfX/TBh03Nuv1KYipI5+nlV9BmQODFFl6UzeEKA7nvwi/s8abWVM8/AzZDWJO8zRAGbdoMoUR5myEKVTGcqkvLv/8TNI7+BVvAWjjfaRlq81ir29MxfS7KzVfHyF737yf0lVbDTrMjwx561cZqCzYRTNT6yrE5pbHa0tQTqmJoeMzOar9Mm88UaZIq1Zimf07T3h72waAXaf2VFRVz3E2bfzNRsA/aNLWXj6ubR1VNNVi2Ulpu8vAgFwmojhVlhZqkmDJg+tji4vxhWHy9dqvSaiNh+nCyCJqZ/9mkxP221FWbLMPg8cLho7RQyFKnCX2YNnNIldSeguUe2a9FfRUWZn03ucGThZibKJu1JuLjL/+VbcKXt5KbAbbdarR6x1orYJN19XafZk7DV21n6JMxzirjTzDiqcD3W+y7j9V22i34ZeHafLO5qHTPnVDVDWVTCOmasBP4c4JmvKSf6e9dUD27BulFnPSgiesn+SZmD9t+PyYKcUOLLmhnQENw31hAQzAMH9AQTk8Mo+fcR71Yw/ZRT6Zx+6gfruiJ6zp7fvlLH98mvHkLOUy1bZNm6dg80dLO4VsXkMOqukIXh55vt+fbSo/W4bw0FfpstyRNCyO1zjn2oyAyx36kpjjHftS2xJHacmbgy3A5joV/2UYO2LvholkGc7Oo6+daarfaWtGUdrXXb+ITxE33qWU8MS7XG0SqKssqtSpAhfrSOqr2oEuMIpmOisLgsWmXOqFNC6M3LYzaNHe22nPL1w+lC13yrMFp06itQScTk6N3EoZ6t+Pc6WJaFS+i1TWSGXmodovxyVE5q5Hs6IMctCeO2B5TkHFLQdIF+O0tRB7GLKmNtoKGxbp6Uu0tqA/1wQLhl2PAZhx4hGJ95xTnCbdg3y3kgscW0MPZn4yRQsRpDCCDvZi8hF68IEaKUWc3pBvixXfDZfW+6iPgWH50K7l9mAj0EpNQqGDy0bNLa6LfFxu6LkqMx+RbMYtU24z2wqMUYx2Ew3wEvTUjx+PyJBmfU84jPcwhT4NNhG0SFpyJy7vJdvNbd1E3U1ALYylE2mE+JaZvYy2KWxLxeP0QuS2YEVbfwRVzX11Bn+uHhKj4nHTm3NIZnc6P2wcpYZJooSD5yajkf4LEo4qYk74YkT4THtkSnv/xlidornZxGIMKiyPhBsjNj3ulPpw42V+rq8ap5TNYh6Q7Y0sOvtO/i9wR3C9HA1wizMhY4zQRZvRGuESYI7UiRm+FS19unn9Yfv/vbyEJD2QdQ9xaStuKxVhcVZefSf181L0C0kIyZHyeXQRL6t6os+ZJAJuevLimL5AfizyXvm2LF9W2SwnTdfTIGNlt43fnVK0/o+U7HfzLpTkhd7rnao/g1xAXd+sDw+Ju/QhwcbcBFEQ/Ck7fYFCJ4wV/CabJP8bIzorS7c3jHSFuMpgTh8XZHiBXWomuwBDfJXjCze4fFN6Q8WLQE4gzsJ2Oqxc0c1w96XCOqx+u6InL3RJaK/93x8muCo3CNVNsYmKDfptdDXJ3WSf0jXmjRctgls+rjZpUMyN9kjTPewisPAJsYgTYVGRYeYT+yiP0Vx6hvzLf32nHaxVP/qrneyPAyyPCJ0aET40EL4/Yf3nE/ssj9l8e7v9tRHTDn9AVLBDMy0oAnBwRLhERjpOPgM3Tv7bCqdnPxNy6+UMxQSydmjOD1rEttYnJm0CVUAaxtS/sobaw4xdkHD1zE/aykxj2M57FYSbeE61lFdOBCTfRVDLmGGA0OphdZoLqSuu8cAsbI/7qTX3CAcC+mbS4igbhIxinFQ3C4fiKBtHoilHoOu+5fJnJ7rl8P/P3XIFUxAAqztQzQZPHUs8EQfCpZ8JoicG0nJWjAqVmnFaOCgThK0eFUhNDqDlDskIkloVkhQDxIVkRKIqhFJ2RDH5rhkUy+K4oLpIhiIboS8P5gC5hp3n7h0/Tq67XjpFbwLNDEyLfMPRuN7+83Gq3sEDXGbWOeZHx+Ds5fGF6aygeN0MhsGyGwghyMxSBohhGEVizNZfxdLI+uYVcR3Mx4aOwzapKL473+/pPsoyRwj/9mqdcQK7wTwR6CQJx1nnevQPEBVfbws0wtetKY7OqaM26fp7/ujTDhYjyH1mIKP8bHyI6DC+64Z13S8E9YXdLIb3l7pbC6Ykh9PgjiYd//6epaP+fGMxgu99dPdrp53slQ8EnSnvIDcMzKOFBEP/Jmjf2iZstr2Bmvh1+NrhP5mzw4PxsDMGLLnhXoFsi5bjbesmVNCvn/Xq/YqiW2/+GGCHmWPNT0vg9+LCv0da7aq27qvfbzRqt6DIo8DBByIqh9zs1TQFzwYEq30J2Ylx0S0OUNb22rhgtResJV+CvUy2tHidX2c3TjdHsyabWW4UtpcFqXqkrrW6P3VHPvn7QuQLt3F6fzrE3id59KzwzfXtk0LfiRfcNMOWDPn27dk05p05twqY7pW9cIvdKF93DUhD3mvrUmm6oF9G3s3a2bdqz3Vzbg09AOQo5YdD87DWD9fjqe0DhCoPVZsHAwrl60oFygOxwrDEnlOiAcua4jtCncZrjOhovpjyaD6YsRqHMlIEdISUlHCVz/mgrmThanMs3Gnof71/PqZozllghOwt6b9U6NDHP+aRxuX5UuB3wZrRGu99UnejdGY3GqrQ080JB2AuAjKgTDktRYebL55Id2ISVR9XRAonaAglugTuqqdlH31otInlg9B2TUfsC45EG0zhCC2LkFpyOQNDAmSMQyBrOEQijJQbSYjus/VQa840KLxojN51UN46qPZCgDjg2c2qzpcy3MKLplNFUDVqjdchwvCUEi/M5AiGZzxFMjPM5QqmJwdS83hCDofGTV5EjLP4JUy0U0dS0whzMsjx4qFnt4ZNbFgvVpceVw+8cpRTYh7/86qdi5/E/mM/kgMd7Rwr1PoR6nwdUwgn1Kwj1Kx5QSSfU+xHq/R5QKSfUBxDqAx5QaSfUBxHqgx5QGSfUhxDqQx5QWSfUhxHqwx5QOSfURxDqIybUQa+3jxTsowj2US8wjvu/imC/6gXGsf9jCPYxE8yhg87OGm7zUBEOOcRgICgsJwdXHkzYy1YLlaMBTr7dw/A/YbcPGle0Kmpb47RoVVRovmjVKG2I0dvgDkECGGEeggRAuA5BQmiJwbScFyL+A90z6Ts93IVIAAXRj4Lr7VxKHqRqP/svH33NP10HWuhzW8jBAXvNrAbmCRiOxs5qArpnFyEDuQZpft0jTJCdv8vw++s9fgd/aOINHr/TDH4ev+NqeZPH72nMpufxewZz4nn8nsXMdo8MLbj/6vm4hKUAwjmlh0iDnDDckYwPkHkk40eCP5IJoCH60uDKntsXXG946+Pbzn75z9/98OUwnR8ZI7vA4yu2VUXrd2a0gtI4h+az1qRFfx215if8ABFsUGR+QvAD47zbO9zc9MdzRsZ7g7DIeB90LjLeH1/0wecuCdNcAOEHx8heVg2t11MNMK1Wyuc7S1qL1qZjJzlPcyb5oNY4tRYEyNRaIClOrYXREgNpuSJjaRDQ12LkWvomfKmzYijNS018s8/NqesFnjwXDs19YeHQPDAXDj0ELfLQrjAVzHElfHOM7Jxp4l38ckupt9p4UmoekTzAxTPJcUnCEg+7iZBvt/UNtYmZcfS1xc2OisUq5KvIlqOqJoxJcflKsmVhVcdQlV3e1F0JxUzaeEHtCR36SskTy3lJ7QXALqk9UblLaj9c0ROXMjluVxT++F+9Z9vZ5z/yt39BhA+Pkf0zmqYatKSK41Fv31hWGmp5vWVVW8oOuwgHI+FyznoEeOasRyHMOesRKYtRKC/sG2RnwMAGx2umlJ1X7FtjZDelNSjoiAECHYUWvc24c9lJuPH+xZueimEuNR883DMzHjUJYxN/SRGFIMTQQBIfXM5u8oFhdpMfAc5uCqAg+lFwHY7I8qDO+9lvvur7344Bu189RiZmtK7a6BuqnbOgraNPfokPrr22UkrZ3VyX20q9QdhW6oPObaX++KIPvktXvu0DoCs/C/YHfQx7usNer9svOrKXzU457I+Jl73tKWqEeEOjBE057JCJl1NwIQA8lIneqBwTvUEYE33QOSb644s++N5BS5jeAoTs7VvIHTNW/a51FePL+71V84RFbZyjI3RsRwmngXcbORAFF5EG5t5tQjSk0MRUkag4E1NFQRiniakikeYSU0WlLUai7TrVwhKTwle3kltn9b6hqZtd03uiqS/ppsdevqlNFgE9FMq6n+xtMJTauo1T660aahczKwpb8HXzEbK92+/gORuocFiOShtMiklVm1qqHlY1+P9Ufgn/W8zjf48W6jPkVvpUztgstvv4+q97Siup+CjQ/DcoKnJgHvyLQUfxX2bv85qm90xH1PkwM5Qme5gZCsY/zIxEVYxA1SlRUcbGJCoSFziJikpbjER7YZJz1NOSI8cHqIE/HCPXWMJ1rNVU6Vtku/jfaWn8cqyO7GIOAmK7YP7HyfV2zTpw6U0MEoAxO8DAWq4UAysYrmJx23UKUwOXQDOrOwXScqiKpYUXxRxpec33W/JYU5XHVE2+nEox/aOYp38cLdA/lqrwvQt/7U6Vq/SPubPyNvgjKeXksWVDvnzZoCjwR2VBHmv15DGtLY91evLlnd5UYYH+Mb8oj/WM1U/93h89tu3sb73q4y/D3EHvI2S/xdvBi1erLrotWMDy78WGnm4lx4n8bPIsC98MxLZ4MKOZTCnVaX1o51uEuZbWxzcMsZScIkfCCcxTk8HCGkvH5bvIbR2zgGPDhK8ZqvnX2kYLHI+NWlPZpI6Ho/gkzOZDfdXYNKdOJPtcU2eFkzetH2Z/Y4wcscRhtbWyWjHUh4DOfUimpinrAzb9B2LMSXKnizEB4hGBib81RpIWE7toAxz7YXFSkpOjcxKw/p0y8m9iA4VS+b9dyiIsv4UXx7isuonEv4XSxJOQs9/5wed//WpQmr+9ldxsK01dq1qGyQlmlxw9Ux06LSYT3zVPYY8OjFQy8anHn4rJtw1sJ0bhlHFC0Vb6sFfScxSaFCF2Z52QKy244eNlMvH3nsfLZOJ7nsfLZOL7nsfLZOIHnsfLZOIfzN9vGRjMZOI3YQB1Ivh2LIfl5U3E3eRqxy0TlpQfPpi+bPZGtzXtoD57K9lu2zfWjwgyOQAR7bgGsFOcMKINs/D/mjeSKEl1kKQ6SFKdSVKdSVKdSVKdSRJ879aZJNWZJNVNSaqDJNWZJNWZJNVBkuogSXWQpDqTpDqTpPpAkl772G9/CLNKfnNssP1ay09prLKXfOqydSER+AgpIWcC46jZd5+MDV7HskEd4o5lgwDZsWwgKe5YNoyWGEjL6Uam7WNtyuqffdsXv4Q3PZ/ZSna4SJjP677r4ZT8x9ers/fZcaFDbs0QMMyAOBlOcsH2lIadGk+aYrj6f0XMkXYe/pjkNgP5h78ZgCz9yRjZbs2uXfUji6VZhgTpDnLroC6IiXKqo2rdiqGvnVrTWnX9vDlpO8j2IVAuxd3Q13Ga4m4YiUtx54klDmPhRmsGagyKDUv/Fj4KcPjdY+QGq2PUkSqpzX6HeSKnvc4Qbux31dqqDkthWTeYj1Zrmjgmf28me13SxlHmb5wCAM0bpyBS/I1TCC0xkBY9/WblRxJU/u3JcdSx/d0xMmmx6wHV0GlWI3buwngW9IY1F/KGNee/fUy5t4+9QkA/uIIW/mDjtKBFABmuoEUwHTGADlcVOuc6e3wncPS4ulnXFaNZ0Rt9Gu1XbRj4UN+4xBs+L675t8VxzR+McS2ADMe1YDpiAB3X8d/Hn8TjP2DWieJ8Xmut4UMBs+i2I2jysB1vWzstYzmWIHiEtk93ZFa8JQA6lLUBuE7W+oMx1gaQ4VgbTEcMoONiLdYNAUl8bAwTetEgyBP6BpZo0jtmXXPcQ4AcMHiGCEMwc6AYrydXrLW0Wr3TEbaC8kjV94URc2XECgK1MmIFknNlxAqjJ4bQY9GEORZNmLDPCN8wRgT4eho2NZ0+dKBXijRgcCB5EsjSTi84hHKcJwKU4AXFSdpBt6R546TsrZhKmOszok16oaXtHPhMojzwRA88lwT95HupBD06qPfFssvhzskczKraQ8nzf64Ugsc9VwqBZc+Vwghyz5UiUBTDKLJK8IP6XSlHJfi4vYM+EiNHl+byU7TKC8gCRoq3NLU5taS1MCa11ducMiffFEsKWeN/s26iriFXsqj9OL5hHfxL4v4lc/9KjMf4yICH/+r5773+ubHnx7ZeOTY+JjwWI7MX08Oq2u2ysybm8/RYJy972jsZG48Jb4yRYlgnByv7h8nCrcDCd8XIs0ft3eDfVs+2O3s28YZ/+ZzzJwwMfCP/E8YEvon/CcMB3ww/BfR2i/CWGKmM2tsf2mRvgcl+HipdZVPv904era4qHZX65JdmG3mq1aE2eLU69NlUq8NovFr1xBM98JzHC0k7tfDX/wxMnyfHyATmTyyuKr3e5km1t6Eb58z0o09/+m+/llwRmt5AVoSmDwlXhKY/DdGXBnebn7X49MuvBD596HKyBx9INzYbbRW8/OWZtY6hr9Osql36rOZ280nNQl/DAOIZrdPvscD/eXDrbNzE+Hw9KWw/qWtWvIlZm+KWEMTZE0QYwqLUyMVQaxN5COuuKH33aI2EtlYkVxWUrtqG9c+IhGF4cMj5AOzhe7iThSFYdrIwTII7WfDEEj2wuEIgwf1mRkIYO/hCIOEUxTCKLJN6lnrZyZQluV/7jUe3CR8fA8mF4cwb+jLYFywVIOb0qsOEhLjW2RDXOute9Ifdi/5Gwb9xLhWDLxRLxeBPhEvFEEhF9Kfi9KUHRfFo/OjLxsjOE/rKktGuNljVRJpdCxf9cXLEZMiZVm8VlC4PYYYI6EbXqimbYyGgXuRmN+xb2ujE5DvJrSpFqnUZdK3LwGuKDW9V7tzlPQ5uAp2VpAYZ+d/7lce2nX3pX3/h1VeCpfn1MSLOKfh2rQP/7THHGk+x8WpQ3TDDIy5t70i5xeiAEKFNLhNJODjLRBKBLJeJJBpdMQJd524jZbhd+RNjZB99h1bBoHfKhFKiJEnUXSoqnV7fUMEoKpG91jvOWnlNNVZUrbF5vNVuVzdaPeoBiUIoHe4gPQyYHaSHkuQO0qPQFENpLtzBv4bJTnIvYCXkHhh2mDwLhPQDWyIx8Dke9c/IxCMYrCiGE8C7v+d4VEYjE2+lFMJ577o9nIdNzSX4/4EmcB//ZlkeWFeDeXv+GJl0ETo9nytrDR2TmsCMHRn28vcGoXBHZP5g7IgsgAx3RBZMRwygw07D07TiCZ7ZOmpMD3z5F2whN1MSC+qajmcA5oZQ6bfb3YahqhhBn3aHhOM1KR6PBWMi3vD1an2fEIbH6WfJLabh+M5juWBQdiwXQo47lgunJ4bQcwVD4xs74f1XoGBhqQPqRFWBZzCBqMTNi9cj5K7BTpZvt61ESvTBCKY9x3M/i8tJd/IzN8IpTUUEJzyXqI6d+9PDRC/ad5CDnrAeZG8hNzouQ6wNxAHgeVtSm/1/3LPeFgIYJNzm/83Rv24QXBXmqq0u0YB110r2Q7FWst9390oOoiMG0XEm9I020nGa0DcaLJ/QNzp9MSr9aN13TkBY97nJith9N/2g7jthHfZ6OmOnt6LJcB4dIzfMqUZjVdF6Z/CSGDObmM+OGmRaNZXmmglS28Cg2I4FZJWATdt3s7vIdYaqNDenevoULS1vXsfu9mlm9m77aRVy1gMCBrp70gf5Hvs5A+WbD7bojb1wwKqbaBuWsnmYbl5yL4z1u7DD/GmM7J5Te0arAX4Fq31XVdY6LKdowVL0oLN3nur3Ti3bNzwUSB1/wRXyzWR318SoGeCN1kBb1NbA9FSFLbl4vC4I42YDg2umGbJ9RnPTagaSkpAUGSblDLkC3b01lcQblVwuZ9+ofGGM3GKi4TtSLEkBKrbY1jW1aZaqAYEokBsXYXZ7eIxTqy7mCyfKVl7JBOZvxCuEECqzN2H+c7ajelDgDg9CSLHDg7D2uMODCBTFMIrswZQdlCKbq+mvXoi3L8+PXXHlyx95KjY+JnyZeiWUlHlgRuP7yorR3gSyRcxAdGl+n7f5G9yiy/wNBrbM3xCSLvM3nKYYSpNLyGef0CCPsTbzTS50TCw4SKrvm1MlEIvLqRIIyXKqBBPjcqqEUhODqTmfRKYzzAw2j6dNR1h4zxjZM6c/qM9oeGg4p3a7sG3NK5v07dqlSZnXIZVvU9whlS8UO6TyJ8IdUgVSEf2pLEwK1znWqZRMMyHCFxHCa2PkSorZaVzIYmHzIXkhAwAuENX6kQWi2iBcIKoTRrRhHLE9ybjpzZgOnmR7M+8aI7fO6YaaX1kx8HZnXa3q7VYThEE3SmrPlvD0cI/3R8DknjeFQrPnTeFEuedNkaiK4VRZXUNHJLjt/aVzpkYQPn852XFSKZ44Or8Eho+CpQzN++2zZOKkukE96pKK7/kNK78kFrR4Ftm71FVxYpwQMIZ5tQOrQZicn8/Pz1RXFcMK26huKJ3iqtLSZpfIDUAZ/93Af/NkAxCFwCZnm+SAq8Nga4zeSvDAYDd0V1unFIMGHNJvh654Lud0+FNkTkcAizmnI5iOGETHGeMXNAgW4xc4TC7GL4yWGEjLlakmmWV66YlfB73052Pk9mN6u4n1m0/SwjRnaBTwqQZGBcOqWDQUzLXFCvvd6XwCexO5sai0G31MnmKj2ngIO3j5epMQCMvp/2m3/g9BPmpPnlYLgENCk4GEjtmzh+mUgimJQZRo3UQ7A8N7fvCibcIbx1BxWC9zYA9sthqsRIL/BU9aCi6WyL5zvLvNzbsbBK9mZzP2TbVW8/iOiJOeiFk7pwXe0Hljil6Y3CW3bWB99hdpOOR3x8jBAY6Zj6242mo3T7fUje7ghefTX8Q5UrNcEedIGOO0iHM04lwR58jUxWjU3YlpZUft7HePkb38FbuZzJfpgG6AdMrxTGBkr/Wd4/8hN/93Czd4ts856Z4QzEn3RuacdF9s0RubXdWmkF9S1r5q/No/vZamXzgIWybelC8Z7RltFUbdU7SGWlBXYSp0Y2oBTGi9f4m3a3E3l24Rbgps1p2d0R9y3MrOGEDMnZ0xmJoYTM37IOj7XwK36rExshNwF9QV9Tw0eKrXqbTUdpOl9HZsM7u8wZxFY+itrSdQaOIeTyxn4h4vAJa4xxOVS9zjhyt64roS99AV+kcxMP70MyClqrG4igddC1hCrrVGN+Mhi3y3DzS/nrwgzPXkicyvJz9s0RubFcs20+tk+GLZv3WYHMQQtJOgwQZBZzSd7pQ0Na9Cm1pvCngFo82T7VZMGeoXMNRx95OvJZc31XZPofHHEr6VB4IcPfDwT8LflLaDhOxLQo5KIuFLIhGVRNKXRDIqiZQviVRUEmlfEumoJDK+JDJRSWR9SWSjksj5ksgFk3iORYIlJ7VIXGOR2BKfjipZkq9wSpFJ+AqnFFU4JV/hlKIKp+QrnFJU4ZR8hVOKKpySr3BKUYVT8hVOKapwSr7CKUUVTslXOKWowin7CmdUsZB9hVOOKpyyr3DKkXvhK5xyVOGUfYVTjiqcsq9wylGFU/YVTjmqcMq+wilHFU7ZVzjlqMIp+wqnHFU4E77CGXVOE77CmYgqnAlf4UxEFc6Er3AmIg/EVzgTUYUz4SuciajCmfAVzkRU4Uz4CmciqnAmfIUzEVU4E77CmYgqnElf4Yw6IUlf4UxGFc6kr3Amowpn0lc4k1GFM+krnMnIvPAVzmRU4Uz6CmcyqnAmfYUzGVU4k77CmYwqnElf4UxGFc6Ur3BG5WbKVzhTUYUz5SucqajCmfIVzlRU4Uz5CmcqqnCmfIUzFZmdvsKZiiqcKV/hTEUVzpSvcKaiCmfKVzhTUYUz7SucUVmR9hXOdFThTPsKZzqqcKZ9hTMdVTjTvsKZjiqcaV/hTEcVzrSvcKYjz4ivcKajCmfaVzjTUYUz7Suc6ajCmfEVzqjjyPgKZyaqcGZ8hTMTVTgzvsKZiSqcGV/hzEQVzoyvcGZChHM32dEwD/65sznug+z3IeH3Ien3IeX3Ie33IeP3Iev3IefzgTsh4j74jVzyG7nkN3LJb+SS38glv5FLfiOX/EYu+Y1c9hu57Ddy2W/kst/IZb+Rc9463q7mUtab4d/71Nu3CR/dRw6b2Y4Kfa3ZVpvl83jJgnGD5XVVm2u11W5P19RBxrMEub3ao0+zzN9qcxLM1eB2RgJnMC3nkphj7oWPslRyoUgyQ0ph/rkXRUVKmEiYx+/FUZGSJpIMSD8VFSllImFCv5+OipQ2kTDb30uiImVMJEwF+NKoSFkTCfME/kxUpJyJhA97XmYiJcmhkMmNm1hZwHp5ZCxTJlIoE6+IjGUKRRqF4pUmlkYSXlimEC/NVFsYcrvcatC7KvuqkEx84MmnYvUpQTAhqw/1FUPFNMJdYbeFzZT0gr5xTG2trPawvbfHSGGUBq2r3kS6c75mU4L2Pwjty4fIfhNxodVYrfZXVmB54VMa1egBhfacYqy0NCGWrE8Rv155DSEaP00pTqMU/1zkWTDFOI1i/OrIWKYcp1GOX2Ni/WCMtC6Gl8m4k5c1k2048EEFnS7sC50GfFw1Wto5ZUWFdj80CsvT8u3kFo6zjnbyWndDNbpm4dFDZJ8fYFnrtXot1YI8Qg6GQVYxtaiwLT6dzciyfJDc5IfAZJUF6U8RDyHwlZhoM2bqgzTqg5+POs+yqQ/SqA9eFxnL1Adp1Aevj4xl6oMM6oM3RMYyd4kM7hJvjIxlbhMZ3CbeFBnLXGEZXGFvjoxlrrAMrrC3RMYyV1gGV9gjkbHMrSKDW8VbI2OZspFB2Xg0KlbClI0MysZjkbFM2cigbDweGcuUjSzKxhORsUzZyKJsPBkZy5SNLMrG2yJjmbKRRdl4e2QsUzayKBvviIxlykYWZeMXTKzPjpFzz5z2PUkxmEqrndLam9Dyh58h/XtHBP1rVv/+t9OnCXPNZHHNvCvqzCXNNZPFNfPuyFjmmsnimnlPZCxzzeRwzfxiZCxzzeRwzfxSZCxzzeRwzbw3Mpa5ZnK4Zn45Mpa5ZnK4Zt4XGctcMzlcM78SGcvUpznUp++PjGXKRg5l4wNRsVKmbORQNj4YGcuUjVxuXJ74UGQsKhuJeDwOWB+OjJUwsSTA+khkrKSJJQPWRyNjpUysBGD9amSstImVBKyPRcbKmFgpwPq4iXWaSF5YC6rSRmWHalFtFnVDw2TRlh8iT3wC0DF+0xMuWm9yZm8yQO6TF9MbK0hQnvg12hvi2xv+AfnDn/6tx7adffR133sFVvV81T5yl9/hwalmM+rZgeQ+O5BScdQt/99rAhxZJ5JsIqFq+eOoSAkTCTXLn5hIn4+RrBfWA/PVBRUj1pusnK3andGs/LUOD/MpLD/34phwpHy+h9WaXDiYKZMmS+nBv5qUZrcD24wqTNIkuObOpA1eCmOO5Bsd3yxs+P2UVl2Y9/94Rq1H4kLK5ALqvK9HZV3aREKV9z+iImVMJNR4/zMqUtZEQoX3Z1GRciYS7oV//pqAFc4JXpxh0QII/ysylimvEsrrNyJjmQIrocB+MzKWKbESSuxfRMZKmli4F/5lZCxTLiSUi7+KjGUKhoSC8a3IWKZk0AQzfx0ZyxQNCUXjbyJjmbIhoWz876hYsikbMsrG30bGMmVDRtn4dmQsUzZklI3/ExnLlA0ZZeM7JtZPkJQXVnVTazD9b9U7UHoKZupwaLLvvQg02Y3CHl/YaL0ypUhGKfq7yGMxpUhGKfr7yFimFMkoRd+LjGVKkYxS9H0T649j5NjIu4Bdl4xX30D282xXIEFKfuQd45J2hVC2JEyBT6DA/0NUZiZMgU+gwP9jZCxT4BMo8P8UGcsU+AQK/D+bWN+KkdNP18TVLMZRlkIjvxs+jSRwVp6+OY48jeb6S+D6+9fIrDXXXwLX38OvjYplrr8Err/nRcYy118C19/zX3uRemtQt+n7VG+RS9RbSVP8kyj+L4g6lqQp/kkU/xdGxjLFP4ni/6LIWKb4J1H8XxwZy7QFkmgL/JSJ9eIx8tAzs2hqQ/g9dcVgT4jIxBfM9TTqsrj4BXjJ6ylprowkroyXROa6uTKSuDJeGhnLXBlJXBk/ExnLtG/o3evLomKlTHmnl68vj4xlynsKff1XRMYy5T2Fvv4rI2OZ8p5CX/9VkbFMeU+hr/+zkbFMrZlCX//nImOZspFKA9arI2OZspFCT/41kbFM2UhlAeu1kbFM2UjhOdDPR8VKm7KRxnOg1zEsvprBw99936Pbzv7BS170L1c8NyZ8OkZuNkmZRwPlbgPzdWOumHm90++w1/DcC3cTcgCDeewYHsLaDxEjwHKv7ANgudeqdkLYLz762LazT738Y5+5Fobyka32AfAJfWUFVMVcy9RfWdylhKIjFIeGhWD6+H/GA5Ufs0+yWWqnfJdVz6IVNbHaTdceCANgag+Pn/GjK31mHRuzVC7X2L+wxoJoCWE9wcacuZxCwFkupxAgPpdTBIpiKEVnsoKA4bJkBQEAfLKCEEpiECVXUQ8qO09++ysvGcOqdmMgO5pa1DuwN2nrStdMForZIC6DVcfnuJTjGZjM937hc/TRrhceW6uujCAU65cpluCLFfqQ1wuRe8jrBcAe8nqicg95/XBFT1wXR82STx8fI3srCuzZSm91HvMOzxs6aKfepvl4/9IejXtVWgxqjUtVEgTIUpUEkuJSlYTREgNpcSn+rUqLD7/pq49vE35xjOzC0nYnWtq5fNdME24VzfBPoiEl4oFJNNj30NIIpVa301Y2hzrAlUbwA2KlEXxJcKURgmiIvjRcCVI/9bpHtwm/MUZuprUAaQrVInC92jNUZQ2T+7E6Hpcmcl5ZZoPb47LMBoOyLLMh5Lgss+H0xBB6rnX71z8AqXv5FnLwVKfXWmtdoMM8BjLbPakXFLC6mcwOrgWa7nTVtBDL67EI7x1kXx1Ran2KU1tFMrSWIBorWFjQzgMPft9QgxXTyEdNuHeoUDq28obHn3IXu/XOpOZLm8uk5gvFMqn5E+EyqQVSEf2psImIWxNBC63+/Xuf93bMb/+tGPJHeaivWq6UVQ3idFzKYhLfoawINwZguEbtA2WN2o+Ia9QBVER/KlztsaSZIgF24H2njBaYbfkVVbOqvNo1jehAL20Zy24huRVsreAWeUMrGNY0tEII8oZWOEUxjCIvQr/23o88xorpnHL679V+fU7RlJVLLrnotWP4tcTtGH5AbMfwJcHtGEE0RF8anplZ3v+NXwKd949byK3zZ/KYjXtdNbPjsMweC+pD/ZZBq+oAwx4kN1v6juW4VVjKISvTWxbWXoXcWj3X6nBUiqtq45yZ3BRDbDxAYHQmAKPMpREMJcjSCIaC8WkEI1EVI1AN7Kp7YD5dHRp/cFe9qHp01Q1GNQ5ueI5qh8IXthIZU5JbIUhzelNt0/2O/rI5p/YU2MMUPswKxGHWM+V98mKozR4n282lJnPEhIskZnZMvvSe7fRK0M8nJeyTuwdJCUduAeYuOXkxPVsn9ziSGF5Uu+JFtMtlzuVq85x92ws++zt4BPG9rWQcCdN0iYPqZeuOsphow7z9HU/F6j9B7nSCOW2Eo/1WU60q7RaoHzM5/T4nrHUoqm+odm5G4TrrtJf9m54Vx8iNlqTaZ8EmMuvJO0buCQntCfHoyati5OBwT5x0j80Xq6xPvzB6n1wthnML+/SGGJn25c5w31jn3vlDYphznbkz26WY5P3vv35i29lf+vNv/uxWkLzXbQHHD5vpqA2U2GqrR4tKlVpKW8fSwUeHzcUk2UWFVVvWWRWqLmybHfgliNTsvXaSLLboh9FhgU1M+pCe/TGyj1+83viiHz6XCNW3l2YiVP9R8IlQA+mIAXRYKl0742gi7kila3r1wre2gpWhGCAQSttUA91T6NX2z88pjUEGwkswy5pus6wq7GMUqv0OCh56fac004PLa82ZU1XhCKvDcEatd2FMXWCHBaw2l7pgObkQuATnYdRZgvMwKD7BeRSaYjhNnWQHBz6jDRFakCZH5kuH5BzHQqO3KI7aIldxPevciUATPAGOPIgb7my6hvnfG1h9LY8NaOCRF/qNc2qv1Or2qFK4qqSCc2bUTqfHiXwd2boGW6GwrUl/rB8it/GUoDM2ISRhtOp9uiXPOHY4pHS9SemKJpPREUhxdaic1UGi4bPqIBHb4qqDRKcvRqRPFXc8Q6shxpPTkjlRX3jPo9uE37wctayTCkbn9zsL6vJ8D9Pllsg1dk2cdUkev0feSa6sG50pytgrm+Y3LDvlT2f2JNlt7XEFA/SMaqDpbhLc4SB4hVmQJITeKwYbuVm86AGl08G75JMwXlVrwrZmmOT3O8jvNslPbTCsqQsMTb6VbLc+dQy9oXa7sH9fo+nalGFSC+nRQ4MRDnfBa4SX3mR9IKJfeKba4Pc5PzBrn/Ml49rnguiIAXRYyk47rPj9LwDT4+GvffJt1wlf3EL2u1aDZmAJcU1tlhRtBcupMIF+YKjInTwek28yNcUNzf5ap2ZKR62BpWAAs34wEvXZu4fK3wHt+kEhEjK3labdW2lEImfs4oxDmsoLHglPRiJ8lkz56ig/ymIUytwVBF/38R92kmmbgpU9uIe7h1WpOA+ivGkZvcdVvJr4zpZBVSNQgZ22WtNa52Aq9W63hhtbbbmtrNQ05VzNYOmDwQb9xBZyJ5OKIsKhdVUBKJ9WyB12r2C6NVbOoGt3aqart23QIzZoSV+jRTDbLfPcxhvhLhsBw+IbveIiP2QOWLaBj/V69PBBNcwrHjBWvXGmHCxtw05ulxHyBh+MtVo9YZ7c+1DeXwU/tXdM7/ZowSpPIOE2xueS3of/wg8+PBaYHNuoaINXDGVNZQeRi/qi3jmhrqtt+uOsShIuozDKRIKQHp4cYeJnl0nSbShGbUccpR2nwRGNX8zgiAbLGxzR6YtR6bdIamD7jjCN0Mj05GgT/yBJO6zeEdsSR2urQeRh5RqmAaCduyajKwxwohIeijZKK+IIrTi9kxE1FPNORkTivZOLaFEcucUVe606hhiuU6GxqclRlPDsqi3tzqFFa0kcqSX+pHNUzW+ddI6K5z7pvJh2xYtp95y9th3DjbJpQYtHJkfb52bbJOMxyKitiSO25qlKwjZYlyoJA/dRJVFaEUdoxWlyRjAAmMkZAZA3OSNSFqNQXpjkHOJE2nEDfPaFv/OHn7z6uTHhUzEi2EzAt430/A8MzNuHjzB3eoHOpsiNw3Nsf4be7pz0QkvbtWKcs8bhiR547HIpzl0uwTCwSuG80u0CD5pHVU01rDIx6y1145R2TId1+PRXKQxrkTvECwNmh3ihJLlDvCg0xVCazDehNdxSOTvc80/+9rFtwnfGrBOIUwC4YbRwrc+3lYa6qrfBs8YDMw2Mhks8UvUqoROpWa6ETiQMVkInGnGuhE5k6mI06s4brnTS4jot6/dLW8hua9LMm/UFtal2WytaYFRaQg4qnmN95zh/r5vzhwW/poXt1ofuUUzuh4E5z7YvGAYC7kIDmnsm/WjOPscOW3CIswcF0ZdCgkwOdcHuIKDumPTod9JWWo5mOSxxGMsZ3JC1o+JowbOvjxHRmtlKy+j2Km1945iu6Ua+39MbOla97V1indSUe6oOCBHanF2y7/hp9bgQcCQ7GYXsaXs/ZJXkItAVI9B16KK0ZBf0+viTWON5C7nTFgL6smVO6ZxRlXMabNw0XqLUos6AYrAtTHJGvB8gYjgSogyC74G/EVBC5ygCDecchYOzOYpAlpujaHTFCHRdc2Sugz/+IKyDv40NNNdJvad28dyanW3ChBwetin2+MJ76hYXDK9b3AQ8dYsHBdGPAit1mqUWVCrFKmybyz9jmx4/NwZmUXMZRLt6rqVYR+KBelqW0oF6mn3nBOugW7B2Ch6t8sbY0GfTGBtG440xTzzRA89pOGS4qzHhr2NkfB7A0DVQFUxSND+TxUQct5icwJsRrFPFjiCwOJNdfP0mcoPBQsK6NZ1+xyNOpW2mIxKGKcO63TMYt+sj9F6YHEaR7X0Dx+yBIw7hsHcx1KKWs/Z4H3nkrduEb1xD9syrBmZCwkpl1tUzGxMM/DXEln0MYJ3RYJyHyK0z88dqBaXXw5ccCtgLGBxTU9aVVtt0+oUtirYpt4noCamuYwlzYM4K7IhCBVXrs+oMqNZFqFpLW9YtALV5N6pZxcA7ymfdk7h7g16HPyuRjt+ND0FhW8W/y01yS0Br/a7aFPJ+TXVX9Q3N2cy998atdjJcK36j7zI3jFaFF7YAunwn2Y+Qx1orq+Xl5VajpWqNTR9OrZGbfWBZ7yXhOO36KgDUVBui1jGgy70aEzVkVjRWPUQOBDZnTc3MRTQqefNNY3zzbZLOT1h7COQzS+4h+nOfnynk/l2MHY6FQOv4NVe8pkonB/2AveS6M4CraepGrU4hh+TtHtl3JCpbRf4NUtY9e6g1hendqMsngAnDHEuQu6IA11rQcKMnbNXAF5Z3kWvNq/5aF4/RrCD/98bIHvd6yjO+t1Vy4/AUDj7u9FqLZJf3zJPdPn0mOzB6Z7G1ptJK5rrWAr4Ik8NU8AgaKXEv1wL6x16uBQDwL9dCKImBlJyx+77cZLH7vp/52P1AKqI/FRa7n+Vj9593LdnrsdMcU3pd693SG8bIAXOz4QcKO8+pfs+6tY2P74TVTVSNXRi2msLe1bLa1M8Xq4l4H12rh6TG8fj9x49fOPtgdU0WyNWgQer2Igbftf5LMTLl1RuPZqt9Y13dFO7yADc54PgSDkzZxVE+4AE8RHL2deCqmcwZphTAmpXN/oVjWjmXdLBmYfP+dEo9mvVjzUjdH4kxIzI9Gmt+O0a222EwA07ASJ7BWXtGRuKwnQ/xVyTRh2FekURHcF2RjNaSOFJL0YY0xOywIQ0hRB2SZ0tBQxpuibsGGUUkzGuQUVBc1yCjtiaO2Nr99hG/59C85OG2yWiC/oB9/uk9EE/aYiTa4O0MXn/Kl6va1FJ19VM/89G30yrL1wH8WotaKMXVFnr38cE7fNQdU/HEFE3U9d5HnorVx90IGHcdH6QJ4DB+mWIIHhhcvOKtbp94CGV2iux2ct7xCcEn3eDTZILjpgtedMGzWHHzCDcxncwMTs6fGxO+MUb2F+er1SkMKMVw9KOqTgNEW3YBe7xuAs4tDZ2LgIl3PZ4X15XGuVpjFSeFlT28kxwa9CGY8KxCJI/BByPBMO+cjN5E3b7g4xgW3oYYuQ12CGNfYzmCvjNmHJXw5CS5aUCuO69jDht0dZggr8vA48/FyO2D0MWe/dCkCyLUUDu9wdlMYjwm7yHXKrT52jKNFhautKDkHfxWT2t7ygfIDjyywMcMPcy6ZGJdy7WEFoXp01CL4v6lZP24fvYBp0Vx3+rRirqYLGOGjoAh4VZ90Hs4JVVr8YPZ7R7MNgYzZLVg1cCLGslDZ49p9y8cP73mGEn15HKj2Og/EDaSp2LkkM9IWl3E4gcz6R7MVTbY0zee5KaefSCvrR93jGfxofz8xrr0UNh4Phsjt3mPZ2ZF0w3naMbkCfdorjCB5J38WC6nlc4vUs7uu0/a0DcLitNylea1h+4DU28EOXOsz1EXzfDMyLLoPZarHc24R1KSq7luSllKOUZytnHs3Ob6hVbYSP5bjOz3GsklrZfMxYxitlWaVyqLRsE5H6litrl0YTFsFJ9xSBc3iktfK6mLGYt09nxT36jrhnOlnF2XjL7SjCBbB7zGconrJJuULmYkx++/8MCZjjb/gFOHZVvnTy8tVCPI1j5rJEVlTTWUS18gKXm/9yCuOd1qqrqZFcY9irPnU8fKmeaS6hjFUml5cb1Qvi9sFB+PDZ5em6NwL47L/RfHOD+CmHRR/V/cOHHCuHCsmHXKU/LkufRyej7CLNzq7v/wsrg8cFk8PaNIllcruQubyrJzhScf2qg+0MqFjeKTMfuOxBrF0IK4PGBBPD0jSCQ3U90LJ+dzztWQb0jVE8XQHdC5rudaDUPvrOqa+kyuiHy/2fIbyUpu/XzhbNGQHSM5Xar0V+e1QthIfj1mnxU5R+JeFWNPx6oIGsP5++6/oF4oL2nOfQ+6eV8vvzLKDu4cw/DKGHuaVkbQSAr3NUpVWZGctsjS0snlrnZOHWUHd4zkGVodQaNodBoVo1rtOefj9IXOfdIxOXTX2012WE7vhuRYC9wH2TEr3IeUY5iYNcD0hge/ZtklJX1pEh+8V/zGmx/dJvzllZyfdF+/pZp967LOgZ/0h4OzY5f5SsGt7GrZceKxQhNyiuwxb5marW5H77acszBxwtx7C4pxQl3uUYroSMtzZJ8HmqEq3QH2HZafOHAOaS6Bw4N/m+++I5rJh8iOLh02bbStbNZ6rTVV2H5KY0zJdzqqYuC7Odf0Lx1v9Er5Cw9WnMpx3bhPr6fWaESDY/o512DMzwelnMDuOtg7LKr3BDH3Fi/m5uv9bmtdpTzORODxjqqyrNK3i5g9SDWQr880N88lm2pFWW6dc26WrebcynGpHMTNLw8O8jnzMYKcZv6dyGkUM/Wi+do/tnq+cDq17tzCTy8+kJaaeiaIr+5cJJSJw7oGfr3HdQAmmckSnvexx7YJfxgjO7GJfk9dPFEtn++pWtc877pjOA5olzcwl2vRC4DlWvRE5XIt+uGKnrgLNwlXSfH0NOYaoAdObIhJK8vAh2NkF+DRPJxazxHDgqNzcyopZcbnZ+8hNzswtPam6wllZpzUJwQfqrMpOl0ObA7NrzPsIWnOitD6m195Ypvwdey63m41NudUY0Wdg262qnrfoLlT7hqemAk/cD43hSeImZvCG53PTeGLL/rgg+ix02n7PDATN/PECO8eA2nCpB8FXT+3phjnMJxL1TCl2X5nTOAubzAEGkQB7hK8gUJzh3picfLsAWDKsxcqL88+uKInrvugOssE4uUvpUkM/nSM7LdyflfwsehGlxXhpm8LquqaovVajUsM8fZ86hveKP/UNxzefOobgTD/1DcaZTEKZVfWTBq7+qotZB+gtnUFA8rM/BJVtYfB4ZhtBcMmgL8yIdaOhrV26mI4FuJYqoThCOE43DWK58uGMArcy4YQYPNlQxhJ/mVDBJpiKE0ui5T9ssEU+w9h4KZN4Jh525K9bPYg2WNx1Mz/Tc1xOZ6ieaW/+b7P0aSYuUEuBAtsAPQXAEQfzAw1gKjDLUhO5G+YLTimSeFDO4eomqGdQ7+7Qjs98UQPPOZFUL0aT0sW5178q+9Ezr3nGiqXLLmCzGc0X2QWCGqLL8bIDpNDpaWZQa4AZeKRJ2j20l3qWl1tAo1aHRNWAiNZztLr11S1N72i6yttdbqhr9VfGLPTeJv0YUyDHhAy+LuwbwjwpE4fG1pLVxAG0NZwhQPVfmeQxXpQiJNrB+fkz8CdtrAc5eA9hhmbeOv/vcN8T4zcNjxMKzjENc5HYZz1zeC+e/UmfAyR+/u+GLnLd1pOVucBwdXpx1inw/sQNKxLY/KwpSiPX8ap1rDOMdUaBsWr1ig0xXCaHjnnvTjE5Zz3AvDMOe9HSQykdJDscKrIAd41k46lM3sb2cmpRAec6IRzaVzXNNsa1/X7kMb1wBO98JxRIlEkiEWJRIHko0Si0hYj0WZpl3I0KRvY4VnTBfzk65/YdvbzL/vLL2N25W+PkRsAY0Mx1k6AYaq0W+dU+qoENolFd37PBBbT+PhjoDn3kRvRYzbURq/WBrx+p9ZAS6vWbq21ekJMxtqcnnTBKHLl06RUP/EYqy7qjePYbS+bPeQ2inzR7rbNciolwxCIPOmDfI/tQzFZ8cYWvbEd1k0yayXb/dR7v0KjT/50C7llHgxyfIB4QS2Cf09PK7SV/DLNatxWNmmy3QCgVHytK4/L8rXkCjzFrBTnhbFUHP65DbqD/9oixeP128nBkHZMHzZNriooXRoZDFTxiWc0PG5evF6KRiPjfCkaCYO9FI1GnHspGpm6GI36wg3CVWye7USob/mpJ7cJv7+F3AQE1pXGZqHfXFF7RTQvqufUjTm1ZzCP7VkkPXDNBiXJqfx07YVHEUtGa7lXVfAdXreWGN8SDTd5Cbhpb9zdZMfwwyj8sMvpZ1oy8bUrZqfcQrFXmDQfxGMsunnaRuul4ONPZ7YvfzCW7SuADJftK5iOGEDH5S5iXQ/hX8+hYqMzW1W0Jux3+WY3Pz+DM/ryMXKrzQWpVi7nUXHMW9U4UdmBHxib+O+vA2X3dXyPxxGyPKSEIGC2qo2lhZPdGW1mGVO/dAWx0GqiG5DXmtWGblDHqt5fsdM8QB+E66zz4UW9A0ImbOf/ffbYgrATs4MbsFvQp7NV9jhC2H7cTKHT2zR5INw89NOpY4uL8wvsULQreHNBuHrR6Hd7i/o5VevyDJH9GfLF/5QMSfgz5Pf+UzIk6c+QL/1nYcjzx8i0pViLWDBC69UqoJFXTNuKO9Iw8wY856tvBu58OvYfhgmcVKT9peIr/1mk4k+vIEfsyBNTKmBoCOgvFvJJcuNQ2/l+b5UlPxKOrPZ6ne6zjhxprBr6mnpOsUAVgJnqKOb5h9JpdfEM5Ih8gNw0RG9Wb2mUE+tKW9giJ1flRbLPEworZW6aTcetplkTUzr+e8pAiKkHAXZ6Wen22ptTKPes7VmPsSBVk+BdfmPxGsdBMjwndM/nB3Ka/P/tvWmYHMd1IMiqBgEoCJKNxNUo3AXiINhdrMo6W5Q86gtEN9FAq7oboDA708qqyu5OoqqyWFnVQPPHrjySqJu6aEue0cHLlGWPNbRnBc+uPZZk0zsry/x2PbYljWxJluVLltbypRnbu5L2vYjIrIy8KptA80AX+fWHyoj3Xrx4EfHiRcR7EUe8wew1SQXU5GEEdldlhuz3pusUjrMulJ5Xhc6Tk53ecr7Br5Ix2Osd+FZo5+RJOqpUq0O6BTPE47mHWDj3EA3nlgdIf5l1MzO/wkK85QNkB74GQJ8EYYe6eDwqbcboy/LyGz7/pd+JlH45SjwGHgkx8Ihj4BH3wCPbTqt1MO1OM6Kew5C4hyEZOKcjmvveIh97zlepcPiWOrK01FSXqBBGGhrpn11WQE6cBayMfQh3UwNTP46Qe5yKf6Q4EqD1v3qTaf23Rsm9Lgl0U3Jv+AL2uJtICu+KkqxTCkLPCpDFl2+yHvH+KIl3FlQBK6o/3CiGgCiRgCXVH20UiXxrC0k6TaOJqw3AgAS/obKpZxv1bKOebWTaRl3G4Q20nboYSd6m1dTHoyTpnBO7j3FmGmwEHSjOCgH7Sl/fKLPCH0dI1jkrhLSiXvwA/wM0v5Ycg/C6R4i0Cyqwgl6Een1aVYw2e4/R0ehZ/0b/5kZp9P9se8nMbHTGyotp7Tf8HjbnQ2HU7hrb2K9F26TgZH86tWCiLVDnUn/j/w8+BuzK/h3LJyMz9d4oKTi16xrK/fLHbq5Fh9cObLde9Ibfv5lXXnLSX7n86UZRLqJEAtaif7YxJRKwFv3zjSKRt0TIPa5t+qA9vIDZ5yuoUZJrnVqmfn6Lew7sdn7UWwz3FsMv9WL4v2H3/mHkRS+GX/ErXXFd623wrZIRD4PPWtmGs8A2vSij7wNRMuJh9K257JvI5vlolNzj676TXYD0MkqJ+wMP99868P1nN8jEJojGOdN7iuZvNqRonFsvnqL52w0pGqdzj6do/m5Disa5d+Mpmr/fkKJxOv94iuYfNqRo8mFE84MNKZpCGNH8940imu9HOm76OzdEje3hQe4qsfAgd7oYHuSNF/fCs78R011a7I2Y7nDiGzHh6MbD0LXfvCo2FLt5VUwTb151w8ed8PZXn1z9gL365EoWX33yxIp7YNkDfDxXWCzAx3vxJQT4+GLHfbDvJrs6L8zalqKAc0dMWJxOnSK7bS/EOmDjIqz9EgKvEcMuIfDKES8h8MONe+Pa28w1/libuTdVhDbzxIp7YE2TE/5FCUMdKByOdXObO0dOBjDhohfvRm/EepmovuC3FwCEYjHfnYKpUeuVAow+CaAR96chhq55KDwzdM0jyxm65oMd98EWHnfynij4404+W5Di407+FOK+FLqzkAliIROGhUwQCxl3AzidEmwN4PJXcDeAF3bcB9v+YpLzMJi9mORMFV9M8sKJu3FOWLG29QXb5AnQt8eE2fSkpeqAuAMybocs/nGkc6lDll8BXYwulYpRpQV/V4vRkgp/S/BXLUbLy/C3Cn+PFKMVSK9cLkZV+Fc1itFFDf6agLsIfwC/BN9LDfiDtGX8axejGsBqAKsB7YfgdxVgqvC7CnnVlWK0Brg1+K49XIzWy/AHZdb1YrQB6Q343ajBH+Q3AbcJ6Qb8awANA/gwHipGWwC3ohSjV+Df1dby89/6U3o5xVcu9iLAehFgvQiwXgTY+gjk0SiRfSLAzlf93d16UWA3fc/40y0k7RcFFtg1ege8vQPenrfzqyUSzDMOqjjSRfvfbNFg74iStF80WLAkbraIsPdFyXBwRFiwPHpRYTe7YdCLCutFhfXspJ6d9Eq1k67PIOpFhfWiwm7QAjpChoOjwoKX0b3IsFdpw38hQmSfyLAX2eIvfXTYKnld1+iw4IXAi44Q+0CUvK5rhFiXRchNFiXmtUsbpkf1IsVuemXTixTrRYp1k8jbIh4XunXb47vx0WKf2eKeF8OcNfUWyr2Fci9i7GWIGLP78o5vCE3Z8+Xt+fL2fHl7vrw9X96eL2/Pl/el8+X9a/Tl7bweY3trM5tjLyLcdJ69L4R/26F4/qL3CvY7G2UF6/Ds9RXIX21IgaT9BfLdDSmQjL9AvrdRBPLlLeS4/ZHxsmp5NPV2WHo7LDdqhyV4H0XeQ+6s087n3EPcCJ4I6+Ox+c8Rctw8DOo2sG8698T/EiHHHFqNH4S5lZpESF29MsT6H9/28+2PL/kJ6z9BTRzN6FeTm+4YT5ish/0n67/dKJO16ESR9JfI320UifyvW1wDnZ8V9ayXnvXyElkva5pBekdGLm+wgJX6P2wURfbdLeSEQ5F17j3s+YT3VNkrUJW9chdnr/THQx6PkhMOq95/tG9c73DfaeEHG2Va+KsIGXRMC6JzuNvMvZEj/KX1EX9LlAw6RkWXyt50cYQey32/9cxNd7fCuyMk7tyKLo5c747Ni/RCFFVRwV8V/fNGUUWiRAK2Y/7fjSKR/w4d1rnF6tVhb7o4+GrHUZjX2xV54RbCV15spIfTU9u34/1oo3Q8USIBdtKPN4pE7PdThO+SX73JAoAance6OkLweT7hxjzZ8A7bO10vosibSPaOlxrEIel52fM7PrVBBqfjpYYQonnnhhSNU5F7iuZdG1I0TlcdT9G8e0OKJhtGNO/ZkKLJhRHNezekaPJhRPO+DSka52LfUzSPbRTR9F5q6EV39aK7etFdveiuXnRXL7qrF9310r7UcCm6VLoUVVrwd/VStKTC3xL8VS9Fy8vwtwp/j1yKViC9cvlSVIV/VeNSdFGDvybgLsIfwC/B91ID/iBtGf/al6IawGoAqwHth+B3FWCq8LsKedWVS9Ea4Nbgu/bwpWi9DH9QZl2/FG1AegN+N2rwB/lNwG1CugH/GkDDAD6Mhy5FWwC3olyKXoF/bfFcvxL+pYZePFcvnqsXz9WL51qTQL62hZzyiefC23N6XtE9V8JeTNerNabrRxFyyiemy3Nw33RxXV+KkLu947q8ldsrO7brh1Ab79gu79bsxXfd5JN3L77LKZH/tMU14G13AfasmZ4180oMjOjFePVivHoxXj1VdhOoso2wWFufp0B6MV69GK9QEvnrSOddKM+wJ29T99Ub5/U22yM54St808V6eSz/g9Y2N12812MR99Vj/Lr5XszXK6OJejFfTol4Xavl12lvurivRuetZ78gG29B9GK/erFfL8mrvGvrljdb/FfvJYaer27PV7fnq9vz1e356vZ8dXu+ui/nSww3nefuP/YRiTcjWJItlUmiEJnaQUhnjQyG978Dq1JIlCHxY87ENCR+3JmYgcRPOBOzkPhJZ2IOEp9wJuYh8UlnYgESn3ImgvU78DQm7iS32TwOIPUZVyrW6WddqVipZ12pWKtPuVKxWj/nSsV6fdqVihX7eVcq1uwXXKlYtX/vSsW6/aIzVca6fQZTb+vYyDNTd5LbFkbKLWxVGJP9txS3S7emUmnWhZff8rGv0ab/j1FyaKapl1XDmDT0KoWFgWKfz6EfHCPEXB1nk/1yaRfZwaBVYeIHMHO1wsAkTzAbm7dMHXew6YeUt+xaMCrc+YgY80QsWNYImhTemHEvzOIAKoICDv10Msuc9peff+YLT22W/iJPEvPTI0Mzhtqu6EPTaqsJ1tzQxOKiWm4NTdYfgn+gNkNydmiGBViCEOfJntmaUq0yqIVkCtdroFuwt8gHya4aCERrVLUySGJFXVhUyqDnoM0SyaRcGiC7oUBWHi+O0XGRldeHbHp9yGbWh2x2fcjm1odsfn3IFtaH7PC6kE0l14fs+oyy1PqMstT6jLLU+oyy1PqMstT6jLLU+oyy1PqMstT6jDJ5fUaZvD6jTF6fUSavzyiT12eUyTdslA1MqxWtXfO0Pw740d0EdFNrISuvD9n0+pDNrA/Z7PqQza0P2fz6kC2sD9nhdSEr2h83juz6jLLU+oyy1PqMstT6jLLU+oyy1PqMstT6jLLU+oyy1PqMMnl9Rpm8PqNMXp9RJq/PKJPXZ5TJN2aUFcmuUW1p7bZHNjTN8I0Vnmb4lgpPM3wzhacZvo3C0wyvBsPTDK8Dw9MMrwDD0wyv/ULTXIOBEZ7mOoyjNZgW4Wmuwzhag1ERnuY6jKM1mBPhaa7DOFqDIRGe5jqMozWYEOFprsM4WoPxEJ7mOoyjNZgN4WnemHG0h+wwz5MEi0HIkP0y0n4ZGb+MrF9Gzi8j75dR8MsY9skQpgchw6/mKb+ap/xqnvKrecqv5im/mqf8ap7yq3nKr+ayX81lv5rLfjWX/Wou+9Vc6KnFfmnTcNY8If295z+1WfriDrLpjfOTY4XI1P9CbuOHoOf0yUr/RXkb2VJeVup1tSpFTsvbya1qQy8vS1vTSfafnCaDTejQSt2oaS3moqnXF65oTXWhpdVUvd1aqGnVqmaoZb1eMaQ+OZnE4fDGtlYe11XjnN6aN9TTqtJqN1Vj6n0RspPzPas2WmqtpDaBeTyu/srv/XbkJefniQjZcSaNTqhOdr4aih2JbHsYKC9wzzQpupy+0Sx+KkKk0dHiiuzk8L+5OYwRCWjWVXp0u6A3aBiitGlUPn/uJRDmcxGym3I6Chw6uf2am9tDnty+BrkdRBIvMct5J8t/uGaW8y8By78ZIQkWzTqrAvioUq9c0Sqt5fP1yUpVnWMUnVX5o/B95eLk+EtQi9+IkJOsFkXVaNdUqxqn9eZI1aUcvh6+KYD/wdGLxYmXuRKj+hVnJb6x1kpMpl6CShhk71y5MVpqMg98J9P/9xMupneS21Q6iyyg7pNuXVSqhurBaUChLRJjhcrepf7uupTaJvug1LF2SSt7F/tf16XYj0bIobN6fanYrte1+hKfgZ1lv+tDL8P8typw5jMzv9vN2Q2Qyr8iW8c1g4qi/5YbT/5NZNvpqrLUKeLUtgr7PYQ0u0o6gPRFchuS5u0IlDmz10uYeWul0VsrmUua3lro4Pjgn33na4/e+uaI9LY+cgqRL6P7eN2AhchF5bI635hbbuqtVhUjCxZbavOsrmD0AVh8JXI75/Os0q6Xl/sr8hGyb6mplNUF5nu3wLvKQhWQ1IoUzSVLg2spZUolacuvLTxa/y2lwdhailkkmY4X3NrKia+hnOIR6Xab86ycQ+/ZDH7mU8OsUaS/jpA9RcUApDmtqs6AGJtaa/WNbbWtgswHXTIv7fWFF5ysfWCYk7UfAcHJOoBC3I9C8TBe7ZtkNc4yd2Fa4Vwhxyv8kT6yo6gqlZH6amsZhaRXq6BroLIHScxy41wwPSxZFFcy3+/I76yXrHybJ2VpatjpSXlS2mYvVhqwf13UWsuz5aaq1uWrQiCIHYgFgthTxEAQJ2xchLXHIfiVzeIQfDkT4hCCaMR9aVDNkKKaISfnTM3wgy/C6u7tfeQYogHGWc1osRYYbytVW+K0XlGrgW2VQY/hgLZi+UJbFZxtdUIKx8jUvyQJQaZdMZB4LCTx/4ncK0o7FPV4OOqsHfjYKJjt8Af/19ObpZ+PkiNFtVxVtBoYg3obWDBOV9vG8mR9VClfXmrq7XoF2iDnVg5HQ2BOzZJTNqF1gYYqHY2FIDpnRcxRYYWgGu9OlakTFm+QztnVyXCGq5P/HCV3AR29WZlRmzXNwLXzxNWGxi7OwTWM0VJqDfTbHnbL63g4ZDABhuwi64oA9TseC0f6ktWFmeBC0Y6Hou2Yf3IplJ9MpZmXufxeiJL9RbXSLqtgPjRHltR6a6aqtBb1Zu28MdZog9z+Z/fEf4rElWp1Qb1aBgNvoaouKeVVMB3rFf2KsdDg+Pz2hkNkTyBA6WAwB1OT5JhN+P6AIJiDsWBSU+S4XdjBtOKBtIqSdGsqmU+cit2KQqXbZGBWfTIK87Na01fU06D1zmr1y6dBUiXo0tgFd5HtLrXZ70jm2tKpJE85leReya8g0RDwhuGGgA8B0RDwpxD3o8DUG+trw1a4wO/+xZObpV+5FQ2AekVtjlb18mVQioiN0kmQUy7p4NyVTSZN0JF6JZscx3u7QD732h5z8odPJdeIIHdHAJreLCXJYBgEG08hMcIw5Scn3yJ8BRUSw8aUTxfebU82O/M3tngGunh0CiHQxSOfBbp4IQqBLj6YcS9MW8/NoZ3Eeu5P/tpTm6X7yc4irJh1BRlXO4FXkakBciev3MJFpuT6KwNvf+KFCI0zShb4LvrzH/joO7dgnNFmcrioroCOxlBRw7gCqnxaqStLNBJ5tF0CeV+ftng04hRwWzo+OXNmwSzOcJZHFy6zyooq3R0IN97GGPRZbQmW+VLXWsDa/N5OpFKo8tFWj4Xkdepfk6Qtnik0/XhY+mUih2PfLhYo4Z5YeClOVaxVb7dKOEuJr6GUN5K7bUMpuNmAdjzWvXGLliVJB1l3mvGuNO3rEzBS8mzcfO9zYBc/ESUxvgkze2YkxXeuoXJ0zwPGyz68B8McGAiyYO3ZRKZiZLcj02dEdQaRl5LyKFlQUh75TEl5IQpKygcz7oVZ3CW9hknJ2tz5x98HEf1ylOyeVRZV82KFsdnxcdW43NIbIJ6jGNzoXLOl0wtJ86qUdPAim+b7iOpup6gGpN1jVQ2adVarqONqi+1LzylLU6+3QqnrC94giB7zQ/8Jy7bBSx588eM++PRUM2/q42c/C0L7eB855BDa3GpDpaXwWwRAejLZYnWY0jFy1Iu8AwtxLP1cOiaFwhHUeM4p1pBELloXjXjL2AGPhGOhCD9oLYR8pO9BOR6GMhv3fKmXFNbDfxElR4X2MUrN5jn1yrh+pY47jXNNbWlJbV7fhOkl6RCFCpIOAc8kHYawIOmQlONhKDNJsz26tGWa//o3QdI/2UeO2SmMK1p1dWZZM+hGHb1xxjir1TQcD3vIDpesV+R+RwaXNs3wUR1euz+hmBB2f0JhsN2fcMSF3Z/Q1OPhqAu7cNbuz5984pnN0gubyEk7jQmjNNPUazqNRa/qVyZrjSasuHDGxDXTG8kdNvt8srHcH8HzWJYGuJDCJla+2D5CDtlTwYgf06tVpWGo5k0ZUPMdNpJmNtA9TAYsuhzazA1PvUXutFEf1VtdOL4xpSbsmviI1BXephvePHXeupGmvtAFEfrAkVhX6jOWKQZ9KwTFeDeK9uV2ISVoz//kGNNndf1yuzGt4smSZtQmruKArLGg/HznZoNUsp+UToRERcSO6xYiSiERhUsaumkCfzJ+msAXw60J/In7aoJA6vFw1It7pdvNeS+XTqSGWdP90geg6X7GYc2dr89Pzi03VaVyfXOdl7HmXY5grHmDMGPNB10w1vzx4z74djWZHja79c/8OazFP91HEnYkU8uOVWFhoS1S30q9PjE7ipSMZb16nTIbccosKa2x/KkayXvKsjsqFhdba3F1UvCWfbjy4mssT5jS0sKUdq2PDNuJFVWlirvU880qGxfA5wReYtZoaoZKr+OrakZrdLUBhUGznXNv3N93HRSn3hoho54tsSYyIKT7YtfBxdsiZMy7gdbMRvzFs1GM4RlBnt3rkkmkCridnWLrI+m5PtHyLqqLarOpNseWFa0+o+DFMEAWm+gQ2ecaWXNzZ/HSQu0RsB2m9uItzQ4AnjVAdnrh+g9MT0PyzV1NeB/ufU14H3i3Ce9H2NeED6AcD0PZPtYy1lj77tthzvjVqDhnz6orav2S1phYUaptNmiZhKHRXuseVydCYvvOuL4Y7hnXn7jvjBtIPR6OOsy4t+Kp2KmYJcMsl6H0nT4SRyKt1bFltXwZb5k1dV3nqA37+0W36MbJQTs8vZVRoWvdMzDa9OZqGNLCZYrB5NhlisEw4mWK3enFu9GzX4zavTbsYtQQtRYuRg1HNx6CLig33tR2GyvL2vpbUXLYRmK+3jZQJbVUsaWDPB1SXTwdUk4rIu1UUXGpKw/Cxm03YLZx25WksHEbhma8K016HppKsvNQcwJ5vC+UjAuk3y3ZUrw7LmK6ZR5KqhumXehNd7K54/mlz4BB9gtRsne2DEKrnl9Rm7CSPN9oaTXtEVr767OTB52S3Cf5FzU1Zjk0gQj9oJBILIDIODlqE1oQlbg/FfsKOs8fpV9+y19+/MnN0qNRsp3hzdctLROgGeRUMhm0lW7mC3K7yym3HZK7UOFWX1cuu9XXjSTc6uuJFXdjsYPLnNlt3voc2BafRI2p4qszUC/odeVlenI6qi7qTfUMrNeYLZh1z43x7ojicOoCzIdTN5LicApBM96VJrMeCoL1wDdR3xyR/gBX7AKFM7A0Qo8IkMusoOVomFV/RN5Ltjc47MIyB+b7Wi6p8fuNzU/0KtwpKEBOVOxa/oJ10/MSrEepAYL1pukUrBuKuSznUKJyZ/34/r/8ObyY8x+iZEgkgIFWdCnD6zmttGhm1fJXHnV3w3vXSGXqMsn5iK4LJtT53tgaC6ta+wMuoYYoLb620gTXupTNtS6flK3u/G2cJ8R2u6w1jDGlXqZeoEnbmdjA3/z0C5HSvgCEt0QohjVxDPwtxZCCMbrPLn7Y4uziB8VnF18i4uwSRCXuT8V+e6pszi7PP/flp7Bzv2MT2ckwL6qlyToek80odSrg3cKVw2Tgne99IYJC2S3cOkwG3sXTMw5DKpVGrOfeD3Le7V2Ik1oG4N/jUUoW0t/bKUUwulgpv0RLkUKVkgf4xzxKKUD6+z3ShyH9Azx9j3iTMRn4IM8QbtI94ewpPpwJ9/N7AbD7+T1Rhfv5/XDjnrhserXOoX/0viexJ/wdnnSq9cq0DobcxAoe8ZgeDFBQjQs9eFWSCrQ9zPzuZ5/d2RA3TrrD842TEITFjZNwlONhKDOxD5ti/70/fGqz9G/pnP1wG3A0pQpSa1KnMDaJeNsyFpDsjShMud2A2ZTblaQw5YahGe9Kk84BzD84nRu2u1fnzfCUx9E3gs3VF/XmZbXJdg1PU9NGqVeq9Nx9gozYfGuCwaES0wq+M8ucvm+A74ns7L5HpG48C0d6XWDZkV43gsKRXgiK8W4U7Q4RuXQi17HDQVH8f5vJcQEf55rJpbrexL7qaJ0mOWZuuALURK3RWrVDmCE0aXY5hScMN0hPhi116lci5LVBheLGcEtptgRy4TmRE8CJCx/3ua+b83+MkNNr5hyPsmd0ozWnGJfttUiTe0aM1Xp5Lby+fFX/Cce8TutQOimFxRcOze2On+HwmeNnyLIEx8/w9OMh6bNjWe4FnE2kU2w/4HPfpMPvOxGyn7++wV4ZmdGrWllTDe78C4PulHvi2APt6oUkvEziCcFeJvFGFl4m8cWOe2OzGYCe/sjDSfvzHhlzBvh6hOxlNZld1hsNHsPU8SocIttZDc1lLfzfX3nDV7/3O5HSHWSbHQmNtEHHIhihb/GFBuLmAtoO/hUKLjnBi0npDtPKzg4noP06i/Q0j3WTb1XrQ/OzxWjb+EAE1zh/AW3JOJoZn+E37szP8vpNnp+FKp5wt+VOIpmFd9CEV8rc2f30lTIPNOGVMm+8uAde8R5sPDphJ/MpofGyrspCTZ/rI3vQAGX26GR9pqlX2uXWGbXacC01IgNvfdZrqREZeJtHOr4G8naPdHwP5FGPdHwR5B0e6fgmyDs90vFVkHd5pOO7IO/2SMeXQd7zrOdyITLw3mddy4VLU0ecNkS/dAe6VnfkJTyyJmb100fWHODCI2tu+LgD3rFN+4X3/Swqmr+NkrtmG2q5zd4iKbarqmFZcNNqTW+umv54BSFkw/IvUao2sFzSFoM58K1nmCCOB8dqDvwxh/Mxv+5xii4mDXQsT1a6Zdnbg2D9gFgQrC8JIQg2iEbclwZbjjPDN2+5S33s8WdQ5p++E0xeU+Yrqjhf4MTbxuHyxxHTZW2kyqUl4xUHpf/CL1WxcKbV5pJ6GiaGoroEeooF6UENmFPsfLMKo1Ioo6w31DEFFCo54SicvvTEK3G+znxkZnS9SuQAQLC0G3rdwDC2M9rSshmv3bWSUzDL7bPDsGRHXbtRkdYoDMlPGFJYYUgvQhhTfx8hx5xs2T0tX40tLHVt4W9EHBafWcFXR6t1r6BgkQr+V2uqHve/WhOOw/9qzeXF11qePejTpzVY0KdPphj0GUAh7kthwTLKnSz4tjsQvDsWtpNMvZmk/FgMLCEeuoQ2uS9MFXx6JJSVib0Y/bNCXheqYgHlxl9MucI+SPBg4vsgXUacuA/SnWK8G0XBtVl2zNVfiZCds42qhnffNPGiboVeQ4ILsLvdRvtub2BxC9gDgG8Be6GKW8A+uHFP3OI+aQuN30qkbNekmJ5IP8azViYBsNMX8aIVrcx3GvR2634eleTc8M/I6H59kOw/XzJoyNioYqgV3JrgVCbrWsu1gc+xpGAswVf7XqfV1w3bHsUfBMii+ANJCVH83WjFA2nZLcGc5WH8z//nM5ult0fJjgnojq3ZdqnVVNXOkvcuh9DpO+y4fLWDI5S4m8KgJBFK2Ig/5BSqE9p+NY09g11NI4AKV9M4YeMCrMMHPTPcuS4KxtjP4KFEu4HBK7ZnbUereglmnQe0anX2itYqL4Ncxkjc3HRf8MWAcYiHdn7Z4qGdLxF2aOdLRDy0C6IS96di3x7H+zI8Nkd+ECX3hJCNKRWQUdpcNrC+M5RMD6WxX+DBqW91MmSHJVgRK0iSUJQZlG5DKr1qxE97pRUJnJJZr/x3X3t6s/TFKCxNGKKBanVcaSmnYQ45oxjL00qji9teoYvbXsF5QJZwjssDUlDxU/eTu5xC9IJDQrFAQmcspdkRpB+leBAl23VD+ZS14qWy/FU8/1pZYjd62V2i2FuntqjXAT9ABOuEV2E0iTdY9ygUbzwhCsUThEeheKOLUSi++HEffCEKpWCXHfNM2Tu7Wi8zKXE3WRQ+BrjSS4ccE3QSN8V++Cj3lfDDxP2WYecsTVF/9Ch3mghC7e404YctKgA/KK4AfImICiCIStyfCtsSS5lbYijwB7/5wU88uxXE/tEoOcgQF7GcUV2/XFOal40xvY030bAtsbTbBDzcDU1wwA4GZQ7YXcgJDtjd6cW70LNfsIduip0N7lzevFFwE4kjERo1ym6yoBdAUG9uvK9pttWurIJ4/iU5YIvEtFD4TgBeqVcq4I6xgMxCEC/I5EBgGVOXzF11FubJscLRlrrQPk+2C9OhSVB6sQRtw0UR19He9Pg62jvTsY72pxD3pfCAZeCyPujLOZA5FOtSubNWh+Y9MJBaPJiaY0z+0W88uVn6fB/ZCVMO4k3WV5SqVrGmj+lOQGku2f+60uu9Ickxr1Tg+qJSraqtkTryLh4wnhRWbV7obNXmlSOu2vxw49649sCXUFyzwJdwFRQCX0JTj4ejztY6WdQWqWHLw/KrP4S1zl9Gyd1zzTZMfZULKN5xdamJV7jiLXOwclJKGqxfVzsuBT6+lcBCaCqib+VaMLlv5ZoKE30r11pafG2lsZg6+717nbfm8ZbNk05qF8AMXVwdV3Hzw76/d32u+Pc55/1TUuiSpxRrp83dOn5IWEQsfBEl624ljzYJKiMeugx7l8+khMu88JbN2VWgUOPbAbNqlV1NwiV4DtYivrdsdsUUbtnsCt1Pb9nsTlS4ZTMU1Xh3qjZX4Azeq2nbjeIik54BK3dOKd3f1NsNw7x84HRTVR9hbnJ3227gptapL/DUSftSYV8AWbEz281SXxRmlvpTFMzSQCpxfyr2+1M6N+i+6ztPbJY+EyG75pT6kobeEtBFz9c7m0ZH3P3oDrLNDi3s7tgz2O6OACrs7jhh4wIsjVjAKdt2+7S5y/hrUXJgbllrVnD5vcq3jK2lOGvb69BASacGOiQFFydYPoGQzPIJJiZYPl2pxYOpMcsnbVo+n/42TJuf7SNH+dXfKpRh0DuWR+paTcFbOWA1OVkDKmgI3eeKcMv0E7xGKgQ6Ijt2KCiyFApZ2LX18vQNQ8Tu6RsCnnn6hiEsePqGpBwPQ1mI57JuOXrsPXTR/iu3kkNz+PJBVWmp/FokQHZcrXOebEP/sBE8c2s1V0FdvZ7spj/PwvBqQynWbVbkKEK6ks/XqVMeNh5eJPIA2YpgGJlBiYVBknxKhM79GppjUfMBlMKyZlPgr/crNRwx4dTVvnfjTZTt3fhUU9i78ceP++ELPurdeec+6iEqKfqoh6McD0NZuIA0a7dZoOd+g5D91PBhYZ+z6NSN/XW23VxRVy/I0G2vbXboGjmFrqhxsrfEQ+OHYIWgDEFfL3G7VboVprNUQS6QARGmxcbGkFaR9qe02SX1oeG5h5PtpfpU+uFU+YHkG688eCkl67p8lOwtMxU1hFd3NwTqm4F6MpVG8iKQnfzYg9MlY7RSuWInPzWdOtMu1OS9RKopV4ewrkMtHYmDwKS+XLIm7yH9Na0+ZKg03JWCSH3ppCEPAI5Wd+JEZaS2s95qDK1ohtYyhpAw6IIlVYpknFmAz7Nk+QiJNfillUNlDLcV6tiXTBTkYbLXAWKv4YOVbOZN4xfOTdlqOP+m0WKzfe6MfBhQm6CXy6tDS22toorEwQ5D6YkQdtqth4up5kPqeNtGe2Z4dWri4fwlWSK3uagdIgMG7UJ+dcmTPQKAvbQ3zY4vzlyYrj9oK614dWL5ymVlFbrCvhYzzYeMdnNRKYt12ZRMpPJyjOx0AtGWi2YN+bUk5sqzd5TpujZee2Rpwi7G3IwyUU8tlgDXf3gED52pGaczDI6b0mulAIpSMEXBGHrAOfdeD+UJy5KFCdkXEArZHwsoZOq0dVCB028gnXgQHfvhbhDf7HA3sGbC4W43WvFAWsUUv5KA+sJG1Tr1hx2Zp/+MjdB/7h/lTrLLzz/zBVgW/rhA+uYfmC7cMvVt4VaCVCGZBrOrIv8WJF9c1lpqlb7vAD1TNaTnCiMlvd0CA9LA8PvZltJqG4MjpTY+EcOcMdV6WT2jtpuApZUDshJTyooyW25qjda4plT1pSBYWKTMo39jEAzzST/fUOuDI+Uyqkk2EhPUUIIFCi0Ld4wc+exybCReLreboHfmtIbJUifFrGvlNN5Ogy/fwO8ZoMx/GnTvECOyoIjEWcVo2RMGR2qNDnC90tS1SmJcaV6eWwY7LDHSbun4hU9mWNnT+Hwqq9XYMmpnOqqAYy8IZK+Dyl5BKeothRXOk/HAjN9Dqlas6/4Z8cGRRgP9y+GfsapWvoynE7Y0tpgzE2A5Db/QjmTT0SBWANbd1cSY0qzMNzD4dlwta2LeuLqiVnVouYn6Er8MuZM5UYEOVTE/1cppTa1WRvDont/w0AGlWafx1Jm3iZCBRyoXrO1BW6berNGIOTGJ0mnTDjF5flbMo4W3gK1O8hmtUgHJqlhzdDvGMpQqLRhtHnelzctl2AEBUu3kARkVmq3Cbj1GxmGpWdGosVTkpLVH7MWzfQ21Mq0Yl008FLkNog0ziEG95qgcvXKM2WX9io3HOfVqi1ZhXKuYvYHnjYDsjZZSb0FFxmA5UrOtIzyAzqJWoAPttFbXjGVb+V5A5+slHbjvRouyO6djjw2GQ0cizyLntBoWgk+uQBNcgTKpJ58tdpJpATq4z+hGq6gqBjSjFwIGm3cmOBsge8yFhtc4v8eh3Vr0oqvOpp0NBvcvxsBuAyBgX0xnKKCgMKOyRKtR1eqXE9SOvtqaVuttpuOsTS8OgG6Uk3VUOzzhzNz0WVjrGx0alk4yP7V6y5QVTWGCTbDoLvrb4DnzjQquKmEmMhMMlR7gQSONggJo4a4x16ys55yfBaUB/QiaBHsmfAgZXMXYk+brGoN2ws4bwDgtmyqryXqj3RpkPqJFM+xzZGZyEIWqg42rNxNWuj0Ru5Tt+qdBlCn8NJgTWoIGE6GWoVOMMciXLpNA6WpiBDRBRUyizaxWRldpVxWycGIywVmjWcqaj2nPPDpQeUZbqbIgCntCZ1APjs3MU7lMQ5dsN5l+HWvqhnEehqxWB222iN2bu9ZN1EpqpaJZbxgOosanL8HQ26Wa+tXVQVi6Ya+v8HivhD3eYnBcL7fxZ+J8u1WFSRmbg52lLbdbFeTbhBhrUjU3aN7QnOj0dSuJtkKz3RAS2SOhtgRzhFNFnmC3uYzCAKkn5vQ2CMadfnFZVatCOuu4Hgg8g2NcpS/fgVwTNhnSwXwa+iFvWzYk0nboi2qJz7GJB1S1oaBoeIKlE0/jFjXdr4ZOasMYPF3Vy9aYxHc66HUWIzAH1XDk3Q99qImiPKesaEtMi9yv60tVFURtgCUJA1G1eBm8v6k0lqHfJmZrut5arsMcl6AVPgs06uVVT4CgvHMwfbEpaQbPSeut8abeaKjMJjI8UUID4mZXe2kZBzJ3kBir4uqoaZjf00pda/AQIcG44vk2oUxWMGtR46uxQaog2OxPf06rMAYqCTZBQAMlqJyFPLBxaDfVYP4YaWiDlt4qqridhurLGBSMPrO7X2zCjMQ1J71ZZxAsC5s2ZoOYlgiywLxJg6+f6K1+oDowEUGpjpnTba+qWVmYZlWPp13SoWfSCbxC00wFfL9eV2nCfPHstGbUsOjJ+ln6XBUG+la1Wkd47KmK5iAa6Mxopq0Gq/3LVtc8q6yCCknAdNioKqtjMN/CF7VBzeJxVr4Ay3d9EBHoxGBAX5jTG2fRDDRlNcjfkOSGj96EhLJSPae2sDw+LAxIhMFnbSQN4n2f0A8v492fiQ7nHWU4iLH3lG1TdXRSbN2EuilT6xurkeDjzJXcrmg6V7nOvOk2dAWYp2rw70jZRnRUMbTyDIBgUCdPm5ieSEBXQq8DzYAGfEi1Y2Au05T0+wF11bDlcFGYWVx7ULvVhLKsaphSqMls6Rs3wDL6InLuaGSwBXVahQpBw9IDX552f1UvYYQd/DZjyxKstqPtVgtnCyjQovCAUlW1im5gwETCJe2z0PvxoCFhs3XpI288HxeWVKr8m3YisKthaQT9Z1GAvYgdmZoB/Fst0cYSmox+lNhD3x04+gMlQD3wWzynCP0Y5v0xWL4BzCwdPpY+xa4KHU6ARG7RwVpDlTCtNZs6fYqGT1N2SFjDeuRjuJ44rfI0AD8NurEB4NDptKtqhU852PXaDXosnzBvlzCnj2kdFIJ6GpfGFZwKsG/QJGokjmtN6HDUprEn28xEWyoD67SeNUBHwEjTmxNV2pF4iK4XHNUVYBdSAy0IIICGqb8uKs1au2GD4DYqqIkE02JzZ2cvwFSB3eycThvUvJ5o0H5p5iCfI20mkmGmIT8wIykrKlOvfJ08eL5W10r61Vm13MZYEjDnkBgYj9YSka1P7c6U9+POpTsF267Vxt4/OMODVrBF63W+Ujdo8rheU9AXaVGnn1CMypcDfLWNqZjNHKkGLa1s/kighcLsAm4wWjl0woJlZSdFCEbhoxvXjR0AbQks14mrZbYyNNPHFBqnTb/pvRB8R3iQLiJA+iuaegWA8B2fQTOTrn/FN5GaMBqr1QfqoKfZ3GFmo/FXbQH0KjYTWv309kedT+SGmWNaW8G59ByMWfkjNZyNBmdUsEiaCZhyoeKDqFtUjBLn+0kGTUF7p15W0UrDV7/x3gtnOhuv3qm8c2KmpkO3EVd2lNGOKewNZFv+dW67tP2kFv/gTLUNE6SROF1V8GlRXPSWIVVvtEFqVR11M/ugw3wGlL9STfBFLH7jxGIgJeggZR3HVoLOld5ZnMgVtUlLnwUDTwHe+RaYUqV7R+b4YysJ4SvBlgnFGaYbxDzzCw0tpt0GrfjmTk9n3cuwfozpMLtXtYaVMI5XSo9Uq8wMs5JNmtzsOKPRrtIuASN0DY3NwTe1sBawjvPJpd+Ml4sjMEnBenKwSO9OxE08U7QVWxrMXCqYmpVx7F/Vqmm7mTpuFqvcroKJhJ2MpV7AIWRb5s8blCKNcQNS1G52LmHFbPtacBYaFOercQ3d5OjqBXcfzcnDN5uF1A2yz8V21QTknKGFRaejQbbtOAorpeUWnXxMnZlgO9dz0D6dJKBg2whk93Zg0F4V0WEdAlODtd7xzuZraszDbkl705w+Blb6oHmNRILtGozBfNtJE/f2rOSLuBWo4jE4FwjO7YbOFhyJB2fHE9w8T1CrHlFrQBe0VpNuy8xOz/I2bg7Ozp5N2GdttpD3TJUHzZW5ZatCQpN3VFgq4o6KLQXW6HQiT7COOdssUxPJ1jcHwXiwbP/z56fpN2scY1lv4aepezvKBuyAptEagQ4I61RUN0FQaF5RI8YIAjsN83UZVBRdVwcSPEfvs8bFGEjFPmGbWwcBuHNaq6p2KcOaQtXK+SZXiDZQuwFmwwMI7GLYN+Ani3HCIT/bootZXwp8noaRA2aCaisd28HSYSGxu5V8VoN5a7VcVW3LL3s+/EQQPDJyJJuGly2VbfRYGbhFMqefprvHtANb3iSdX536sMNMtMH5QUUC13LMCw+fRqbqyQOKntl4pL+xraCG5FECKJ0LGh+Ng/NsoNBDDPNYxP4xyfLMh4PY0v9CAVYMRi0B2hgULQ4YDS0dMZktNZypbE5lZr4ja07D2Xm+MXhBa+KWHKzO6K42qxdWaOYBc9fT/ORtPFGvmCnWVqeZwKYU/ADLh76wZdfykH5+bmay1oBv/CjOjeGGJCqAM0pzBVfBoJF4BrMxrs5WGoPMzEuwrVK2g/lgMcF2JZjugE9Ae7Bo/cAZ2TrrcEIl/BK4Ei3dSvrmL9fwOcJ81nQqw4hL6f+JkP3z0yND46t1paaVh/BSZtDL0HZDc01NqRYio+ap8tSdZDN1UsQLez537YsRK0Huf83A5zGhnyek+/+qb+ALdhAMJvoNe0IWcH7TjpNDnOcxJcNT8v23DPwfkHDqaLumDFU4h+0Ohy3kkHl35IFO50LOgd8CNKytbF56+Zbv/pvn7nxzRPrXZA9WF80w1oeHcITDOGthTSu8poRsrtNNtv4KHqtX9KVFXa/030I/zEKQflp20H/sLLkTJA2Tc6OKJw3QUwuRqd9/gGyhSWqlvyIPkB0eB7hSJCWnyD1rOKuVoqlh+SQ5GuLIFomfIsdCHdxKkZx8FznU5fgWKe4ju0Uoc/fLM9OcCtG9RSK32c5wpb50LinfSUjnLBcpHCJ7OwljoJh0S64y1D2Hbix+AGmpLwU0j5PD3Q6FgVJGPkoOBp8HIz/HyZGup8IId5AM+J0NS9FMUo6TA2b+Ocs3ulgx7HVH6XkeI2Pr3E3uCnOYjHTuIFvNczgpOoyoB6wDR66pzAD1GoZzSha4fILsCzhQtgEeJXs6gLhp0EJwnK9sQCfJ/qAjaBtkjpwKfx5tw7sLxO5zOO3Dh8dJtQ3yIJHcx9a2/Dg0kec5tQ3mdSS51kNrnxZwn2DbAA+BRjEBYeLjloMN4PUktebzbh/6s2y7FMaYDeAYjES/s++uYOwg3Kfe7lNxG2CWJNd6mIyDYoicDHukjOA7yZ2Og2RMBcUb4ngZIfeTPT6HzJh7gAz47TVg9jayhR8441ec7GfHvCgCdEIun1UeWUWlQaspReUUug4yGGuYmVf4weKNqcV+chs/nIZFENXWO8gdtpTJSgMTd5F+5xk2Ku8Bst11kg1080lAuEM80Zb6ZDmJXpPO02qpL5NNyklyj/mC0pzewKOoiYaOxi7+hhWkdRUd3YQyZSli2LXmAJHoOpBdyswPpKRoGnXyXvPkOsEidzpvgTOd3ckHCwqMPtU63cY6g8RsJ9+IcYzsFg/UzbMtyQ4JfXnA79xdBDyCvDvP4QNp2c/lRcC7yC7Pc3oRSiZHPaGKKn3ZhS5zJysiDkzSnbN+FMwxcpCZsXQTJ4FTfqM1doZ2R0pR6itkcCI+5AXG3rngcGmZzo1ecNCDFQbF5mE7zFhTg3GgVEVqAHeXWCZf/qgOajGQu8tlASy74WQSzYJgXwQksAcIuDwSmP0S8/dLYN11h4d3AuYcJvsCfBS42vDzVGCW5KnwTgfQROkkqIDbxydn8LiS7WxI0SyOdYmuetiwmm3XagqOqAwO6jsdngXSppQMQrudbDW3ULCDwDTq9jiQLBCo6k4v9wMbxH7S7/RF8MnlJzq23H1EsjsXMH8DvJYd/qMPAdszqc8B1CIPeUNkj497g+RB0Ruc0fMoQ06QAT9nCE/y3vD+9LMksTZnCKkvB11gL9nucneQNmVRVsfI4fsV3HemI5+9abSaYEY3P1zhfd4D7DT0j3aTDjcYUh4A0+wYAwcqAu0lOzycLGB2S+Ls5u9rwWahGGC7XRsgL5WU81CNLv4Ykhc2KMt9AX4a3jiTRF67/4Y3qX9BTob16/AmAEaMw6GDTWKHurh1wOwpI7LDlwORJUJssx6shaGbgFXk7dqBBktZb6JiuSBzFRfgBOIB4XAFYbp7p5dDCPA8DDPJvgDfCegNhSTMEAcC/SLAfKGart/pHwHoaexoO738JKQoM4VEdwmGAnNXV7cJZvbdSo+tgQPg8wDZ7e0kAURhTICgApwlpEgei+3qMsHMqx0ejhMS4wUn50AXCm5I+TpScAu2k3+/2mILNDDF6S3hMLWAWXgPORbK5QIUQgG3NUI5VmDhMhlakysFtyK6uEzwNYLDcQJTj5gN4+k+IUVz0HrkREiviU4r7PX1nzBhEuTu0P4R9kr6e0mw4bbDw1cCrIdc0pZn95mA/jmMc+mJkL4TtEXBoHK7UEiRNDT1UXcGlIe6DxZJc2iitAw2JvbBkPF0umAV2eXpesEXPU4HDNZvD3TGamJy/NwIqLsC3X/XzL4PKscGY540mkd3MJSTOMXu8HCxgG6fln+CnAjp6SF50cC5rZsHiDfiMIl39wzxRr2LHLT5g/BI2XH2kHBljgVZ5VGDOr1G2EL0CNnLlWeCLsOpPBsgcpxTN8lgeaBF4Qsi86HncDZhO4zb7CdYdJcPejl3I6EbS6B/O0q309LQW0M6m4AdjEb8dpeHidSXzeCG5musU2kony5evR1OZNbx7hD9Tpg1s5/s8vQ/YTPDDobU8UPhCyTLb8QlMx5l5ISbQZ8B3ByeUVbRcjZscJxvDz8UpjsOmLlcx+j4QsgIjMelJaulod97uKSAWJKMDdEZBXTt5YtM3fK9bijoXnLKCWd+s52h0/gAXbuz8TJITgQj8CgKa3JYk08Mn+18PWO41gj0j2E09vg4uUATZ1Gz7vJ0a2Gy3x/k8gJrGRxCp8hdNqiOKNhyH9Uo35xOZ9HO7+YCwzrrnQ7vFyk6nJZvI7dSDxdo1zTAbLP7vkgsC7os6XjBmIm7gZ7oD8N6v0Ruszm/SH1pGFdT5D5vXxk8h6Y3KuBpqfk7YXlSjLZhCd+S7ATlCZJbCy10uvAic4k80JVM58IHJ0udb7ZcF2nPk9PXQxtZ9iJ7N4n5exyJoKfIPk/Q8w0FOrUIexJGhJ+3UjiZdeoS1IwvTmYhafvJLAzLN7qZQ9L2Y9mvg/uQ9evgx8mhLq5mbEW1k9wuOJJBapYuUAJcz9gC5XA3BzTQKTLG5neeFe2sgtJ0x+a4OytB5xu9bcyAuao0V2eYFimwebUD3rHppb5cBnem7/bM9aaXz+AabcB+RM/2ojozxQDZ4eGYxtbgh7u5p/GdFX93NLYjuMvTiw1W5nm6gXMw2GeN7dX3k9tsHlRs9t/t7TEF5k8WQ/tnFbp/So+IR1YUzeK6Lw3G/wlypKvPGpj9MhreTtc1KZLF1YDbgY3Pmz5ubKZtuW9WXapZDYLirU5cBRRmEGYzuIAXHG8T56l7OfS0LG5UBXvD4UnsEDkZDJSwDmf5jkaACx3vJh6OdNwI67jTQdeauNoyG87Ecznb8ZNkb5c7zLyb3GVlTqtNNOpa1AcogUOaN6ZlVPqAFvUrlFdGksrND25WVfk24YCfwx9bJknkdsGtj9Vxu8slj0HDEsDhbMfPtESXO0yElXFoXzlcR7yB3HcdHnPcCg3pN4flgZEb3nuOr1fn9MacrldLSrOzitlNtrscx/hKX0i3FjMsuokOOtrYdig0E+d0HqAN45jubCFcP3mN5YLGRh00ksspjdmxp8ixUM5pvLP7uKhhbpok1uaohki3k83MXY0tnLaTbXZPNd5fROc1VD8HyG5MpButqJixTmxF15eF6oKYXR5urLQt3D+NeqocIbu93d4kEwx0wy5PF7gOxDES83eH64DtJTu9fOCQq9vIZubXxobTHaJbnMQzQWVsd3nIWZkx0u90lrPydpNtdr85Kx2a08eFju0jb7M70knRfBq1FqQpgDFSqczpZ0ChsXHMT9zcHnS4QXQ72Wp6wKF2hqHhdIjjncvHT44PnAXu47VgcG8iSC9tdzl2Td1BCDR4c7XFHp4vSubdd51ba6UfTXs5hD0/vU4OYelXpUNYztMhrF9wCIvmC7jc9vcIo2rrFefulQrh7iUHuXvlX7S7VyFzM7t73QM90nK/gkY2N1aojeP0+vIDLrbr59stWBKpPUeyjexINtDhS2atSTdg2VGBLYe2DvOBgLye+9mr2P0s89K5nw1fp/tZqud+dh3uZ8PpkO5n2Z772cvlfibTFT91P2NqjKkut0taZtjXJa0Q0iUt1XNJezlc0g4COx2XtPN1m19XXzqVfFEua5mbwmUtF+Cyluy5rL0Yl7Xsq9BlLfuyuqzlhm+gy1o+2GUt3d1lLfeqclkrvLJc1ja2M1rB3xlNTvac0UxnNHTMi3k7o9Fp59XojfZactQj2XHOeR2ebEl/T7a9ZKfXPUhcv/g7uTEDJMjDLe3p4ZZ6qTzckmvxcDvo7+FG3e9vYh+3Qs/H7eX0cbvH18fN1Op8mY89KrxDXP66HOKydoe4zLo4xKGt1XOI6znE9RzibiaHOOoV1tUhjm47ePrDycNr84fLBfnDZQtr9YfL5V/Z/nCFkP5wqTX6w+Ve1f5whVeVPxzF5S5k1EcEHd46Wxcby2MutYE95l4dDnCZV5QDXD6sA1zmhjjA5XsOcP4OcLKHA5z8KnaAk5kDnNxzgOs5wPUc4HoOcD0HuJ4DXM8BrucA13OA6znA9Rzgeg5wPQe4ngNczwGu5wDXc4DrOcD1HOB6DnA9B7ieA1zPAa7nANdzgOs5wPUc4HoOcD0HuJ4DXM8BrucA13OA6znA9Rzgeg5wPQe4ngPcq8oBLs0c4NLcAe4928hBfHPU+bTqUGqIHwUUIlPbyVb6julCEl9XffKzX7QnyZD0lJiUhqSnxSR8YvUZMSkLST8rJuUg6VkxKQ9JnxKTCpD0c2LSMCR9WkhKJSHp58Uk5P4XxCTk/t+LScj9L4pJyP1nxCTk/j+IScj9c2IScv9LYhJy/8tiEnL/HzFpm5kkA/e2r5TwJQtfaeErI3xlha+c8JUXvgrC17D9Ky3wkhZ4SQu8pAVe0gIvaYGXtMBLWuAlLfCSFnjJCLxkBF4yAi8ZgZeMwEtG4CUj8JIReMkIvGQEXrICL1mBl6zAS1bgJSvwkhV4yQq8ZAVesgIvWYGXnMBLTuAlJ/CSE3jJCbzkBF5yAi85gZecwEtO4CUv8JIXeMkLvOQFXvICL3mBl7zAS17gJS/wkhd4KQi8FAReCgIvBYGXgsBLQeClIPBSEHgpCLwUBF6GBV6GBV6GBV6GBV6GBV6GBV6GBV6GBV6GBV6GkZf+zgvRkYEnQOMUifBA9K9FyCHv2SBpmw4smkmhDkmhDkmhDkmhDkmhDkmhDkmhDkmhDqjm7a9cO9l/Aznsw70v+w4Kvxkhx7sIYMhQ6pD+SpbD+/2aUU76zupPX3PN6s9cc83qP3vNNas/y95Jt3Wsp665OtYv9PmYGVlfht7uZuhRN0PvcDP0zmsuM+Nd11xmxruvucyM91xzmRnvveYyM953zWVmPHbNZWa8/5rLzPjANZeZ8cFrLjPjQ9dcZsaHr7nMjMevucyMn7rmMjN++prLzPiIq7Xe5m6tsz7dJ+uvBQJ64zv2kLgnuWTCav2h1Ms+nrbZm9T2JfCSEnhJCbykBF5SAi8pgZeUwEtK4CUl8NKzA62vnh1ofvXsQPOrZweaX2uzA28nrzF1XVL8TImfsviZFj8z4mdW/MyJn3nxsyB+ilylRK5SIlcpkauUyFVK5ColcpUSuUqJXKVErlIiV7LIlSxyJYtcySJXssiVLHIli1zJIleyyJUscpUWuUqLXKVFrtIiV2mRq7TIVVrkKi1ylRa5SotcZUSuMiJXGZGrjMhVRuQqI3KVEbnKiFxlRK4yIldZkausyFVW5CorcpUVucqKXGVFrrIiV1mRq6zIVU7kKidylRO5yolc5USuciJXOZGrnMhVTuQqJ3KVF7nKi1zlRa7yIld5kau8yFVe5CovcpUXucqLXBVErgoiVwWRq4LIVUHkqiByVRC5KohcFUSuCiJXwyJXwyJXwyJXwyJXwyJXwyJXwyJXwyJXwyJXw13WhaEscblniZvitH/1LPGeJd6zxHuWOP3qWeL8s2eJ9yzxniXOPnuWeM8SD2uJfzxCTnQ5oRnivh+FWxwHIx9+8kvOg5HHxSQ8WvgpMQmPFn4ak4S9/w9BSrFf2jScZO4gFn/fiHau3JhvGK2mqtSmh4dhaZAgu3lcBswVjXILDGNZTuG0QUoS6XciITwPiPOAl9zwO0k/v4SnAzkzlSJ7T6PbVOV8fcGJ0n9LSYq5CckkxlEWFz1x4i6c4jKIYpiJohhVjGK0tAh/LfhbLUbLK8Wo2ixGFy8Xo0s1+KvD38PFqKbDH/xbW4Y/SK8BTO1KMVpXi9EG/GtU4Q9gV8rw115+/te/+fRm6bOb/AQ8RHZZAhbkFfGUL4Bb8nWChxRv6aUS7+ejgnw1+AN5KSAbBeRXqsAfpJUegj+QWQnSSyDb0iMge5BdGfLKkFYGmVYApgJ5FchTl+APZK9COy2BzJfgexl+a0BPA/oPQdpD+C/AX4a8y0DnMnxXFfgrwR+0VxXatwbfNWjvGrR7Ddq6dhX+gH4dcOoN+AO4ehvaFNsV+kADeGkAbANwHwbcJvxuYnvDbwPoGsCTAWUakG4APwbQawFPLaDXAv5bQKMFfLSATgvKa0N6G2i1ocwVoHEFeLkCuFcBblXl/ebrvgPzGLmd9xt2O0h/xau/FD8mtgHIWQEeFaibArwrUHYJyi4B7yX4LgO/ZeCjDLyVgY8ywJeB5zLglKE+FfitQtssQn0W4fcipC8B/hLgLQGNJWwToLME9VqGel4G2V2G9Mv4G9IvQ3lVaI8q0K6i3IFWDXBrUF4NyqoBXA1kXwO514BGHWjXgZ864OhQXgN+NwC2ATQb8G0AvgG8GPgb6mSAXA0oowXpLaDXAv5aKF/gYwXwVoG/RwD2kStcvr8ZIf3zBvV/1A2tpTen9JKvcJ2AwjhyZrJx5EIRxpEXTtyFU9wrbYOBm0gmsplsPpG0+fM9+M0PvecHm6X3Rylz49OzIyNAfU6rqgbUIk6IqVpWZKZPnFAIY+oTBiO5YWBi40oEdMdRcttC52Ik5NgDwyEYIdMSjIjiFIwLJ+7CKQ6gYGQUTHY4k0jRKe35jz3+zGbpf0TJAQCfKOtv1GcBvuO2PNPU0W0YxHNvRzxgnsqlQ11QEMGUFUOQuiDYBHfLVNIpuK7oD5Djdin6QyKxWBdiZ8kJQb7B1OLB1JjkWZfMDyeSTPKPvQdG1Eei5oR2f6NthXqML6LEj5AtXOLQ03YSCcpwACEIlzGCSF4gQnc85pSqN06W7LOL0pGNaDEvtBzZLwjNAy/ugVfcjq63ppH1/Pc+9/RmMLIeB8kA7HyLxp/MLYN+rtyP5hpIZhfZPtNUGzS0ccGSkZhsyUWUwEmnBPZI3sVM3UcO2oXghkDkmA/y68ghQRTe2HFvbFBht/ORmktlEzLvMM984anN0luj5M4LBe6BTm9nSl+fQI47BbJL2gH9ttIuq/ZSpvJW49YXPPIRMeaJWCAHOqLwwYx7YdoUVi6VMYfNp74Pw+b3I2T7hcK0slRVV2ZbPEbp+uSwzykHIm01i4Bxtt2qvJmIILEOSJxInWraYeIWTPEgVihDKzRsVehDf//M5icitzwRiUj/Wx/ZdqFwTp9rN0v6olKnSuBAh/VODtqnaTBRcZ11iAbkcRAvgIMk5qq+PV8QjykRZWrYKZGTUuxCYbaqX6GX2Cw1lZrR4UgiFwrm76kJctQmLz8UILk/FkBy6jS5yy7TIDrxIDrHyA4bOza0bTE718fJTntxdri4DY7pq5S5KPzcN6m+eidYFU4WCg7ZmxMokz3dpgxoG5rf1ZpwlilYE85MZk24UARrwgsn7sKhQkim7UJ48NHvf/IXt4Ao/k2U3HGhgPEsk/WqhpcE3HgVhSEphqHWShiRwgoRVJRHPlNRXoiCivLBjHthMhWVpiM6mzNHNLWpvg3z1wWo1uIqXtqp12oaxidjp7hl6p8itqldvpNswWsu8LqFTa1mW5UHSP9yq9VYMOhlPQsYScRzDpBdGkZtVqATKcZCXb2yoILIVnn2YTKgGQv6itpsahjsutA21OaCskQvjKIQd5DNNXq/E/+GssF0bi1olU4JxrLerlYW2jRMbWGZXwXJsm8jfe1mlX/EiAQfC1Bkuw6TV3mZRg/SvNIen9rDWNxqXjQKNgvMvt5g9g5ApZwsoJTTGUtvvvvpJzdLz/Wx6KR2axlvl5is0ysuMVgWFyYXSIyLGa/vO6degWQ2uNK4sS/vI/0lBr8Ak8hCDQW9BYUKX6W9vpSn3kwO2ejSSw/Yzcl24oc9iG9rIOzCFQocVMK9pF9QBpRkaa/ki2CTlzL1L8hhYSB4IECX3hvzpfYGckQcED4U4n4U7IuNQl4wef8kQqvNw48NftPIGW18ZhKGxqB7LbnXF95ZUS8Yq6KeBJwV9aMQ96NA48aSw0LcGKjAj0ZpDBxYNRjUO9+ssgBWpgpz7joeJUeKqlLFWE07MPA2gfF2jaZmqFOz5JRV267QwPXRWAiic+SejgRCUY13p8oW4Sna/DmZLcLNW+UfJDG8IXFubIjqdHWyrPKuDssB6AE7Sb84T4KNUike7qiAFCjamKV2U7x3gdCfjpC7TdJqOT2j1pXq6iMqBmiPq1Vl1WD331RnlhUDb7Lf7ygpkyr0ywN/9OwLkbdEIlMHyHZTUdmzv86zxb1ClnuLw3JPyZ1+/+Dvfu27n90GXH4kQvZyLkevqENndcMYBX4q8HFBhvrfS46e01uwMBjBIGuMVZysj1Sbr0W1Omjn5CsdRrkAhewv82z7sjqAP/SAiZBBG2dFvdQ2WnidVHtpGS8eNFpaDW8PMgNmgVkQocpEyPm7kIKiv/RhVvQecpvFGc347Q978LSdjyDGyz98D9T6NyPkCOdkcmYlZ934Z+jVNrtU+6qKxX84Qh6L8DYcpNkr6myrXcdLq+tKDW9lOq3UtOoq445DgCHnB0Fvqmiy++qwZH7xPMscx2sUDJisJhsrORoHu6jA+p5lsqGMbyLQvsqX+7kU66vU6M/LKauvPhWhEbxYw2KrhZfvs6bnlRlKJoaTg3ISqAya5mBhYXZuZPTsRH9k4PsfZHI8SvY4bEYb0N9woH1kj9BTbUAl1iEYr4VUIp9njfAzf/4UdogPRKyxijH7o228pmRWe0QdXW1R+e8mJJVOJfMytK/cTwbe8glWJKTLOTmVyfD0n/yER4fAjB9/3NUhTrO5gymPTNqcO/73//EksvSvyGEbRzMK3m5inK+zkGK2hga+JHKHObJNFVs8AjWFboY1heaQ0x0VIg9bzTJK4pz8hRlYZdfptSxldU7FJweU6lm8IBArvpVskrFLV/CAKJ+z9i7+5Oc2S9+PkruQSAtvKy9pdbXC++9IveLg8zqM4rzTKD4uhSp16k1kyD5ndkVA0rFwpC+RhDCbhqIdD0XbbmmnLUv7W39KFdenomSAEZmoo7XcHDFW6/z39Un5HqeUY5JvSVMjljFhStYNhCRi/iSg/zkl6E0j7kvDvtsoZ01JffWHz6CkPh8hdzJEvIxQr+s1FNBdboNkuwsOtNOAo3pWHnC0PeZCSForUasyAkbcicFmeWuM2mb5bGeW/9Uo2cnw8KIzo6jiYyCTlSrU4x7bsmrgF3/6hUhptzcsqpx7bHunA5+hwJIvsNApTjg7hQ/i1LC1rjQlJgIgaswb9bXW7qMlOzdu3BNX2DdLOTrAoxGyjyHN141lfHGj0VTL9CkMJEIt8NdwIV5IQyEHAuHxVJ9LEaHxk8sJPiswu3DjuHPywHWt9MEonQDnJ8egz9JbEMEiO+6wyNJJsBpK/U5IhHMsjyic5IQTjhSOOJvNDT9E9tgbzJaF4DEneMIaEayRHPBxB7x9ZGZls2H+6ktPbX7wid/5p7/bKv19lNxOUWAFNdtqao2ADSSYdakLmd8Gkpkv9Ny0UwRxafvkzJkFoVBJ5AGQYpZQXMBAY0fMTWMqYx0hgGg8seIeWPeQ3aL8bQh3xhx8DVqNZUrfDh0XoXGehoUon6cf/9FPbX7w89/+9a9tZTpx28Vl6Mzn1CtncAxEpt4eITtMWVdU02yCdYh8ihC1voCPXOCeyf7ZN50tr9T14plke6k+lX44VX4gWRw586bTq2UD7ztsNMGSNK/QgrFYGiSnztDb18GqnGuC+YI3A7ebK+oq3so8rhqXW3rD5KZ4V+dSEpkfkMjRiirfWlGHxs7Qf8YnoAqfDa6CWr9RVUgmMmuswl6PKtyq1ofmZ4HvXw7m+yHlxvFdWCPfu71E/5ACTP9lBNb2Wj2VnK8rLbYZx0dYAWcV11Q64Ac+9XrrlAi6uycI9OSBmB/6T1i7HzgAfPHjPvhsts2jOpKz2UQ65tqxkf45qKoj5Ki1QJ6oqc0lvL72Abzf64rWKi/ztSgKQHqFCmC/bZOqwHYpMqypPxqJSFfJAV6/2XYDr5Yz2Ltl9LYv9D2LTN3XBYRV3juvOLD1+Y/82X+9FV98gG62GR/R4FskvxUlsUtqUx/TG6ugwPh+EyxsK6tQE5ijZdceCTq6PfaRF+hBsRsVTRjZMWcynPdTHMkHp+vhsRtNODx2Z7PDYw804fDYGy/ugWc3dpKWsfNvn6Drgt+OkoPzk0xS7LI+erEhnYyhsPsb7es7mEg5JXJY6lLe1LTlWFBfCAZFcrFu5M6Rk7Zz5q704l3oObYD5AwT50/+2lOb47vZeJbTQ6lCMpXMJTJJ+lSJtDXSX4lt3RqRNm29pX/H4QgoSmlrlKZFIW1n/wme1ifA9Z2MyPu3brLDwa8++HWy/z6aG9/62CdfsBOPcNBof99hzDp5y/8P5V/I+L51BQA=", + "variations_safe_seed_date": "13326833898000000", + "variations_safe_seed_fetch_time": "13326834056751624", + "variations_safe_seed_locale": "en-US", + "variations_safe_seed_milestone": 111, + "variations_safe_seed_permanent_consistency_country": "us", + "variations_safe_seed_session_consistency_country": "us", + "variations_safe_seed_signature": "MEQCIHKH9neEUW8fzXhEasmV9flyr/G+kBumDdGIHCJowK1HAiBmAakAnQYFjqzj7gM2H0ZVRq3TCXZaVsYsbNxwr1KTPw==", + "variations_seed_date": "13326834056000000", + "variations_seed_milestone": 111, + "variations_seed_signature": "MEQCIHKH9neEUW8fzXhEasmV9flyr/G+kBumDdGIHCJowK1HAiBmAakAnQYFjqzj7gM2H0ZVRq3TCXZaVsYsbNxwr1KTPw==", + "was": { + "restarted": false + } +} \ No newline at end of file diff --git a/libs/nemesis_dpapi/tests/fixtures/chrome/v133before/C/DPAPIUser/AppData/Roaming/Microsoft/Protect/S-1-5-21-937929760-3187473010-80948926-2115/ab998260-e99d-4871-8f4b-d922b2848ce6 b/libs/nemesis_dpapi/tests/fixtures/chrome/v133before/C/DPAPIUser/AppData/Roaming/Microsoft/Protect/S-1-5-21-937929760-3187473010-80948926-2115/ab998260-e99d-4871-8f4b-d922b2848ce6 new file mode 100644 index 0000000..742b808 Binary files /dev/null and b/libs/nemesis_dpapi/tests/fixtures/chrome/v133before/C/DPAPIUser/AppData/Roaming/Microsoft/Protect/S-1-5-21-937929760-3187473010-80948926-2115/ab998260-e99d-4871-8f4b-d922b2848ce6 differ diff --git a/libs/nemesis_dpapi/tests/fixtures/chrome/v137after/C/ProgramData/Microsoft/Crypto/SystemKeys/7096db7aeb75c0d3497ecd56d355a695_f26c165b-53c8-414e-8abb-ec5f0f52df22 b/libs/nemesis_dpapi/tests/fixtures/chrome/v137after/C/ProgramData/Microsoft/Crypto/SystemKeys/7096db7aeb75c0d3497ecd56d355a695_f26c165b-53c8-414e-8abb-ec5f0f52df22 new file mode 100644 index 0000000..67ecba9 Binary files /dev/null and b/libs/nemesis_dpapi/tests/fixtures/chrome/v137after/C/ProgramData/Microsoft/Crypto/SystemKeys/7096db7aeb75c0d3497ecd56d355a695_f26c165b-53c8-414e-8abb-ec5f0f52df22 differ diff --git a/libs/nemesis_dpapi/tests/fixtures/chrome/v137after/C/Users/itadmin/AppData/Local/Google/Chrome/User Data/Default/Cookies/Cookies b/libs/nemesis_dpapi/tests/fixtures/chrome/v137after/C/Users/itadmin/AppData/Local/Google/Chrome/User Data/Default/Cookies/Cookies new file mode 100644 index 0000000..31be42e Binary files /dev/null and b/libs/nemesis_dpapi/tests/fixtures/chrome/v137after/C/Users/itadmin/AppData/Local/Google/Chrome/User Data/Default/Cookies/Cookies differ diff --git a/libs/nemesis_dpapi/tests/fixtures/chrome/v137after/C/Users/itadmin/AppData/Local/Google/Chrome/User Data/Default/History b/libs/nemesis_dpapi/tests/fixtures/chrome/v137after/C/Users/itadmin/AppData/Local/Google/Chrome/User Data/Default/History new file mode 100644 index 0000000..22c46f0 Binary files /dev/null and b/libs/nemesis_dpapi/tests/fixtures/chrome/v137after/C/Users/itadmin/AppData/Local/Google/Chrome/User Data/Default/History differ diff --git a/libs/nemesis_dpapi/tests/fixtures/chrome/v137after/C/Users/itadmin/AppData/Local/Google/Chrome/User Data/Default/Local Storage/leveldb/CURRENT b/libs/nemesis_dpapi/tests/fixtures/chrome/v137after/C/Users/itadmin/AppData/Local/Google/Chrome/User Data/Default/Local Storage/leveldb/CURRENT new file mode 100644 index 0000000..7ed683d --- /dev/null +++ b/libs/nemesis_dpapi/tests/fixtures/chrome/v137after/C/Users/itadmin/AppData/Local/Google/Chrome/User Data/Default/Local Storage/leveldb/CURRENT @@ -0,0 +1 @@ +MANIFEST-000001 diff --git a/libs/nemesis_dpapi/tests/fixtures/chrome/v137after/C/Users/itadmin/AppData/Local/Google/Chrome/User Data/Default/Local Storage/leveldb/MANIFEST-000001 b/libs/nemesis_dpapi/tests/fixtures/chrome/v137after/C/Users/itadmin/AppData/Local/Google/Chrome/User Data/Default/Local Storage/leveldb/MANIFEST-000001 new file mode 100644 index 0000000..18e5cab Binary files /dev/null and b/libs/nemesis_dpapi/tests/fixtures/chrome/v137after/C/Users/itadmin/AppData/Local/Google/Chrome/User Data/Default/Local Storage/leveldb/MANIFEST-000001 differ diff --git a/libs/nemesis_dpapi/tests/fixtures/chrome/v137after/C/Users/itadmin/AppData/Local/Google/Chrome/User Data/Default/Login Data b/libs/nemesis_dpapi/tests/fixtures/chrome/v137after/C/Users/itadmin/AppData/Local/Google/Chrome/User Data/Default/Login Data new file mode 100644 index 0000000..0fca81c Binary files /dev/null and b/libs/nemesis_dpapi/tests/fixtures/chrome/v137after/C/Users/itadmin/AppData/Local/Google/Chrome/User Data/Default/Login Data differ diff --git a/libs/nemesis_dpapi/tests/fixtures/chrome/v137after/C/Users/itadmin/AppData/Local/Google/Chrome/User Data/Local State b/libs/nemesis_dpapi/tests/fixtures/chrome/v137after/C/Users/itadmin/AppData/Local/Google/Chrome/User Data/Local State new file mode 100644 index 0000000..77712f4 --- /dev/null +++ b/libs/nemesis_dpapi/tests/fixtures/chrome/v137after/C/Users/itadmin/AppData/Local/Google/Chrome/User Data/Local State @@ -0,0 +1 @@ +{"autofill":{"ablation_seed":"mCbKXca84TQ=","states_data_dir":"C:\\Users\\itadmin\\AppData\\Local\\Google\\Chrome\\User Data\\AutofillStates\\2025.6.13.84507"},"breadcrumbs":{"enabled":false,"enabled_time":"13404954528187220"},"browser":{"first_run_finished":true,"last_whats_new_version":141,"shortcut_migration_version":"141.0.7390.108"},"hardware_acceleration_mode_previous":true,"intl":{"app_locale":"en"},"legacy":{"profile":{"name":{"migrated":true}}},"local":{"password_hash_data_list":[]},"management":{"platform":{"azure_active_directory":0,"enterprise_mdm_win":0}},"network_time":{"network_time_mapping":{"local":1.760480928928356e+12,"network":1.760480929e+12,"ticks":10980518781.0,"uncertainty":2276295.0}},"optimization_guide":{"model_cache_key_mapping":{"13E6DC4029A1E4B4C1":"4F40902F3B6AE19A","15E6DC4029A1E4B4C1":"4F40902F3B6AE19A","20E6DC4029A1E4B4C1":"4F40902F3B6AE19A","24E6DC4029A1E4B4C1":"E6DC4029A1E4B4C1","25E6DC4029A1E4B4C1":"4F40902F3B6AE19A","26E6DC4029A1E4B4C1":"4F40902F3B6AE19A","2E6DC4029A1E4B4C1":"4F40902F3B6AE19A","45E6DC4029A1E4B4C1":"4F40902F3B6AE19A","9E6DC4029A1E4B4C1":"4F40902F3B6AE19A"},"model_execution":{"last_usage_by_feature":{}},"model_store_metadata":{"13":{"4F40902F3B6AE19A":{"et":"13407546539033521","kbvd":false,"mbd":"13\\E6DC4029A1E4B4C1\\1F0B860C470E4D4A","v":"1673999601"}},"15":{"4F40902F3B6AE19A":{"et":"13407546539085787","kbvd":true,"mbd":"15\\E6DC4029A1E4B4C1\\C3E707B086FA9E2E","v":"5"}},"2":{"4F40902F3B6AE19A":{"et":"13407546538914893","kbvd":true,"mbd":"2\\E6DC4029A1E4B4C1\\A14E3CA7526A111C","v":"1679317318"}},"20":{"4F40902F3B6AE19A":{"et":"13407546539086380","kbvd":false,"mbd":"20\\E6DC4029A1E4B4C1\\D6506988A3C3B0BC","v":"1745311339"}},"24":{"E6DC4029A1E4B4C1":{"et":"13407546539679014","kbvd":false,"mbd":"24\\E6DC4029A1E4B4C1\\CC9B28BB59B08A84","v":"1728324084"}},"25":{"4F40902F3B6AE19A":{"et":"13407546539595201","kbvd":false,"mbd":"25\\E6DC4029A1E4B4C1\\875AB2461F0D1602","v":"1758035051"}},"26":{"4F40902F3B6AE19A":{"et":"13417050539669480","kbvd":false,"mbd":"26\\E6DC4029A1E4B4C1\\A1F0154682A8217E","v":"1696268326"}},"45":{"4F40902F3B6AE19A":{"et":"13407546539671761","kbvd":false,"mbd":"45\\E6DC4029A1E4B4C1\\54A5FC5557982A33","v":"240731042075"}},"9":{"4F40902F3B6AE19A":{"et":"13407546538734432","kbvd":false,"mbd":"9\\E6DC4029A1E4B4C1\\04DFB46BBC977666","v":"1745312779"}}},"on_device":{"last_version":"141.0.7390.108","model_crash_count":0,"performance_class":7,"performance_class_version":"141.0.7390.108"},"predictionmodelfetcher":{"last_fetch_attempt":"13404954538215724","last_fetch_success":"13404954538282761"}},"os_crypt":{"app_bound_encrypted_key":"QVBQQgEAAADQjJ3fARXREYx6AMBPwpfrAQAAAMGQEfs9EvhFlfQyruKP4usQAAAAHAAAAEcAbwBvAGcAbABlACAAQwBoAHIAbwBtAGUAAAAQZgAAAAEAACAAAABYA1bZ4wwHhvtKvWYjN0jQOz/fnDjq+YQwqSz4YQKH3AAAAAAOgAAAAAIAACAAAADHIVrp8RsE/Vo+zjohkKETNYxXvFWU+p4XHUi0Ng3w5JABAABZN2GlsXp8I7ZG7IH1fLUonX3By5uWPbqwcxCrqGq2rGP4OkQic0yHEX5e1j/L0IOJ1bwVDmFOGdxej+BXjBQ6bLSNISrk7uo0tk/cHY5+Zd5y8TPgyKnyzBrlNhYIAmQ3ul4mlW3BoyoYxIrHYP6Rx/er6yYV08Na/2mu1itHT82lUc00lMX6ZL17OwFuCDS4OjMDtBHCkmJ58HU3B6N8ViAmOcg/YmgvUNLL0CLVljCBgMZzNbZUEGw8TEybJj9UloPCg7jBztNNLCMVkjWMmPwsHRibr1k9Q7IA1rA0DKwLFuCMf/+2SyO0TeyM++wHqp20VnHPkE2RFTUd3GNv9xEZayi+g4euMU+J22CkwAhQR2Zebrs8w9JaqZB28fARIdikxvTI4OBh6kmyRtw7VTQwUXd+nthD+pzp0gG/Y23pXOzTamWS5verSqgd14tZJKgX8fENTS0QfQ3QBCSo5qyv7JSAx+eEPI7vlF6xi3mlYbBVKBpdIBM/jfW1fAwh0u+AIwX1Q9S2e6L8n444QAAAAL5Wd8RHU7r6RT2ZOSLGg3yxEZpHc7kzaXoAg5uMbLyOuIPecy+c1aoa1uvMftDtYUglzc36KKrjWy36m7PLmEo=","audit_enabled":true,"encrypted_key":"RFBBUEkBAAAA0Iyd3wEV0RGMegDAT8KX6wEAAABPaZPtbVriRrghIZ8sDs1NEAAAABwAAABHAG8AbwBnAGwAZQAgAEMAaAByAG8AbQBlAAAAEGYAAAABAAAgAAAArxTP+qZmoiOBnBPbV25KHdOtaviR2Wd0H3uiseXANf4AAAAADoAAAAACAAAgAAAAGz68ok/PFg/D/MHVShShglA8eJbU36sPViEmb227lwUwAAAAoLnTPxvFhW4dITQgGiS4LNdYBFMQkpOdwEVqQnXLpRiiZVs4qLFM1T2IIr4hFdcRQAAAALiIzcjnnN4ThXUAq05ff9ChkK6i2kp0u1PFKTZ55XjgFoygKgzfBpSCdE0vVZW5B5mRcA8ABeBhLUvd15Nk+gs="},"os_update_handler_enabled":true,"password_manager":{"is_biometric_avaliable":false},"performance_intervention":{"last_daily_sample":"13404954528269366"},"policy":{"last_statistics_update":"13404954528186516"},"privacy_budget":{"meta_experiment_activation_salt":0.09676747313544709},"profile":{"info_cache":{"Default":{"active_time":1760480945.457267,"avatar_icon":"chrome://theme/IDR_PROFILE_AVATAR_26","background_apps":false,"default_avatar_fill_color":-2890755,"default_avatar_stroke_color":-16166200,"force_signin_profile_locked":false,"gaia_id":"","is_consented_primary_account":false,"is_ephemeral":false,"is_using_default_avatar":true,"is_using_default_name":true,"managed_user_id":"","metrics_bucket_index":1,"name":"Your Chrome","profile_color_seed":-16033840,"profile_highlight_color":-2890755,"shortcut_name":"Your Chrome","signin.with_credential_provider":false,"user_name":""}},"last_active_profiles":[],"metrics":{"next_bucket_index":2},"profile_counts_reported":"13404954528168422","profiles_order":["Default"]},"profile_network_context_service":{"http_cache_finch_experiment_groups":"None None None None"},"session_id_generator_last_value":"1724037180","signin":{"active_accounts_last_emitted":"13404954528151043"},"subresource_filter":{"ruleset_version":{"checksum":1224776413,"content":"9.61.0","format":37}},"tab_stats":{"discards_external":0,"discards_frozen":0,"discards_proactive":0,"discards_suggested":0,"discards_urgent":0,"last_daily_sample":"13404954528179243","max_tabs_per_window":3,"reloads_external":0,"reloads_frozen":0,"reloads_proactive":0,"reloads_suggested":0,"reloads_urgent":0,"total_tab_count_max":3,"window_count_max":1},"toast":{"non_milestone_update_toast_version":"141.0.7390.108"},"ukm":{"persisted_logs":[]},"uninstall_metrics":{"installation_date2":"1760480928"},"updateclientdata":{"apps":{"bjbcblmdcnggnibecjikpoljcgkbgphl":{"cohort":"1:2t4f:","cohortname":"Stable","dlrc":6861,"installdate":6861,"pf":"1b594296-139a-4fed-ac5b-1482fc7574d7"},"eeigpngbgcognadeebkilcpcaedhellh":{"cohort":"1:w59:","cohortname":"Auto","dlrc":6861,"fp":"","installdate":6861,"max_pv":"0.0.0.0","pf":"0b83580f-caf5-4917-8361-9f1feaaf1e93","pv":"2025.6.13.84507"},"efniojlnjndmcbiieegkicadnoecjjef":{"cohort":"1:18ql:","cohortname":"Auto Stage3","dlrc":6861,"fp":"","installdate":6861,"max_pv":"0.0.0.0","pf":"ea1be4ef-276f-4a99-8ded-2af5145ca1fa","pv":"1457"},"gcmjkmgdlgnkkcocmoeiminaijmmjnii":{"cohort":"1:bm1/3arf/3arl:","cohortname":"Control","dlrc":6861,"fp":"","installdate":6861,"max_pv":"0.0.0.0","pf":"e695c118-bf80-483e-aa4c-64cec44dddaa","pv":"9.61.0"},"ggkkehgbnfjpeggfpleeakpidbkibbmn":{"cohort":"1:ut9/1a0f:","cohortname":"M108 and Above","dlrc":6861,"fp":"","installdate":6861,"max_pv":"0.0.0.0","pf":"64f8a080-da34-4284-afe1-aa8d454483b4","pv":"2025.10.14.53"},"giekcmmlnklenlaomppkphknjmnnpneh":{"cohort":"1:j5l:","cohortname":"Auto","dlrc":6861,"fp":"","installdate":6861,"max_pv":"0.0.0.0","pf":"887bd2cd-5eb0-4fc7-b6ad-49835975547e","pv":"7"},"gonpemdgkjcecdgbnaabipppbmgfggbe":{"cohort":"1:z1x:","cohortname":"Auto","dlrc":6861,"installdate":6861,"pf":"8a40a0e2-f045-42b2-be7c-b2cefbc020d8"},"hajigopbbjhghbfimgkfmpenfkclmohk":{"cohort":"1:2tdl:","cohortname":"Stable","dlrc":6861,"installdate":6861,"pf":"1f8d4a68-7c5e-4b9d-8c68-834d3ad8978e"},"hfnkpimlhhgieaddgfemjhofmfblmnib":{"cohort":"1:287f:","cohortname":"Auto full","dlrc":6861,"fp":"","installdate":6861,"max_pv":"0.0.0.0","pf":"873ea39a-c1ae-4889-bc67-8c4e4fdc0073","pv":"10092"},"ihnlcenocehgdaegdmhbidjhnhdchfmm":{"cohort":"1::","cohortname":"","dlrc":6861,"installdate":6861,"pf":"1a658ce1-7994-4b7b-a0d3-7043ff532312"},"jamhcnnkihinmdlkakkaopbjbbcngflc":{"cohort":"1:wvr:","cohortname":"Auto","dlrc":6861,"fp":"","installdate":6861,"max_pv":"0.0.0.0","pf":"070a38c5-1315-43f6-9f92-c5bd0f729b5c","pv":"120.0.6050.0"},"jflhchccmppkfebkiaminageehmchikm":{"cohort":"1:26yf:","cohortname":"Stable","dlrc":6861,"installdate":6861,"pf":"5309eec6-1956-4c1c-b1f3-ae2f076cfb5d"},"jflookgnkcckhobaglndicnbbgbonegd":{"cohort":"1:s7x:","cohortname":"Auto","dlrc":6861,"fp":"","installdate":6861,"max_pv":"0.0.0.0","pf":"44bbc6d1-642b-48e4-ade6-1b99eb66699b","pv":"3085"},"khaoiebndkojlmppeemjhbpbandiljpe":{"cohort":"1:cux:","cohortname":"Auto","dlrc":6861,"fp":"","installdate":6861,"max_pv":"0.0.0.0","pf":"96c8d65d-4684-4010-ae79-beaf67838160","pv":"67"},"kiabhabjdbkjdpjbpigfodbdjmbglcoo":{"cohort":"1:v3l:","cohortname":"Auto","dlrc":6861,"fp":"","installdate":6861,"max_pv":"0.0.0.0","pf":"8e24936e-d378-4210-ae92-fbf2f204cdd8","pv":"2025.9.29.1"},"laoigpblnllgcgjnjnllmfolckpjlhki":{"cohort":"1:10zr:","cohortname":"Auto","dlrc":6861,"fp":"","installdate":6861,"max_pv":"1.0.7.1652906823","pf":"32bead8a-5c11-4e73-a181-ad347f54cbc8","pv":"1.0.7.1744928549"},"ldfkbgjbencjpgjfleiooeldhjdapggh":{"cohort":"1:2v8l:","cohortname":"Auto","dlrc":6861,"installdate":6861,"pf":"07fa07b5-3a42-4214-aeb2-e80f1888f999"},"llkgjffcdpffmhiakmfcdcblohccpfmo":{"cohort":"1::","cohortname":"","dlrc":6861,"installdate":6861,"pf":"5995c37a-1bc4-42e3-9e5a-1ae66e07819a"},"lmelglejhemejginpboagddgdfbepgmp":{"cohort":"1:lwl:3avr@0.1","cohortname":"Auto","dlrc":6861,"fp":"","installdate":6861,"max_pv":"0.0.0.0","pf":"7b5d2d7b-5839-4189-8020-d8b8350e62c3","pv":"550"},"mcfjlbnicoclaecapilmleaelokfnijm":{"cohort":"1:2ql3:","cohortname":"Initial upload","dlrc":6861,"installdate":6861,"pf":"5c5f92f0-c12b-4205-9094-5dfcc9f61942"},"neifaoindggfcjicffkgpmnlppeffabd":{"cohort":"1:1299:","cohortname":"Auto","dlrc":6861,"fp":"","installdate":6861,"max_pv":"0.0.0.0","pf":"1f3d01f3-a6f9-42e7-8adc-c30784d6019f","pv":"1.0.2738.0"},"niikhdgajlphfehepabhhblakbdgeefj":{"cohort":"1:1uh3:","cohortname":"Auto Main Cohort.","dlrc":6861,"installdate":6861,"pf":"bc2385e7-457a-4a19-b20c-c34af2a23c6a"},"obedbbhbpmojnkanicioggnmelmoomoc":{"cohort":"1:s6f:","cohortname":"Auto","dlrc":6861,"fp":"","installdate":6861,"max_pv":"0.0.0.0","pf":"d63d1e22-2c18-4e00-97d0-171163f8a7d2","pv":"20250915.810226330.14"},"oimompecagnajdejgnnjijobebaeigek":{"cohort":"1:2qw3:","cohortname":"Auto","dlrc":6861,"installdate":6861,"pf":"09227797-2f3c-4fc2-b2f2-e0906dab29a0"},"ojhpjlocmbogdgmfpkhlaaeamibhnphh":{"cohort":"1:w0x:","cohortname":"All users","dlrc":6861,"fp":"","installdate":6861,"max_pv":"0.0.0.0","pf":"6a68fc36-5d03-49d2-b49f-c0fe5b913013","pv":"3"},"pmagihnlncbcefglppponlgakiphldeh":{"cohort":"1:2ntr:","cohortname":"General Release","dlrc":6861,"installdate":6861,"pf":"7298900f-dcd1-495d-aa55-5c7a70b3e11f"}}},"user_experience_metrics":{"default_opt_in":2,"limited_entropy_randomization_source":"615C5D42DD045F87113201A4F2649A4B","low_entropy_source3":3664,"machine_id":5025582,"pseudo_low_entropy_source":2321,"session_id":0,"stability":{"browser_last_live_timestamp":"13404955022617564","exited_cleanly":true,"stats_buildtime":"1760396125","stats_version":"141.0.7390.108-64","system_crash_count":0}},"variations_compressed_seed":"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","variations_country":"us","variations_crash_streak":0,"variations_failed_to_fetch_seed_streak":0,"variations_google_groups":{"Default":[]},"variations_last_fetch_time":"13404954530594772","variations_permanent_consistency_country":["141.0.7390.108","us"],"variations_safe_compressed_seed":"H4sIAAAAAAAAAOy9CZwkx1kn2pU9M5JzZKmUkmZGpRkdqfuYnqqs6jokX9WXpkvdPa3q7plxc5SyKqOrU52VWc7M6p7W7tufbc611+DlYWANLAvLchgMewCPxYZdeH5Y9jNm1+w+jFkvx2PN41rOBR484G1E5BWZEZFRkp/xvp/gB5K6vu//RX4ZERnxnfIzW+uL+289Xl9araxvt4+ubJVvbLzUvrG+tD5ZXxpW1pfaR8vDt662F9pLq+32Gx90uuXLxtZa/9LawRuVrzwlX2wPBsDzzL5pmf7xJnD3HHek2wOwDnRv4oIRsP3lG2PgmvDfmoXOW+WHlkxP71vAuGb6++3r644BFp3R2AI+0Mra/MVy86KmFQv9S8p04J1d+eEk9DXQX3RsH9i+d8W2jl8d9ovyPHvYG86qbZk22AY3/AXnBvBenSxDvpSU1QW2AVzgbgHX1C3zJd03HfvVP9GKfDtUj+tYrw7ntHzTEtjTJ5Zf7HUO5PqK4w6AccXuTQVTnOlfKk0p2ZIbgbC9vemlqdNJ655TbqlUW3PluYamlefK988+NrP/kX/0yx89pbxdkkvtvjPxt/dNb8v0Qds7tgcrwB/sm/awWehckl+/bKP3uaZP7MF+0eif57F0luUHYzUyyYoz/fMlHsyK/FBCQVwclYPTvV+5pVJpzpXn5lvN6ly5dEtFq8yV5+qVSnVOg4pQvvWEfNemPgSr9p5DADULnUnm6bWSfPvWvnO0pcP1EyxS5eSebnmgvyJfvDL2zVEw0S+btu+FI2nbjn08cibeku7ri47tAdtnCCam4lSAeCpOxUJOxamlqVNKe0a+N3o06sMXZ/pnSwy9vEG+Lx4qk1ulc3cryulWY648V6u3NDQTylU4L8qVOl4SmgRs7SSwL+5soX88u/BCQfneWfk8Fa5WXtPtodcsdKrZJXK/fC+Vad1xAWLrrMuP8vUQkRZn+veX8uA25MdyNEPgqTl43R8qKK+rwL0C/i9cNFBVda1RDlWlu5rUH2rSQNekgadJhq5JwNIk4GvSnqlJ+0CT9uE/XU3an2iSaWjSi7omHTiaZPmaZB1qkm1pkt3XpLGlSa6jSe5Ek7wDTfIsTfJcTfIONck70iR/X5N8V5MmB5p0aGonX9q/uLiB/rF9Df3j8nMvFJSXJfkh5lPBPy4B78B3xsxNjcq84djLNrGpscnwpsaBITY1Po7Kwel+qXKyUm7OPVG6pQIn79x8s1oLXkxi/u48p0ljXzs59i8udNE/Nrc1ac/VTu65FxfbmmT6+B0YQJOAp50C3sVapfVCQfkHEmPSb4Cj1YFjNwudSlaF9/KZOqvywzlKxITFmf69JT5UR34kT5ExlsrFwl+IGtRivVVDk73SQDptBDpVfkuSn6RCLAFv4JpjuPNtWvoA7DuWAdxmofOWrHYuToXRGco1vrLofMWZ/sXSVIL25fkcVbIlqdNI6j6gvL5SRqqtlmtzFQ1qGu4v87V6BWv6hYLyjln5EhX0OQDGW6YBNnUbWFfsLX0EtvX+hn4It+ClrL4rU+N0HLnJ1zmbtzjTr5SmFjiWWzm650tUp5WI3gGe7I3q/JxWj2d7qxy9gy+Xz7X7E888BPgMCewBWLb34Eibhc6j8k2BrotGv8Qm7Z5VTtRrc0+UTtfhHlWtaq1gPf2+JF8MuK645tC0Nxzf3DMH6AixCdyR6XmmY3fBoYP/1ix0FrJv+NKUKOQJfxrO4IQ/lTDyhD+tNHU6aVDXzRbUNTzazNXmy7W5J6Cur7/8Tz73qRPKHxXk0qal+/CacHn56uISGDgGcLcm47Hj+uwvIpOF/CIyyYIvIhuG/CJycVQOTleBX0Q42U7CLQY9uvIvCvLr4T1p7C9eXnH1EUCfrfPh/bS3PALuENiD4+dMy9o6Mn302LcpJFPnSfkMcS+MfinO9G8rpYifks+S9zqCWiWpu0XlRKsy90TpRKsVjPnvyefbg4EzsX14YDY9H47vqu6a6DXDra4jn1kyB2DdHLrobz38wuBq1M7Jp0bA33cM5VbDHIDeKCTq3ykrWVwovw6PESfqofzvKuQOYE++AAew6YKx7tLHcTEax4NoHGNMG4+nZ4OjHrCNsWPaPmNw55QT9To849QbaPto1eeadTTIn5de/SB3Py+D7PTkM4Eloke8Fa148vMjoCOfCRZmVsC0r/ucktRlo3SiHi6Vb5Tk+wOGTccyBybw1hzdwEYcZ+JvHduDZqEzn90l1HzGzvPy48kVxCUuzvTVUj5kV36CWGe5mGouJlqNNbQam4Fa/uYm+db2wN+auIfgeNO0HLhV/uuCfG/4Siq94FLRW15u96AxqtyCpijtNvl1BrD0495Iv6FILS/xB9NWpLKnnZVvw39wddtwRuZLQDnhuxOgPSHLwO55pg96pqGcbyxtL7518qxXfrY+6Vb2D9yr1yvl7WveztK+q90hnx67Tl/HdiDlRHmuovXvls9uuuahPjje0m2j79yIHqHzocTYNdbYi8mxz1ZaXuIvpq3MVqYY/W7neOut7u4iOXrHWa/sTl7V6Ktio9cyo9emGP2L/W27ubD2LDH6t85PJlc3qq9q9DWx0Vczo69OMfrnjd3hdXPrOjH6naVjo926PvXon5HPRxsddej9uxUmM2FnfbN8f3xyoDMUZ/p3l5hob5EfSJwZ2AgqC6H7WNYeii7G8BaMbsOmv/+RD//qR08p33IzZQf4EHUH6F659v/NDvDcSDtu74xS77G1Da499zbtLvI9nirPlbX5Ou9N/iR1D0iN/vO5B+xMthpLa4MNcvzznc2dw/VXOf6q2Phf3S4w8PZ896W39cld4Gjf2Wt1X+X4a2Ljf3X7QG390NebredI/a+ZG43W0isYP2UnIAf/P9JO0KLsBIQJOLSKIQskNpwFW8N3QNfJAB04V+2N7c0u0K2+cyO2Mj4l3xK+5/nxwC+ePPfpd/xcoa/IxQ1/HFBvAkO3vLcXCp37Ys3s0mg6F+XXX3Yso68PDkK8X0J4ChWvIt8dKTf9e3Gmr5SyEjS5FKuTxqNmePDFq4UuXpVycOf8kR/8s3fc+kJB+V8LORp6RL412kfLZfhMBvXRv5DPUseXyOAI+EJB+VFJVsnpE/zXsxPTAG1j2xmbA3jnaGZPxw/LD6ZmXkAeOI42ddf0jzvX5KdYC4FGX5zpP1wSAr4uX2QuEBayKoKMrKZVeJloVGrzyGpag7a8RrUa2PKUT0nyE23DgBYqeNbedg6Ave0sWiaw/S19Dyy4zpEHXXQgsEG8Oau/p6aB6AC5Gt8zhNmKM/2nStOI2YtMs/DuMZUcdQo5aDpWNLy0wova5yT5ybZhXNNd27SH0BFpb29xtUo3RU+BQZiip+DDpuhpBBGm6CklqdNIQrMXebfq5WYdO7g05ODSmsHs/ReZVd82vPbY3LmxbO9DTzt0rMNV38jq9yER1s6O/CRz0WfJizP9h0oisFejvYSy5Om4qgAushxDL9NcvdVqzlWq0YrXoF8d6eyPCvJ9beMQ8hibruMDtPdv+bo/8bqOZTkTOCEvZhUGrcgMvk47+sbD2UcnKs70SyU2xIKsJucVG0NlYnTPKqeh83OuOl9tzpVLJxpasCIN+VzbDPSHrHpHjnuwua97oNIsdB6UT0VPeVa+q21uAd837aEHN4Eu2HOBt49Vi+Yf9ONVoWOkOg9VW4b/QFJeklWSd9n2gTt2TQ+E9swds1nolBPyHhLhwRt5E23kTezrDc5DcERI9scl+ZG2ZTlHCyuL+mAfXNsH9qLleMBYB4apb/ku0Efbrj44+O93o2eyb/cxUfbOl8mX4nctxFKc6T9WEoX/crmcmAfC+KogPt5V0HustIJYE/ga69XWfORbQQE3CO54rHseDpDa1D3v+YlueOyAGyYLGXDDJAsCbtgwZMANF0fl4GB3KnZKBweDIOBmPgizUP6gIN+DAKDV7frl7qoNJ+eS6UG3hm6hWZzRwQUuT+fZaPSBEuh0xZn+hRIX6HLkpA7VwEZSeUhduXSi0Yo/25cQ7bY78XxgXIXn/PbEd3bGQ1c3wNax7e8D3xysmMAytmGwHPNAJA5BHIjE2fCBaAoxxIFoOjnqFHLQPqnBqVWvNZpz2jycW/AIWp+vhHvVn4WK3rENfaQPgbHh2K7j+PE03Xa2Dsxx7PNpFjpAbkZeoSm5i0b/UeVhIabOl8hz5CTN4yjO9B8tCYJ/abR3hhNXBF0VQ8eruoUOTJqWXNXlUPO/U5DPIKxrw8E10zaco0V97E9cFA/xZHYmn2ORd94YRXkFasqQFGf650os9jdFVoVQEVR+lcGPZxk+G7Zac41KFP0EoxbCbfwrZuVie21zawMcLToGQB6cZqGzkn3Oqnxux4PBJm1r7EW0l31/rMlnKb88v7O6SBx6WNz40MP6lTz08DBUNkbSQMMYKjbQsJ6DMNBwEFQWQvdu5WSlWkGhTsGZqBIHi/y3gnyubR3px96C5QwOquOBt2oPnKFt+g77pMlgIE+aDKLgpMmCIE+aHAyViYFPg3V0ICtX8S4XnsjCgCRLfqBtD/Ydd9lCp/NVeLDT0XF10wUW9GzBVfeYfHPslEWPzmBCWkaWj1tgXCwMhdLiI8ufzMoPt23b8XUfoMtrYAlYvuEH/LFF58vkmwMrVqVY0O6QXz/Ay6qHLJWKNO9p5+SiY/cGrumbA93qjXV/H9stYdQkX07Hl09jgxJ8q0wJinxi5BhAkfWI9lVJnZNvDiyelWKhf7+SR58wcupEoCefDwd65mATgZ75eGoOHgoNr1WQ5aYZhYZ//6c/dkr55YJ8V9v2zc1904PhvNsAzhvfPW4WOk9kV9ZZBjUR8UulwBG/dGYi4pfJrdK50bSuBNO6hWLAW2EM0jtm5TNtZ3R1fGPHA4v7rjMC2/suwGfwxezjleUza2Zfd0ZpcvT3wywM8Rmjs+LPGP038jPG5ldZ/CnxlBFG4mmjT4tn8KsM/sRdSJuvz8ErLTStzNXnE7F+fwntBePxgjOxDRgj3gXAHrjHY584nLVyQ3bOKndRYcipR6MIph6VmZx6LG6Vzo2v1WgXL89XktfqKAruX0qy2gVDGJzhhhjLmBvt486hibdxuoUpn5WwMOWTYwuTACxhYRLDVQVw8ZRBF+ZauYnPmMHpvhVNmV+X5Cd2PMBGWXHc+I/MO5Q4BHGHEmfDd6gpxBB3qOnkqFPIwUqO1QrnJTrr1puNSMl/UJDPZvGeA8dXq8itldHo3Ux64gjJoMFHSBYAcYTkIKgsBGIpYmNvYO9qxef534AfO9pCZn/svhh3nMAHU8HWpxrcfxu1ahzZ+xFJfqgLhsAGrg7jhI9XHHdR93XPd53xvjlY0U1r4gIPbbuZp35EjLnz1sjrhPadXIbiTP+Rkhj0bnSFxnuPELYqhI3nCd6ka7WkAsN73/Xf+chXvu/UCwXlS+RSezxG5sBFx3UneGmBgXMI0PHo8WSI9HkeMQw6a9ZR0FngPVXeC81jAcO6bpt7wPO3Bs4YLO6DwYGHJmQC/gKXuvOkfHP49YQmK4VHjAZThoNpBtbt6z/7wV/8Dkn5TEG+qT0eX3ZQDC39Mh/cATavtb2AFB44iWMInQQfQxjsxDGEza8y+Dnx7vVgVSjfVpAfbI/HwDa2UNbCqt0Fb5sAz2/b5giFU4bBw+flYhQ4U9bmK+Vyo1h4yz9/788XoMP9vFwM4yISv/5w8GviTtDv3Caf7sF7zGEQ7929Hd5yA2Ph/kf+7OtfPnX9fZ/+nm+4+b9vxmP5zrbr6sfQI+H4x2NwVbcmaIUmII1OQ74neM806uJM/4xCxUExwGgCNsIJ+IGCfCtKokTzZM1Bdv0Hsy+9mCbrXIyin+0e+VNxpl8spcnn5HOJ/S5Dr6bosUMU++crYYjmNxfk04hsyfaumfCLfyE7Ulm+OaTpPCDfTo5xyYbqkUsxiSorqXEFNGpEgzaLMjyszFdq+LBSRjcMGFqL59VPFuTbcC6qY/s4X7dZ6DyUHd3tGbrOpUgzwSDj34oz/dtLGYZyFCERDpnkUNMc3fvibMhKMhtyPjgHKO+C137ItO1MBvvrziHwVkcj6PPwgXXc3vOBuzVwHQtaqt8YrwzPh//swZvyo4IAkD1cOkl2RZCdWFuEkVWEPTCyCkkijayi6KoYevdu5fXhXbxVntMqeDd4//tePgWTGx9OOWl9H3g+DngPnn4J2MfoZdwWXZhiJ5zySECFzJ5sLMIJJ8aCnXCC8IQTThxfFcTvXlBeh/3UhDOzFh/1PizJ5xIcW8D2TRtYz05012gWOlvy44H22EJCHngTU9LOcwodz9dPg6X5+mmwPF8/Azfj66fQdUvQBI7t+/XG3HytdBL+J95wf+Y19eWpL2H7KJcTF9laK+FSf7Tt+67Zn+DTIIxKMe3hqo0CVYC76TqowAOqwcDwqfP4ozwR0qcuxBL41MXgSZ+6ML4qiI/t/w1s/w9ubUG4yfXv+bY//WjhhYLyfkm+tz0xTGfVHk9gwsue6Y660Ap11dS39kdg1JxhJOnz2UjbLZc0sN3y4UjbbS6emoOHLyzINdVsNPBuh6+5jWa02/3OrHwLgglmVHOm896CfHfyTz1oTOu1/d5l3R4WDe1uWfGB7hrOkd3zzRFwJn7PU2Zb5bJ2Rr4t+gsYOLbhKbOVZrn/JvkcQtwC7qE5AFcm/pW9AF1WWb9AsW0fCiVdLwzywPXC+DXleuFgqGyM5D6TP2q8zwg8HbHPiOGqArjw6N7AR/fwNPzpQvC2A8bmDMsJliDC/47iWDzmm0gSZd8EAcF8E2kMlYmBHm0ePVoY+fg3MJh4MsSVbUwPbRfQZb2m94HFCZxhspCBM0yyIHCGDUMGznBxVA4Odla00E6noTi7aj04il//1+/5k1+5RfmkJD/Ynvj78DMz0H2wAz8PG+AI+6+9y8CynPYYRoFdk5+K4xnyOWCgsiICTQQqiwCjQGURYCJQWRBZFUFGatVqWK3Y7lkOb2ifPSE/moHY8YAbFKu5Clxzz4Q2a6hUKzu/3irML8+JECKXrDOGCulF31WKpukyijP9x0uiA+q8IFc4GmdLUIUljKIMeLFHiJ++ONMvl6bVmB1VbxB8IFKeOqU8fMrDPq0ghriKjswNLbxB/6gkPwRDmjbNAfRQr9rBv6w47lXTAM6mpR/3cdgk3doqwkxYW0UYsLVVCJqwtopiq0LYyYMdNrNGUZLKd56Uz0OMbb3/rOtMxuH99oqNldMsdH68ID8e/LV3xe6Fxocgnbe3MTGGAJrhauVWeb5oaHfKt/quORwCtwfLVDiGItX3tQflC+FfPWB7pm8emv5xz9+HYbmOZSjSxUp/Q75tW+9fcYe6HVS5ks+l/vCsqxsT6GNXHkj9EobfXjXDYm2EbSdFjW07qT+Sth0Kh5rhSH7EWUPFH3HWr+RHnIehsjG2oiTtzDCyeinO9B8sCahvOzqjZQdGR1XzUbuX4I0XR3lVtLlaPfKLNaLqYDgtrCtNvPcWCsp/PZE7R7+qIN9DmaN5s/K1CffahMtMuBnlp2blO4gJF82zoXz2ytjvrU4xxWrZKfbFMZH+B3qDc8qtCSNZpV4uEW90PvUOYZ3Je4gXeFXbQsbu6EUazP2iUq40i0Z/Ub5zfWL5Zvrd3b6t97vASe8Y5J+WTBy+0ZIvRCqmoUFfUYkqp/N05N3e22PyqnTeapSQid4sObbiTP+OUvYpOjX5HuLNZblUCldqN6KqIdqN6EpK70ZMDJWJ0X0SHhBRzHS9gv3JQZJPeT6zwpWfCM6Kg6Au74pp+Sh2Yx3oMKdtb2Jt6COOZ16EOXNWzGOIz4q50Jmzogi2KoTdvVc5DcuTzdVQLEPpNPSxzdXqkavqgxJeWiFQF8AoRxgDA4u9HDNjqDg8AXXKJJRHHpqEcmFTJiERXFUAF2qqAfegah1ZBE83oIuy2qgHBsHr7/7R7/ihW5Rvn5Vvh2B70Ja0uQoNMCjS7B2FbL3bN8t36AHpRQ8RXpy4lvLYvu+PvacvXRrgkNKLIdHc0HGGFtDHpjc3cEaX+hfxu4G/YUGLzmg0sYNqZvKt4Y8oiMkkM3nYfEEmD5sglcnDR1K5SIRbmxht4NYmn4B0a2fo1RR9IuqiGti6fg1GI4XvZ+A6nre65warnxGNRKMmo5FoFEE0EpWZjEZicat0buRexqu0hk3TOCes0Qr9SLDGaMRqGC7wPJTqs3Wkj8fY70GvMcpjImqM8ghxjVEuFFFjNA9L5WIlo+6C9JnQVhCG9L/rJPRGBBDmVceHN+YVWNXb3/JdE1cazpaj7vflMp9txXVG6IM8tgAalIdsUTkcW6Y9DOk7b4vKVCaUKiisONPXSlMPsePKT1OUP4VMdXqZKXvVFBqK7FXTaDVtr5pSnjqlvO7jcRhks0Uk+gYXjp8onPrw+z733t8/oXxsVn4sB91x23t75g2AU+CjeLRC/wlxVsgYxH1ARkWckYj4eCYVTTUVkh6ZQXNfesQERZTERfRlTfhFEzJUYRk4jSNIx2+FaRzf+xsvn1J+YFZ+IIKxDZjTd+S4hreK6sJuTdw9HZV1bZHBbeWW1ipq/QcFmCFrMvItZFUEWBMvcqZTS79IIYjklTGXGl8Z80GJK6MQqpqPmnhL1UotfEt//rGXT13/61//vl848UIBfvnPRDie5wxglBB86R47i5JKTmZRUkmCLEo6O5lFyeRXGfzY7TKP3S74DhQlPvxVIT4RouC+4MyluwYsZczO/2bzUE+NFDry1EgDop4aGUgqDwlWjGhCP14NHn0qpRPN0En7nln5UsS4r9soaXziASM4OmxNhkPgwSWw7QLdD5rPXJdvWh3ajgsqqNDrHSb6D+OiF1F7SqECy15PiU2UvZ6SF5e9nlYgUfb6FUhUp5XYvVuJY3BgbDzOKQ1CJN51Ur4tAnRGYx1F0f39gvxIF7wIBv6C6fr7x0B327axBee4tW36Fmh7AXHR6H8ZjKjACHjiBj/B62t8rsNwwU8Rqnw/9feEpM5iZI2I31BGTnGmf0+JPYzOUuTuTmidiqJyUMiQGN5zhSExPJp0SEwenpqHR5SazVFrUGo2T/lkqVkBTDUXE34IGnBXrLZq83ONVgnlAIThdQ8nJyPO1V2ElWvdY5gafcVeHQWlnZ7ObpOPCnKTYbIiHEGYrBA4GSYriq6KoePQa2QOagYNG4JA7DDsSfkWKV50Gaxl3bWOmW1b+GzU6U8nJac/A446/dl4ag4eVg3qdlSt4nsmrIwxNz8f9bL4JiW+/i069mDiujAXHGl2dTR2ncNU8yh48Pj+WVnFumpb1qYLmUGGxSsa/bfPJi6XMFD3sj+yto/HIPEePeikXZlY2LYnPxXSY8BNF8D/AEbAAfdcmL6MsxXi2z/scYC+ugY8bwS/PhH+SowR3oR27D1nMEEhMcEl+FEOLXT1G1u4/lN8nekC3zXBIYA0uBRQosPZavJeveOh7BPYSmeiD8G2s+3qtmfpfqgH/OgaQe96YNH0j9HTBmS75hhqbNW+DCYujBgayPcleKKK5wnt0m+xQq8idYsV4mHcYoXlqdPKM6MacfHjicyc4kx/rjTVXOu8GLWLSDyaqCx1Olk0uxVtipN2KxoF3W7FwlL5WERFReG1FVRUFKZPVVScSo46jZxUUJXI8o+CqkSIM0FVohJUYQk0e0XexkTaK/Ko6fYKERmquAzaLiW2a5K7lBgPfZcSl6dOK28iP0N/PKFNvjjTr5Vewcehcyi/gfGYwnLVVyL3SnSMIR6X9n0qzvQfKOV+xDajYzz5ICxENQ8RNciB5ojTTXg8qlUrQfrn9e/6w//8rbBY8a/Nyk8mDkYedOHiGz3sV4qMGqswoXwdTm1U7/fppNnz4lTckDe2fF5UpuIljJ9vStvMpgQjiruK8wXFXacQRBZ3nU6SOo2kpJWtGtlCYU6t8mM4gjpEQqVwDBMFYWBLwvJhcOKl13IW4E2HSOfRRyHSucDpEGkRZFUEOZmvVAvylbDJrlqNTVZPJZAOYRWWbQeWEQIjsAbsob8PI/eBH9hgmoVOX1ZCyt6201tz7CFwUf9UZRD+3Xd6Fvp7UIppbjop1MOgCCN5GBQSRT0MispSp5KVLKGptZLtN+thhOxXFGKr0aILDNOHZsdEADMufvJG+S54cj5OGPbL5YbWCPr25ADAYIJmBVkpYGhu6XRzHpkza2FVy3HsHIgxFnT7AH4El0xvbOnHyJqbKFfwoABPonMcMpLUGqHT+GOwFG3ADjuATcYWiFU7sf3owMFOmxNiJ9PmhFiCtDkxeDJtThhfFcTHReRwPCE0lcAihkGmWFjlS/nO2XhDWwLGZGyhyPegTVIwF5F/T0t+6B4W4oI88QcOZ5fk8hAftnr6wyYIQtt/OfTk/ssDpu6/OciqCDJuz41r6WvkBytpyApUs+3AA1nbNjYmoz6K2uEbsuhsVEMWnZQ0ZDHgqIYsNp6ag5cs0N4KakHhnKiovcCHg4A5DGLpx5vOeDIO5pwF3CVggWAbZAfM5TFnAubyGOKAuVzoTMCcCLYqhE2YSMtJE2kt/IK8W5IvxFCw1jm6I8fxrWjdZ/R2Xw5X57nI+pBUGIWyONO/r5QDthZNUkJFDDSVj4YzTjSccYKrLDTCyZQMRsKPvAQOfcexvFXPw9VW+MFINCaqUYdGSBp1qFBUow4LS+Vi4eMFrvJZx2urMo9js8JPwwek+CsdJBKuOQPdgt/qKDW7WejUszp5UICT6rJnUpMuezYo1WXPRVXzUXGONQ7b1arJHOv5SrySzidwxrDjcReMnENsScufOzQm6tyhEZJzhwpFnTssLJWLlawaoyWrxmih/+rfS/LjEYKz4fhYlWDH9nCrVmBEWoaqeVNWNU9OgdAZRMaqhJ7yuIoz/SdLUwgxIjNoUoMiUlRxKajFBOqUjBZiIqv7HYXYW4Fgdsawxu+WfphECjX6jHxL8tBfNPqPC7PDkzfKuz7dQI1Cm/Wgj7Dy51Ls3lhybGzPvrGu+4P9+ByPP97eFXfxcMCsozolDNVUKMZKmgoFxVFNheLy1CnlJUNDA5ddcG6PevJ25TsjzGNbH5mDcF95RJbhq46Ue4ZOGff5jbqWvHc29j4Fnj18Kt3yHVcfwiCeVRtY5tBE/n9oRzPRZ/DZ7CutvRIoqolUnJ00kU4hlmoinU6u+grkJrOF60EyCD7xh4FgsMTToylgXCwAGJv7jp30iTULnTdk38PjwvxUR0gOD+kIyRNAdYQISFBFJcD20E1c2rmMCvpp83HLtr9JXG0xzsLkeMM52tSP13QfnpPDeuVX5dOJDnRFo78cB1AEIxhBoZla5+mjHymAGl7BxSPDK7ik9PCKXHRVEJ19aiWfkXZqTWmBc2rNYqVPrSRF9ylo0cBLZr4yVyvHp/haNmFS+fHZ9Hsk4VYc9zlLd20dhS/LUZ/GclFLxtBwWSFjGPeKGRVBRiLqtZk2dAjDsCcZg4M2yVjgnEnGQU9PMgYpLnUX2egrTWzz+NqPfvyU8gtSevEik9aK48IqrvhjivNDrsjF7sSuhZmSlTJcwZoiywPI0PNQR1T416TRigNJtR1x6EnbEQ+YajvKQVZFkGk9p4I4x7B4zE/9zKf/y2nlnTelvw7I4Ov6bRsd/TZdx5gM/A1c6fTbsolg/a9FBXgoCKiITWwXiH9LYMZRNPhn2Dclkr8BfNg0DeF48f0RU141XX8S3MqArxu6r6cq97CGFFbuYf2ertzDw1F5ODSbB1ULpM2DSkK3eTDR1Bw0WrhA3hsgwwXyqOnhAiIyVHEZNIMBc3qQBgP2LKIaDLioaj4qqp0Y21Pmq7B2YniS/10pdnDF72sB2GDP9FE6xwi45kC3l2+MAzdaO7sQ56YDoXrJRBhJL5mQKKqXTFSWOpWsZLsATZurwRM1bk85H4aB/jMpdnJRIRdGMNtgPqtiNZ+RGmrMIiZDjZmQ1FBjHqaai5lqJ1StJsqah+nTn5il7d4hzuoY1iZ4PqukN+VwyRdWNy/HvtLY24dJcrbNBBBr20yQ5G2bKTTatplESw6N+xB4aPznJIaWi6by0ZLdDOrVROPlKO9L+YcJp1H28WC3wlynEZ2N6jSik5JOIwYc1WnExlNz8HCWR2JHSOy8LyccxzE3ytOZ2LCkZRCxhvou8h3HfHaq45jPQjqOc+CpjuN8fFUQH0+voM1vOdksYz7s8vtJ6m6xZtoHaFDIJbmV1eBb5HvgSwo5r+mWBXxED9tYyA8ckJjoF2/VXnKObA8VhSRyvDhQOMeLQ0DmeOUgqVyk5Lkk9wHwuST/OYlziRCqmo8KncxNZLnXmuU5rVE6EWXiv+tEnDMWv9ANc3Bg6yPYJ0IfogD/tm3sjDEa6vSWecWd2KnKw8F1N+hUoQCq45UHSjpeeZR0x2setiqGnS4Xwn/KuFxIjjYy5ULycVUB3Lj7SDPcKL9HSl/AFg+hFfnqwH4rQKXXdX8/1/XH5OSc5CnUtJM8DZRzkmegpk/yFDJ0ksdNHWqVGizRdRK6TUMb42dmYRXdFEZgA24WOl9dkE+HzRd7rWax359k5n1E37aNFdOyTHsoX8ynSbRll+/P7k7wyACDuHQLbVHvLsjFy45lwKKLoaWkWPiiGE0cIDRRBEajTDcaJXc0qKFItYwailSDOsPXP/ivvv7vKl9X4L7ca2mr8YqQNsUGpFXRgMKjSzCg2fSlb3mkm1YUgg0jYcPKI4FzaF8+Gw6ynSgc1LasoqHdIyt64o89ANGUk3u65YHsBZMvi3PB5DPSLpg5ojgXzHxZ6Qsmn6N7D3wZTTw7Ar/ZTxRO/fjbf+/T//N55bNS2qSVQpv4MFA5fBlvzG6XT4gDcMw4bCaaGYcjgmPG4ctIm3HY1LgESdDFt0F0YotLkAT6/enMgR3hXvHHlI+RyIGdxc45sLNYaAd2JjznwM7DTx/YWbTo0hPYm2C0O/xIhSXTkyFyAcoNHybhGIl5rvv8mnJ5zJyTGp2BdlJjQHNOamzs9EmNTolD5FBnsHKYRYz9iFGTjj+S0t+/FUv3uzATJmlmgTG3E9e0HajHhaweL02J0jmI9ri0QnM4izP9S6UphVlRdEVGxQLS1OmkJVvszTeTLfaiOli/nogfCjDHut01vYMFHdZ4SIe48+OHchGo8UO5XGT8UL4QavyQkBRVXEpXhQVJcRpHvTJXKc/HQd9Rz5S3f/d3v3xC+eyp9Npec3TDtIfQGYQ7jYT6fT/FE/TVhfQBAcYSISsAFSbteYqpB7oF/2NFtyx4IkyvlKsDe9mGoa10XM7Bgz8g2sGDz8E7eOTLSh88cmSxozOYmqNFZzCJedEZXAnp6Ay2BPYelvNOaXtY3jTg7GEC0tJ7WA4LMqhrdWRlbDTnKpU4XWq+OVfBH97Xpa/Ca86xbvnHKMguuA40C51vL2Q6StaLGlxftDPVFZeCIiBIvjdpUc6SyWeT95LEL2h8qZ6WeHyK8PiU/PEpOeNTmOMj4ii+pZAOpPiiGWfO8Z06IObxnUqde3xnyqAe3+ky2LYbOnbGdkMD5dhuGKhp2w0NNemp4L817Kng05Ceinw8NQ8v2RWbMWlwV2zWjCK6YnMQVBYCisCJj2I1DUfgfPhXP3pKeXcmOHQduIN93favjP0rE3/RMoHtb5kG2HEtXLQZ72ciwaEiUJzgUBF2WnCokFhOcKio3HRwqAgfvsPhLsWVCnGHe6Gg/HwmLpTEXHZdx10ydcsZCsaFMvk5Jw8mD+3kwRbAOXlwJaRPHkxi9HnGN7lquTZXacL0lgqqgaSFGRt/IKUdHuvOoWkPn4Vxas7Q2Xa290HXHO77V+w4VHQpq9nK1DjUYnuCvGSxPVGB1GJ7U0hUp5WI2zQ3sBEzTCD/XMYnjYrvbvlgjEudwMvz2wvymfA4BAuGxEWuiob2oHwPQL/1RpDT88G4N4gIcAK5dkE+m/hVtw3T0H3Q831LkeZHWQ93ehAcD3ealObhzsBxPNw0vLSHO02DjRaRSwKlbuFErigb6R0n5TdRQMYWCLqar9qJqKXYP4iPvEGtyc1UROxbXi0mRCRCZd+ivFpE4uz3fPro9+rx31OQn+VNhSmw4HhKr3Y8X1eQL3Pn0pQDUl/lgJJlNrR6mLX80+/8+CnluzI+xA1cBBdA5yMsKoOPCyI+RAon5xxKoaadQ2mgnHMoAzV9DqWQwSyFViuRpVAthzmW3zGb9hltgKMVZxzUQIi/O7V4MVbKRTnsW8Hng1zhgsNcaQ8blSuxqDY78+lFJYbBdoHTyGkucCosxwXOwk27wGl0yWlcriZrMitfJ8XV9jAACqvYcHxzLzZA1rKT+IFcPmrFJAYtWTGJBUitmMRBVPMQYWwADBEpnWiFuZ0/LaUzIzb1Y1T6cV23Dd133OMu0CfIJfNmWYlnbXkM3AH6jGeTKxgQnOQGBgctuYEFzklu4KCnkxsYpDgEGC33VrU5V2sm26d/n5RevkHFt7hABJ5X7I41fFbO+qOR09YfFZaz/li46fVHo0M7ZBPtkMFRphqGovySFBf0DOyNwIYWuYWBvWo/C4I81iXg6ybqAPzmrMqemgaCWu4wn40sdygghlruUEyOOoUcVPCgXMFfH1z+Ieow83uZAAdcUWu1r8PJx2oaTWdINY2mE4VNoxkQqabRbAyViYELPODnDUIY66Ev7ztPxPXZAj7fHBwcL0z6MBUORrbBm4Kx5gyH+HCiZ1WwkTbLrpg3gBHuAhgqAEivVCyNJOWYBdnANLMgm5pnFuTLSJsFOTLYGw7tqWkbDlU7nA2HhZvecGh0qC5/FAhZTcRBfuVs3Esn4MfJ8bAjmeOCLd+dBE1tk9WRlrMTRZseiNpkR5SZbLIjLJLaZGcamerUMlHabiu5JdVD1+SPvePrv+J1LxQoSTJM2LAjnkiSDB+E50rkMlJdiXxRPFdirqyMK5HLgdTdJL4AgaHg+kf/9K8/JL9QUD6dcbjjbiWmPYwbWoSBo4IOdw4Cx+HO4aI53HlCOA73HClphzuHHNXHwx0+UNnE003U8EzTwo/OZ2YzH51j298HvjnYAhYY+EHFyvijwyopKsDNLCkqwitcUlQEjFZSVICPLCkqIohaUlRQkjqNpO7tykn4jp/At8Ovec/HTyk/lwkAhO3i24e6r7uw6w/2jK84jo8yIkQCANkAnKMCm4l2VOCI4BwV+DLSRwU2Nc7QwT1zYeAPvBiFBWY+k9mHrg5ga+fjKxO0uxEGf8F9iIPA2Yc4XLR9iCeEsw/lSEnvQxxynAmJQxCCgl5hkGWYEvaujPn9KtzO4JsJPiCCKWFpNo7BPE1KM5hn4DgGcxpe2mCepsEB1dgXEXb8Uf7jiXQ1gISpk7CDH2Q1cj29Ka/r9kS3wsAXGJ4Zg3npCYlvGVRxnG2TK4K2bXIZeNtmrqT0tsmXxF5jHEXQ1hhPb5w1liMlvcY45NitiHqplMvzuPwIjh5F3kVcJPlE2u6XgLpir5gWPD/1snNqLY57QxXXrw7smGXLh9YlC1a/ORwEvUsoOWk7nj4EKMWCFnolBEuGXgmx0EOvhKWpU0rjZwBGKmBlAMY6yskAJJBoGYARAf6i4bpO8FYJ95nQxvLOWbnCnA8eO6R1JTtDqq8AqeNF1ztO0QQWd3GmXy29AqF+FLnBK6rAk6pOL7V7LlmWENpby2Ghsw9kfFG75piSLSPii6JwcnxRFGqaL4oGyvFFMVDTvigKGa77iZpDtVqVZE3dqDPjhwgD/x4cAiozC9tY+q55gKZ8X/dAbvMxLjfDts/hSNv2eeAM234OuiqGjm2qVWyvLiNHfOj8VH48WSDnxliHSQeoQO9VExzBOfZ48nZ3nkfceUK+OahJimgVHu3jicsbHxYVAmyhQoDBZeb6b/7fv/2tsvL+k/GxJvExbydSxHZsFwycoW2+BIxmofONlFbxqlzy9p2jnmP3dMvq6Xjy9fbQ5h2U+P+yqQTFxSI3YSEEVKfM49BTD1ECcshDlAAD/RAlKEmdShKt/qWYNsj6l4IapNa/FJenTikP70qRswztSviCWAnLk3ysIF9AB4Cubh8EZhhL97ykF/ZpucIl6cUN7XCP4pqmFQtv+eD3fxx62ub4vERzY8z4zf/1kwXSanJbymqCbAbVWmgz+P5Pf+zUCwXlk4lgvhWy94K3CYj2zrnBfDn81GC+HB4ymC9PADWYT0CCKiohmaTVDOqY4+6G82G+0A8lqsVBtPBqvOk6IwcWisRHR/SFTyXtPizESS0Kx6Eni8LxgKlF4XKQVRFknLke3E60ufkGDCitUCq/rpg38IR/fgJgldLVvbBxNS5p4K56gcpyg3vFoajBveLsZHDvFGKpwb3TyVVfgVx2Lf6om8t3J+pUQeCxZfrxqojavfLrVLEYqXWqWMRknSomJLVOFQ9TzcXEqYFBSdx6oq5RtRKer/7qRNxlecW8gVoUboGRDq8BcIG/mOwz82XxXXzFvLGI25wiltWgjyxBsGqbMAkfEVzZwzZgjjRq/A5DDhm/wyCix+9wENVcRMYQaU+aGSKNiDlEFqKai0jra53RNdnXOvsqqH2tqSgqGyXZMEerzlXqcaH/elhO7o9n4xnxrOMMUQDQgqujCIxmAda2ICuv9/vHia5w0YRfcp2x4RzZGYw4KTMs0LLjAR6VB0PatoDvm/bQgw6/VTsE73w9vdLGsTLNiBShESliI/qGgnxHXIkkOo/BQf2tqal7n3IL3IfnqlVtHmYLnK6jwvfVqPD9fyrERdWfdQE4QPVXAb7PZTbkM3TiTku+kJnpSYLiTP9Mic76tHxvdn6neVUqL95UUQpwq9xIbqrV0Ef5w7OxF2q10gw99euOAay2bQRAzy6vbm+uNQud1ewj1+NVseOB1e0kAvHT5lryJ+rizwCQiz/zM33xU1FUDgpjKOSAM0NJPQ9rKFkUlY2C3xi6ADUa2EgTvr/QzPyLJ+KpnX5jsIRqoli/kX1bzxOvpL3DfFuLbeZPS8si2iOxM9ojf2ZqL4uiclAYQyGfJTMU8mfmULIoKgeFMRRSd5mhpFTLGkoWRWWj4DmFv22VKm1OXf/GP/yl37xN+cuEAQvOrJVuEqZZ6Mxl59M9xMwgOVg6IKkyOkiBsHSQRVHZKImdsFltJfsvRB2xfkMin351I/X0FTJZu9yqNItySgEkE2QhjAcBi8JhIULwn0qHYnBZWTvqBlfdKRDmjppBUdkouKUdzi6tNsKo+o//1sunlL8oEArbWJt2jm0IfUY2+Hv3htjenUVR2SjE17aSLG/UCL+275+Nbx2rQ9txweI+GBzAx13GiWy4sfAd6YmGkzgeyOXuPCPfGRqO08xKLjMx97T03BMAoN05GLTknYMFSL1zcBDVPMREvofWKIcz8/3vw92B7ye5l3XXOl60zMGBd8VGZvRmoXM5a/i+Xb7ZmOCGYcrJ+XJ55CVv5iws6s2cRUzezJmQ1Js5D1PNxUQ9v1rIflENCiWh3rRaJbwYJetMYZRV+1C3TCPo13LFjmwY/DpTPGZqnSkeA1lnigtNrTOVh60KYWPzT5D7hfs7BenI1dCu8dfJzy5iSrV5upRV2nkeC7U/QpaM7I9AgaH2R6DjqBwcGCfZQHesBir6FPYWm6/NoaoDyq8mEj0wO6wZ1WY7n/iJHvkQ1ESPfDYy0UNADDXRQ0yOOoWcxAGv3tQSB7xmPZxjybZ8GHDDsVecwQR9KuJLdzjl+NF1uQjUyJ9cLjLyJ18INfJHSIoqLiXTEi+MA1X+zWxcY3B1NHadQwCb2CIbfMZB1UylVD8mygs5idTpxxRRTiJFupX+nIvj0Cok8lnICok58NQKifn4qiA+PotiW16rEn7xf/vffvSU8qETcZ5OALI16Y9Mz0P9sGATWfz2RnEt1R3b3DuOqYpGfzM+laEfUcXXG3DybLv64ICoXNsFqKcm6uVjXAP98FiycNyFRQpd4K4a9AqAQpxkCJeYMGoIl7A0dUpptBMiQ2vkCZFBRD8hchDVPMRER8ZaMwinxQaZanDguf4rf/q+n7tF+fDJxMmFOXuuas1C51h+IJw/ocv/ytg3R+ZLIDila9Wi1u/GmwLsHAlcGD6NtqG2h6B9H/9nbLPb8QDxy6p92R9Z8Tg6pnx76kKARSmCohRxUcR+Q9s0+JLITYNPS9808vFVUXxawH2eAsiA+1x1UQPuRWSowjJSVZ60RqK3fDIyLpi+xpreBxbqYLkHXGAPQG5kHJOTGhnHpCYj49ig1Mg4Lqqaj4rP6DiwoIoNZBUU6luPqjUkizCt2gNrYoB1/cYasIf+/qq96DpHhudM3IFI3EYOPzVuI4eHjNvIE0CN2xCQoIpKwN60oItIc66aaJsd5p8pvynFTp8ADnsF4cbo2F5Wq2/JavXiVBjU6DABPjI6TEQQNTpMUJI6jaS0pisUTRMLHUPuuFZWvzkLncVJX+gs6tRCZ4LSFzoPVc1HTVroalXcMwxnytTChf6RgnwmxAmdhGv6sYNyjt4g3xt+xrePnDXTBt4aQEVCVweO3btaLZ7sn2Pxdx6LD/KYUuFQBh9SSLnLxoQBnY0aCuicDwI6f+QrP/Ezs8r7T8W3tTVgjl8yhytAh5mhsKiW7pt90zL9Y/yise868/JvxCasoMVxUMDB27HhwcYER/IFGBuPGeGz6QMfdhWC0bLbx2OgzHN/XnHcxYnnOyMYOzPed3UP5Y55VMMYawikYYxFRTeM8TDVfMxklzDug+IuYVwSsktYLpqag/Z35DeJDY31Dooz/UbpFb6+vyu/WfBReNLVVyYdB5Tizzesi1+KyuNHgRzvPhF3sAoWx7o5xPbbzX0c3k5N07lAnxJBpw/lybwBL+nePnCxkmjd76iwZPc7Kgm9+x0TTc1BS34mp3gk/JmcRgfEZ3JKSeo0kpIVNuGODwMig3bhyu9LcZQ5hkPlrKEBKJoVK4674dhQIIRrFjqL2elRnhaGGtktxkpGdguKo0Z2i8tTp5QHDWdN2FwnTJIvh+vvtxIVHrIoOx7wcNLF1rHngxG6QPMrPIiAUCs8iDCSFR6ERFErPIjKUqeShcyTDWSehNe8ais0T17/1I//widl5WMn4+TCLB6ssrkCAArJ6jqWhY851NI3kakjto9m8WL7Rfa3rX3nKJRFdebwgElnDo+S7szJw1bFsGlGDf6TkkaNHK1QjRr5+KogfvfDBeWWBjz+VuvN6lxjvnSiEdR260o66Eq635X0SVfqD7tS3+tKA70rDfa70sDtSoPjrjR4qSsZoCsZB10JgK4EvK60Z3alPbcrDftdaeh2pf2DrmSCrmRaXcn0upLpd6UXR13pxXFXsg66kjXpStZhVxoZXWnkdyXb6kq205Xsl7rS2OpKY7cruaArefD/hl3JP+pKE70rTTzlOxMlE9ec4c5z68uH0MIIZ/GWDv0isPcIWTLxapMsmcjmS5ZMxFyKCJdwyUQOBq2CEpucrKDEgaVWUOLjqgK43RIs8AvbBr4eFviFGYQVba7SRHv7fzkVXxnD9IQFZ2IbyTRO2PpgNutGv0O+RT/UTUvHlxNlVrePtZH8egAH0fNdczgErvKlsPbK0wfguO/ortHTBwPgeY573BvjOk+9QyypN4A/m+P9kPMZ6DvTXVip7+k3VJ85Mm3DOXq6VX7Gw+eep6v1sgZkGYubeMBQruXJ8ibDIfBQlhD8cewDgxCjMcTcId/iAWSi67m6D/Cj1uU7kn/smaOxPvCV+2Ax+eeCMbTDISzo7taROTbtYb/LKvCdUbz8KBUrqJCVoBSoBJ4B51UCzxCLVAKnSmBVAs9KSD6C4GPjRxDVEfEIU0hQRSVA1xVs3TNXm2805qq1RC3O3ynIZ+N1hnvP4+p/cHU9lf2C382kJ7oRMGhwNwIWANGNgIOgshDiUwz00M3VarWwKtinvu8//3tZ+Q+JYjvrpl1Fbj54zIepheijFzWcy++2lwdAtf3nMZG2/1wRVNu/iAxVWEaiWHi13khatSthK+efvSk28cDyYUHKJtRcUB53Z7VZ6HyAYhz6pkLMughsH7htyxzawNiKdkQvLh4Dh7g1BgPolEVBRddMw9+Pty0YPuvYHqzbbvuIIAzXh4a1RBmVq6Y30UNLPnIvrjhuLHLHpNqOWAMkbUcsKrrtiIep5mPS6ubQtUTWzaHT0OvmsPHUPDza9p/zksjtP4eYvv0LSFCFJdBCUHKnDxmCkj/bqCEoQlJUcSkoAg+6p2qN+UayVWEzDO/5ikSpkXXnEKyBoW5tA3fktW0DqgNezcFRpsARv9SIMBK11IgwN1lqRFwotdTIVFLV6aUm+202g36bYcHiDyZykVExeBwBgI7PiRgIFA2U0fvDQrzUbGQOPZmNzAOmZiPnIKsiyMgO1ETfdjyHtTDi/qtn46C9DSdqInfF3rFHundwxcWng60JOiGhelwXkgl2qEnIlq+7vocuXaBo9C/HVz0KZADFokmK636uEIUt1mqovCM6mpSr5ejK7CauzABfm7lX5gH/2rwP/znpSqYRXKH94NrsJ67NflcaHaeuzfvB1dnvSq7TldxJfH32zK7kHXQlf78r+W7yOt2VDu2u9JL+QgGGpU/3Ir6qIDe4b6IXlteHjaVt0x5GaUmf13f0KwXlRKOFbIzky9mVdHdX0v1dSZ/sSn2wK/WHu9JA35UG+7vSwN2VBse70uClXckAu5JxsCuBwa4EwK4EvF1pz9yV9txdadjflYburrR/sCvtw39OdiXT2JVMsCuZ/q704nhXsvxdyZrsStbhrjTyd6XR8a5kW7uS7exK9ku70nh/Vxpbu9LY35VcZ1dyJ7uSB3Ylb7greeau5B3sSv7+ruS7u5J/tCtN9F1p4u1Kh/YufjkfTYQeoCr9205YVTG4l4M13fNXnImbG3qQw0/97OfwkJ/9PAHUz76ABFVUArE/N0lL/48l9mdYHQmFqq164Z8Arjxm5e7PHF7q/syhJ/dnHjB1f85BVkWQscMM+UbqLRzvVkHFJMtRhPq3JMpHhnFrMNxo+Ybv6mGMJL98JJ2Negymk5LHYAYc9RjMxlNz8IiiBC2iKEElLk6jhCCoiF3bO7YHzULn0aw67qSRduble+j1+tDPxZn+nSUaW10+z6i8F/GpFD5UwKqCi+QnioGhZ/nfE+W/EAfqBYnCpdq2sQmjO/cdy0AOMH75Ly43tfwXl4Ms/8UHp5b/ykVXxdBxB/sg0p6a4hV3sP8/T8SvG6sqaK5gZ1X3JelSwJCU3iwwDgWBixfSBUUhzxJPsLrQ3gjqzLBrB7Ol0GoHs6l5tYP5MtK1gzky3ijfR63DE6ugONM/V2Kop/OmyMiVqrZD8qssfpqRLKVp0kiWfg1UIxkFQWUh4MZ6VVTMNvqm/VAi9wrRswsg8XOveMxUdx2PgXTXcaGp7ro8bFUIGykMVoKEBSETh4B7k8ymPdzUfR+49qbrHJp4d3s+65w4L98+jiqa9VBEGVBuMrDrJ/mlo4NSv3R0UvJLx4CjfunYeGoOHg56r+Lm8ThqJuhxXQ8rbb3/5niT2tx3bLAxGfWBGwW6Jw0YzULn5UIiWM6d2AfITNkL0uPgSyoa/X9aSLd8j/sZJGTE/IkEWBi9G6LpqOsvBI0JcIA3mh9GAkuJelYg65EBkoK2QOC58a7CXD70BaC35hBlJltzCIuktuaYRqY6vUx2z/acl0Lr2Z7DwuvZLiAt3bM9Txo1L5k+gVJ5yXQiRl4yG1HNRaQNkTGFySEyiOhD5CCqeYi41jFKjJkv46tVNSyzm0zhRLbYJQd4G45/5RC4lj7G3VxtH9zw14E9yU3hzIegpnDms5EpnAJiqCmcYnLUKeQky2CWq8nivPPhJSx5e0WAYT7DtjmCkwhmunsoWIp/e+XwUm+vHHry9soDpt5ec5BVEeTEhwtGITTiD1foQlR+INFyDyHtXnENuAkOJq7pH+MOLzBsREsU3TWSoRtsLmroBpucDN3gwFJDN/i4qgAu1lagH1yMMMh0a7XmakhbP5i8BDKBemii8XsU/v9Ha6ixXBndmYPSEc0wlvF7C4mTnwsOTXC05R9bIK5vhioTXliwYM5fUMxswZqA8ny5t9be2Vi8vLyEYl9u7g97A8dyXOWmB5ebK+WVZa0onwz/Ukb/Q5wzqdKQJx1FXTZqyHYdVWn4jkLi1bjmSHePYcBsdrTX42TXETDMyaiH3y5cFdpd8uk9x/Z7RwA2/VZOYQritTOxE2OLgtCwl/9nP/kfPnITbEUVb5k4JDlOft+x98IM7ehGk/MRyYWgf0Ry2VIfkXwx9I+IkBx1CjmJXpqhgzB8+X+eCHLuouA03TJfgv3CAPCDIjZt2wj+DWa25AY5i8FQg5zFWMkgZ0Fx1CBncXnqlPKSFYqDem1hheKwfHUydqULBo5rLDqui+9MYSXMRPEffuxKHgDV2JPHRBp7ckVQjT0iMlRhGdjwGqRmNZN2tqjw819L8qUYbg/5cA1zzwRGHMG7fGNs4jDYJd1HFfGWstqtTI3TcaJJllSyEG9xpl8pTS1wHN1ACZULS1SnlZiY15WgbE16Xv99Kc6HwahZP7eWVfd9OVzUxBgqJZkYQwejJsYw0VQ+GtuT/YmTcT2eLrD0G8EWEdU9eiFVO65ZbhQL/U66SSWmv7YP7PWJ5ZtjCyTuXx5PBpRAlJoLJChTSVB4Eohy91+SjnT+fEpiN+Pkg9KacfI5eM0482Wlm3HmyKKVZco+PVmWiaIdalkmOo7KwUlUQqtW5olKaN9ALG1Lv0GpRp63tClcjKVNoUwvbRoYY2kz0FQ+Gr4Yacj+OR/cv+fRpbI1H39wHo0xRs4hWLVxQLgZ9zZHVaXhzmdlLchz8sMu4uuZCcagPQpsmuLBAIrJWDm5p1seSPrtc+RR/fY5PKTfPk8A1W8vIEEVlUC0eqgkg9zCBsHK/0EcpDAaKrmWWGSJUnN5Byk+AOMgxWdKH6RyRDAOUvkyVGEZWK0oNqalaUm1toKmT6mmJF3wtonpop5rjrvp4DcF1wlujpcbYZLDz5ipXJ70TOULYMzUXAmqqAR8NsU1VSr4bFqroF20WaYe+REaNrSH6Vmwr1QQrCowU/kAjJnKZ0rP1BwRjJmaL0MVloF3YNxJr1FNll2qhdVTP1hIzjMP+DAELe7L7F2xYVu4Zdd1XFTOItEO4nFhTsgXlsuf6T+uiPKh3loNVIohbLXyf0lyojoWjH47BND8q5PNkVxnhEJzkEmNX+REAIVa5ESAjyxyIiKIWuREUJI6jaRkfZ75FjWT4ScSbvAwNGzLHNqrdnvPB24UQJjrBucxU93gPAbSDc6FprrB87BVIWxUfxOe86rzVdSnPLC+hbGSytcU4rxdBBQdjtL5xijwh2xyYSRLGfK5YbMFaCSZq843UFWgyEbZCEbyqcTGycdCi4W/ceYBUDfOPCZy48wVQd04RWSowjLQ641tvaXTjTr6j1ao1D+W5HsisIl7CI49YF/c2QpuA6i8YTH4D3h3rFUq5XrxpPaELAO755kwE9FQzr/t+WutgbbvVMqTod2pvq0yeK58vdbcef7FXU9T5NNj1+lHmZuVuXL/rHxXKDZAx9I7F4mraiCuf1ZhkCfunbudZ6JuGImyGUny4kz/bImB9IYohChZJiPNrdK5u08FXgCoVO0kUiGOox7d6Ep9F8dqm/b+R376nR8/pfw3ST6f1rp3sVZp8dWufWHVrgmpfeZvU+0Xk2o/hXXI0fvvSfK5lN5f1Pk6l7+wOpeFdL75t6nzx5M6l17UOfqm7C5j/+JC9zWVv5rdBamQo/Vfzs5ytCHBT0Sz0BnKr4f/xtnNV7Q9zzu4fLiQ0PfG9bUXd/Tn5qn6TgQvo4MGfuYnqFv5nQqNltjHaVHPMS0Z9ZzAoEY9k3wqhW/KvfuzUtxxgti7Weq99wuo3nvz1fvYF1q9U+7R/1GKF2+0R7N0e9sXULe35et25Qut2yn2YsqugDaS13YFkV0hb8/9rkQdTWwDgMGF2/oB8DZdMACGUMFcJie1jiaTmqyjyQal1tHkoqr5qDDQoNlKBhpERtr3JI+9iB9n7MHyArA2G+qjlNHPvXymzqr8MEM1JGFxpn9viQ/ViXwQGYVksVQuFi6+GlVXrragKRAaJhq1cqiPDxfk2yOMfd1wjpaurDcLnUeySriDQtmpyqXsk4e/Fmf6d5QoTLVoGSWfMcmlZrlwtHwFpxeEpoGvSpjcYCmpdqIFBQwsdixwTXftwJDZIALoErazHFbUhRHad6owKq1WKYX2ikpoUf3JpH0nBbYE4P9fmPg+ykPLse9wmOn2HQ5Dyr7Dg6bbd3KwVSFsNA2DlL3a/JwGawAHofyNMLXrZxJRfVumBWzfOsa+g+cnumcuTcYWbJcAvNzULi43NbWLy0GmdvHBqaldueiqGHqy7H8LV1nQkE7r9dD+/O8S8WlbB+YYZgiZ9hAHX6GA3n3dHgIjcgLy49PyIajxaflsZHyagBhqfJqYHHUKOdBE3oR1w1AUBVLpHyRPLCPdgnXZR0E0f3Om05HvCsMm2gkCr1h4yzd/38cL/SeURP7YHnyEddMOPA0GHsWKg3p0H3c25XtCrA0HQQUXsBDxW6ZFXJLvDI85AWIAlbRC5qJsyQ+GxZ5549OnGRqMVKk3E5GWtSbutfRCQfnDAkXlYemomU4zdpsU+k8qjwuIxMyQM+4znOgWlM/5ZvnWQAVx6+RpRCMTKDYso+Sl9FMrP5e0K5Ptx4PgjEV94pvOxLOO8+3KOQB0u3IOU8qunCeCblcWkKEKy8CZ9FXkZ5nXyEz6XzohtyIcFB0JC4BccS87Ey/wQMenRejZSX67moXOHxbk+xILu5eIqOxVem3b6GlFQ7ssR0lMUVkrNuzWwBkD5U49MDVZGEuHWNpy7IjDB7lXBNN/5lU8decrCvJCdlpMC1Oc6T9TehWj+MqCvEiZOa9kGOorHwbOQp9HN4gg6kYLQ5V/MfmV9V1z4Ad5MkFRPChm0bENEzkJ87+yuRD0r2wuW+ormy+G/pUVkqNOIQdFKbaSUYqhZj+daLkR1JeKfdmrtucD3biyFznnc73RAhhUb7QAH+mNFhFE9UYLSlKnkYTTsOvonqSF+W+fy6o2WYvyir1qh3Mffm5FVcvD4KmWx0dVLVcQT7V5kjKq5THAMzhR7/KWShmVcqlEjv4PJNK34xppG9glGvYQXpZvwX9YM0emX0UdWm/x0F8uWvBPSqGaTKWhA1FTtumkZMo2A46ass3GU3PwiFyuVvLCUgtDtH8v0dE2OFSjGDHfHMA9Y8VxO5vNQudeubTpApil4xjRGTTqV536PdPPukAGBlfTgcGqkjsGatFGFjFZtJEJSS3ayMNUczFxxCw28GhR7+B/+EMfO3X9Rz/zsa+XlX+eyDQMQRzHXddtj6yewk+cY7NSE+fY5GTiHAeWmjjHx1UFcHFWKzTlzNcaQeAOPlCGrfRgdavI3rgNYA0ndwQvjvDfg1MhukpvyU9Ebexy6aHFVcmHpVpc2aCExZUNSrW4clHVfNREdFwdtl+OF7sWLvZ/kLC8boMbftsFOr5+4yLeuZZXGhPV8kojJC2vVCiq5ZWFpXKxsDpQeGBYmCns2FibqyB1JGPYUWvHMCR/xwNhNhA/hp3KRY1hp1KSMex0MGoMOxNN5aNhpQQ2q6A4ETpbN5qhBeu7Ex8EhBHEYG87B8B+fqJDx06z0JnP6kXNZ6Tu4ixichdnQlJ3cR6mmouZDPWvEqVOtDDV/gcL8i0hzI5tOM1C54GsSm4liTqPy3dla+bahlOc6d9aIkmfkM9Qqt8GtCpBmxhupVFLZibUacmdqKlpnLuI/qVtG0FtTtMeogTn3OROMRhqcqcYK5ncKSiOmtwpLk+dUl6y/Fu1kYz0Dqt9wRDfaMJhWxS6IoC3TYDnb5sjgDuqHMtnwnNVfeyYtj/vgYFjG17R0FpyRQ8QehMEgTsXuBik52OU3gjaVgM25UR9vlxOLkqWcOqiZBGTi5IJSV2UPEw1F5NoYT4/V4lbmDebwYb+K4kr1s7Y812gj9qW5Ry1DXzz1S1Utm3JgSVHvdwrlgAG9YolwEdesUQEUa9YgpLUaSQhq14ZHcLKFVzEpRwGiv5owslGQllp1fKdbDxmqpONx0A62bjQVCdbHrYqhE3RWyWM8/+R1/TG1huyljSwtSRsTfXeH3jv759SPiHFPoIIYuLvA9tHLjlYhsiCVSZgmaRmofOmrO6enAKBWhA+l4ssCJ8vhFoQXkiKKi4FK7WGXfVh9fG3F+KbVIgAr0zwpSxbTts2OoN+s9B5NpkX83QcvEnhYf/YGfRRwksLJbzUgiG8Q4pTAUKOZcNEDSXgno9vhfA+jQ6ZN8XensdEGTtvijOpLzsW7oFVmI4/NGEk+RVBfuRqwgH2qPz7aXi2nquVa6EO/s00OngmO6XFn4TWjIzPQjYjy4GnNiPLx1dFFXlWOV1HLeugu11LpE39LEeDZJmCqTVIsgtpkGThazAFL6TBLD5bgyQtmorwdFptasjrGfRZjKbi34tNSiHSFrANXLExEaZQz6T0PCTCCeXXUYF8rYXk19FgqvVQ/rsTwUMhDM6hhIU6x/7yjbGl23pQP/qN8q3hwqYlluYAoBAiLL9Vn6tppXByNcNQpn9SiIN6STBjx0QdX28P6xz1wgJQRUN7Un4kcTzHbL0J5utNzB78gw+LliiFSv8ejojk9TywWQRH3Fq4hX8m4fqK+D3QhiVwbd0HaFggbHGa6/rKh6C6vvLZSNeXgBiq60tMjjqFnG4JRkJFbV4rteT38Ztm5WoE5YHAtNYFR67pA3fVDpvgTvoe8FGXbNf00My8nNXy/CvC6hzJb8iqW5y/ONOfL70iwTfkN1JewHSS1VciOWmpC7pQBYUNIy/F9yWs5ol3ax6CLV/3AfxYrOvjXKs5m5VqNWeTk1ZzDizVas7HVQVwURkvDUfXBpWlwj3sAwlrOTT7OaMNxwCrxorjxnVqckOQmZxUgziTmjSIs0GpBnEuqpqPil0LOOixggsbhLMsjHn8NlJZy5bTBUNwY8VxF0x7XfcH+6Y9FFEWnZOlLDp1RlkMUJay2KhqPiq+L+BC2FGu7S+UYlP3jgdWK007WNTIvYgSbH/+RFY3HzoRn6/wb0tgDN+L7SMfuekfBzWm46BRTBf8+coejheB1KhpcKoGfMout2wPTRs64Navp/tcxmV/g5HDOJM9yzmSNUHCtm2s6bYx0t2DuEJqlmdk2mvgEFhaeqwJorHuonNAWKzpERblAvCPALCxErw4xolPd8WNBno/iyNLkeoJCB/bNQ3gJcrBomK/uAT0qr3lwFCSoFVP4jDjgSijD82OxM0wO3UIvvXryZ+oR27+LCKP3Hxa+pE7H18VxafFN3MnNxnfzCWlxzfnoquC6Ow2EOwFR2sDwabmtYHgy0i3geDIYDemZS5yWmNaJjGvMS1XQroxLVvCJGpcJ/wIiX2qONOvlV7B/tY5jA6f4g+Wkqu+Erm0otKcLZYsKs0hpBeVzkFWhZDZa4W94dPWCpuat1b4MtJrhSODvdmyvjK0zZZFy9tsefjpzZaJT+u7IPqRJPsuiHLR+y5MI1OdXibNQ8f6spMeOhYV3UPHw1TzMWnDZB0vyGGyqOjD5GGq+ZjUEvbsM06qhD2bkFHCno+sCiEvyirtIkGeuIoz/XtK7ANZZykqLkleHLIoKgfl2ai2JDGU9AmvONO/UOIdATuXo7ghcjg0JJWLxNAOea7MaCd17GRpJ4uislFQoh0Ovqhp2pzWLEUVy6JYjK+ZJSwSm7qrjwDsYPUSSIQXNAud9xTkWzac6HcYCXCnfHoArYmgN9THXlAGUntAPgdujGECgu55qI90zws6rYYk5+U7zKHtuKCnJ4LswzqSD4mMiGUjYZBnbCQsWJaNhIOrCuAm+mU0G8H1H0UPlOfDlNGvTUQK73ggCOm+uhhXGMttWkhno8YF00nJuGAGHDUumI2n5uBBvxzqiXoiMn3/RCL7c8cDO7buec7AhHbpNdg/20jUH+dnf3K5qbcjLgd5O+KDU29HueiqGDqyudWxzQ07+efDiPO/kuIrOsTA9vxMJlq64vNyVo/a9EDUM5AoM3kGEhZJPQNNI1OdWibKB9JwV8qw701Y6P3XpLin19WBjVtxZyKsjjgRVk25HLlwDgd2DyAIkQCrB3JFU/s2MWjJvk0sQGrfJg6imoeYjK1qtaixVX+SyAm96vgAlrCGpXqhabpZ6FzMzuUSm6HTjjodJlSSIirO9EslNsRC9NFPKoGCoTIx8GMHD1rBjx20cg0txO+Q8V4/gjN0U/e8I9iVAMUZb/kT4xjZQD8wS9Zur5TLjaKsLcsPkxyLuj0AFghqi2y75nAIDdXK+eP1xclmfb3mJIqqbO4//1yn4l/XluSHSJglYOnHFJT9enO0NXKHawmUbn3l2tLS+lVtQVZJFFSSNIPxfLu2vOi+bbyRwNipvOhO9tpvy44kaLKdQekcHIwXnz3Y3U6gXKs1jw6ftV1qkRhTfhC1a1pzhqaNPB0LYM9xASlOvhB2eEoNA7iH5gDID6ZH1x+Znmc69vrbLG/NGQ7hcePtBfn2KE8gflt9UxEZgsIfgiI0BKKg2EvpbJgv4ECSNyEBmfgmJEBI3oQEkVUh5GRwPVcDOLieS0IG1+eiqTloSW0KaB9rU+Q1EdoURFZFkLsPKnFpag3v9InaZ/sf+cn3v3xKeRf0ksG2oJ5nHoJ18wYwUHap7cOdcTIeuroBvwHtbEH7O+QTI8cAymndsi6OMUD/btyIOGBM4mVa6FJo4ha6NIBMC10GgspCQLmxsIPu6SbKjdXC6v7X/+Vf/u7HZmHCXBHRk89+v3w+kyC3YDmDA/hvV7Wi3HlAvpChgD3FoRHbOkYkJflMhqRtWei3s/IdmQw79MMZ+fb4h3Bb+bc3dZ5I7yt3K1/MaodXkioKFQyiMa7/8sff+69mlb8uyPfmzrxskIt2ezDzXmc7F82RPgTeF/m8g5VPyuhOFkS5Xf/Wr/vGf3pK+SmYtHWo+7qLa/lsHdsD1MX4mmkbzpGHGsnL4fFjvgwLtsp3UTkgYRjZiAkVBiFR2fWx9ExishHFMWkUQXFMKjNZHJPFrdK5k61KqtUw8fJ7f+PlUzjl8tAxja7u+cBdmsBe4cuWDq2C0CjoDeCBmB08kstKGkZyyQPDSD4saRgRwlUFcJO10qtByiX8BMzPV8OclXdKcgkDAdzGOrguLDpjE8UDXsqq6jyPhWxswyQLGtuwYcjGNlwclYODQ46C7i3NZCeBaj3qefFZWHgIQmy7Ew+d/l195EUqKKf6NbUq5WIBXXoYPJAjlRqNOBQ2B5E2/WR6GfI4iQsWgyi4YLEgyAsWB0NlYqBVWUW39VarGa7Kr3v7y6deKMC6d3cuORuOv3xoDvwrdvt6lIX6UHZ63a7clqLrXJLPRc+Y+q0407+9lGEoy3fHT0ThUNMcyOgAk1/gGkFlBhr1RnAh/o1Z+b6FFdR9YBO4e447gre7dd3Wh8DddCxzAPMk31mQzwWPgv/WQxxb5kugXjS022R5AP+755kvAaVQ10ryXfDQO3SdiQ2zvOLfyv1GrkD5zgV9cLDiuEe6a0SCCANEDgI2QOQQkQYIAUQ1F7ElX4iHSHmG4kz/TIn+dE9HHx04GAavSuUletg1klthZCP+NmhbgvkMW2MABvtbx7a/DzzTW7V9J3goZjrsjt2HR8AUJ3T0YUbCYZZHjB1muZCEw0wEU83FxKV4cLhhYFBvRl017pSLac02C53fPhFNe6yQXhnVeS1r5UrxNu0p+WFoedp21kzYmigNAFu+YBvbbKVZ1u6Vz+iW1QM3fGDDC4zXQwkmwFBO+O4EaHfKxeAPvSPQh5VqPWUmu7Qek+8fwAtmD1jm0MTWh54O+2D0xvoQ7JsGCAAflO8Z4FNZz7RfxB6Fnof9nZHUc3IRoCfsefoIoAK5MXs01t4IeJ4+BBn2B+S7YTBsz0M9Gnr62MyQsPcCTbskPz50YZfl4ODvO7090za9/R7MnjTtYc+0e6GlUqqXtfOyMtJv9PqTvT3gAqPXP4ZqOqWhpsHa4/K92V8hdm/sOtDEo9xUga+vXNbqshINtLcHdH/iAk+5fx0Ypr4F0A1zdTS2rtjBtXgR+aug6+rWiY1yVwN2RbKd/kJ2+sh3rbgAvASu7ZsWeA6AMfroAfnsGn6ybd078HbsPUgE9d/5cvn2ME8nnmQnz/0/H/hEob+gZPAVOr7CxM9O5kosZ+W1yfzaZP48TubfPEGcK8PJ/O4f+EThtZn22kz7PM607LamJWbca5Pttcn2+Zts3dsD5zn24QY3bFiPiLwBhqHI2GbNrEfEYyLqEfEIcT0iLhRRjygPS+ViYZMD8jVWK5VEPaL5ctg+9ncl+Sl01YDJamNzDJZc3bSB0fYW4KuGFdEnI5hIFd87sib3/tx0IGQn5SkYg07K04giOylPKUudShZN3TgOaz6Mw/qLgnx3el63x+MF3bZR+Zu5rG7v4XAQUWhMKhyFxgYhotC4KCobhbjWtpKt2aO59m5Jvj/Nv+A6ujHQPR/OWBuVSSxndXBBvifQ8ZUxsNM8RHgghw6HB/KAiPDAHCSVh4S9/Ni8pwVZtdgk1QhvsT8nyY+ktbGoj+G2iZw4pj1cAr5uWh6zu20XDGG4kNvZ2kL2xnD1h/zhGkgmJwjy4OQEUQFEcsIUElRRCUS5qVayOlY1DMj5x5J8X1qfSzD6DH5lrjnuAVpgtawiH8jlI41ZfNrAmJUDSBqz8hHVPES8/FDRh9Z80FEZ1yith+XD/vEJ+WIaJdDFiuOu2ga4AYylhUXHtkEYiPjlWXU9Jz+INsVNfQhgQcDVJEu0F8oPpYm2Xd32dJKKDIvORw3CovMJU2HRYsiqEDJR8EbgGYOCNyLaIAveCGKrQti4Gwe2M9aruBtHaF0OPTCwPzR7gmw49qY1GZr28qgPDGY5elEAIplFlAknswiLIJJZppGhCsvA6w6vtGoz+dmrhtbc98zK9TQcbOEdvDC88WMfzYbjb/mOC4zLQMf18Jysl/lh+aQF84GU8y7wIPXFiW0Bz7s4cJwDE1wcoLOf9jp51vctRaqO4GshpSAhy7YP3PTAiNciyoRfi7AI4rVMI0MVloEjN6vIfAzda3ONctQL6ocl+cE0ffKWEtxPmoVOMzvDHxbiJfY1AXq8r4kAE/uaILIqgowN7jjWOCxhGn5a/zdJVtMQG47fxdPP6ALdwzXv6WVvMl4QYBtZdiItTIwFp4UJwhNpYeL4qiA+zsIIDv/anNaKKmLWymFg589SDsDL0CJgxFYPVFQ4s+bvke+E8cDwOm+ZhyC6wc/Wy2VYFiCNGuMFi4UoC5BLjcsC5IMSZQGEUNV8VLyl4nB3jdhS4UkaKfI/URQJ99Ktfd1NHPYuZdzlraIMQ3KCj2KaBTKkvOWIQWEyEFGVtOgnFmMy+IdBg4N/WABE8A8HQWUhJKNXKlroJ3/vn7x8SvnArPwEVC+2MG3r/cDCsuI6o6w3tVnofG1BvjvQNBSzrpv2CkyQQSGVRUO7W75jZAYNHYE91IcovUGRKvPa/fK54DPWg5X5ensusppBRmzugqWDxAdDlA4SZ8Olg6YQQ5QOmk6OOoWcZLR4AxfYhqeMuUZ8rP93BVlZgBU1cBHTJeAd+M4YRWhlNuM7aaREe8vsz7i9JYWNaG9J51MpfPiR0FWuWa3N1eEjzaMvdDUsvfMpST6/oPs+cI9hm3kXprnhGivbkyBPrZbyWDQqMLbsXj4f5Ep2AA25FD4XEZiWtPTxuLClj4tLWPrysFQuViK4COYAJrsuhMfQP5bkOxeAPnDsNaAf6EMQxOM2C539aKb0DisoSk+7T74D3i6HjnvcG8Pomp6tj4Byc/hH7bx8W0zggj3zhvI6feAOxhCif4Yuq/NIKhjwjEKnI1T+aHpfZXERAR0UgiCgg8ZKBnQweFUqLw44wofMVi3cSN/5Ox+FAUd/UZDPLAD44nDG0eK+OfZQFkez0Hk+6v0Gd80usNA63XYwKfrsn8H/DqvN48BdnESvFDTYmDP9o0c05sz8ihtzZpmIxpxULjXLhXMdcVWzSi2Z69gIt6b/5Sb5wsra8tKzywumATe5tm20J+hiijMEm4XOZ09mTzvffkK+f8UCxhBk+J4Dx4uOvWcOlT+f/Tvqvu+PvacvXRpP+pY5OIANsdFhdm44GM+NXfNQHxx7um30nRvBL97cwBmpT9/PZh3rOdyX5o6AZV08sJ0j+9LYdXDxwYs6HuClw0qAeRGCqk+9GlFfRAPVj7xXMVA+93QD/Z+0ry3IF9jzY6e7pgy/QFqDjRkYI8EznMgd4VLi3BE+GJE7koum8tHQjQ/2mS4lv8d45X6PJD+0YDojAPt6JQr+IteS64xAfMyg1yljMK84btCMk7yQ5FEHF5JcUPJCIoKq5qPiq3E5EYvWqoQ+jZehFd8y7YMtWKFzYsH2cqbjmr75EtjQD80hwlrdXORcj4XYyeuxEEtwPRaDJ6/HwviqIH7ygFJpJF2RWqjK34cHWcvpQ2/bpjkG0VnvLfLp6Ky3voCuE7f+v9S9C6wtWXYY1Pd193jm9Njz5s6v5830eOZ4PvbMuTW79t71m7ajVO2q6n5v+jfv9W/soDf7VO17Tr1bp6q6Pvfe84RQCJiYMBjbmDgBIixCCIkiObYVARYGDIPsBDCBGCko2AkWJGArCUQJ4Q9auz6n6vxvq4nkKz29s39rf2rtvdde34BnPABBfS2v/i6MLEPVsKRwN2AMKdyN4obC3Ww2pHC3thtvadeL+ylZ8c+YMmy0pTUeaN/+7/7r//tHn/zWCTim/iQ0vy+5fR4Q/Kz2klBz33dIA3e1GEoDd9VqpIE7gQylgfugjHdDAb+3hnTCaxhIUa07rUtg0hqa/I//80/89Hd966R+voCw5+UojJxlQ/Xtfr5sVF3/uGvF3cddb7b+cbe0G29pVyPyylfoShKltWTPvw7sskZ+5adBJUMOp1X5RiHyFR0rFQ16Ps8/f0yjgdnH4eq12ccRYAdmH8fBHR8BF0jjFRpg485TRhur9Z+7NfriW1HyirhyXvJe4GXZsCNf5EkIh+7XIYTXVVQGc/na+0hnPrsqgO1w+smdEAbbYWetejvsBjLYDnuhjHdDgRPB1KQbbKP2iW1IOz9qNJGA/8snR6dOmhblXTAbaw7RpQzRclvmP3TSct4RyLdHz0j7socBeOs5PdHwJ0anJfjhf7gQYVQtHjbxBPHU3AZ4NAYWzevzXPDwxWg2b/NlpYYhMdxXGyCafbWRv7avtrYbb2vXx+zDo6sx+4hZDDD7OLjjI+DWr59agNYcA43bV0IaY40/++To03Ke9UP9tVphCnxX1SDMJ+79ZyejD9QngB3Ht0P8vaNPZN0FCs/qXDaNklmj+vWF0ad3VHiYJvGy9bzzhdFzvWp54572YZREpfQy0kD71Oh0UE2y4VoYHx49U/J8JsqHVR4Xp7d+5B+T7JU9ExryRvZUbHgj+0ANeSMHYI33wqoZTrX2hVbzk5toiWhlePVLHxiNGyBFo4jQKZW1NI00wfrtYYxo8O9d1MwkHWndTfXvnTRL5afgrXMd1HL02dUOeCC21lBkjVfSpJ5We9g+KJfxlsEtR1/prcHBys/Jym9GoUi3FG9+yB3T6H3IHTW2fMg9sMb7YfVNVw6tX226cqjW0HTlGJjjwzD74cdu9hHr8GM3azMMP3bz/sY37a8ffeoGOFdHn7pBg2H0qRv2NL5RT/1H+t6dUT/S92+ewSP9ILTxfmj9sICUDshNraYdvnVy+ivfNfqohFLfWUWPftBHuG/Cv8jSIirT3E7Cu0lWlQ8CkfA8Smsao5Z5Yev2yb0vjsbb2m3U+4HRF1b19oP88uiLvaqv1mO9C9L72jNOv+6e2L6y/GPbHBA8vPdPnqyzirPTL8qBrKawtkbSN08Yi9MvbVvB2px5MK/T57aMvc6QQSUG7/Sjum7e6UfVXXunHw1/fCz8vq7gkStS6woeWXmoK3iDHsZH9zBw17LvWzXuWvZ+zqG7lkPQxvuh9UM1t2qx8+/8mb/6F993+mB028nTqwJOKx5cxFFRPvsf/omf+/NPmydO+0q896HRM6+kXfntJ0BO0r0gwZsDgehbT+l688j67ZMOasehAXjtG/hfOhk96UfXt0P83Gi0kkqefog8bz6PFPV59DyBP/wFEBy2xe1x2q9G4Q9/ZjTyO8uE09vm8wQ/jxTSgfni6HRV3oHp19PhbyD+kfF8TBk0R2oDtBF0Ok94D0efb+coeB7MX+CluOLL13N+fh4FL0ezvH1vf3iDpdA3yjcb+aqU5IACAm2O13/x1ui5tgf58BSgBCbuFi/kYgkfYmfM3r2thrfOvprNrbMX2PDWOQRtvB+aVGZBwLWipqUrutTBqJVLrXbZ/69bo2clELix0iwKipXXUPPk3i+djM5WzEB4Rr1RStOauu7DLqgRRpioWCW3Q/x6//kDtiuyJgzo1NKMialPVKxPVGpNVANPVBNNVEufYGRMMLEmWKMTrJMJNvAEm9YEW9aEEIw/Nrpd8us0SRfLh+CQIkoTeBjf2T38gSeDXZVqTwY7QQw8GeyDMd4JQ8YNamRq4CHPAksW3Gztf3X/8j8Yffr1ZtJv4rXFVhG+Hf4eXpYxqLLq0iacagrBdwarZNZKl7dGn3Kk1RIokffNNaQnu91mBrvbDM0MdtdrzAz2ABqaGeyHNN4HSbJZZdg/E1mKTu48Y8iT0mxjH77947/6n/8csFmr0cecKglj6fun0Wl+UcSZlEp85sXXX38NUkzArTWMdyaZk5/Y0Rw8V+ngq6JleOuN78a3/8hv/af/znef/p9Pj07Ba47IpcZr7RwM8POnnxx9sO3n7msvatJv1wf5JY/izkkfT5b4D4y+W4BbjIdl7env9Ough/C1rAX1kAeSB/YQeBfAEWvqPR/IWE+8TPOv/aD2/JX0W/Q11UTPNxXhNw5Gt2voVVKIWAr6Tl890MGqar+PH/ohtKuTt0ajppNChKd3D4Evjgb8kdEHi5rP+DAHdSlYsOkPjp5rA240Xdh1Dw/qlqNP3X3txYc7CgdX0l4w9ZW0t8rwSjoIbXwAWn/37ZlCvfv2zXGw+w5AGu+DJLm6NSNXxhB7xpJXpIbaSIP/EQgCNrD/vriMCumH7W7ix+nVTvOeXd2utR+Q7Ee2qUn2YzsYkOw36GF8bA+1z5tawtoq07cB8n75faMvrRYxF+eFnYSvz8VCFDI2OpB8dSQWOFe+fbK5lMuWY1d7uxdJINY3xecfCLnhpP1RDJLyprwPfvTcjlr1aEYfrv/vqbwNDIcOjKE2HDpQaWg4dATE8UGIfXuWY1ahtmc5pubQnuVY2OPjYPdPqr2fpT6p9lYZnlQHoY0PQOvrYG2gRK2DtYkpAx2sra3Gm60knd5oT1JLIbSnH9oqU/6dk9En6v3zatJobfgpWAxVcPlPNrfLJ3fWH2otb6/TaC3vADDUWt4NYbwLQl/8qvf1CKjZ6hH8UTR6zlnCDcvSfOp13gGkmVTzXPtXNkXr+K+fHWh3+stniFi+RVxXdX2T6L7qUg8jkzLV8XzXobptatS2XHWCiKe5JrKpxQiyfMPRNM/VHRc7yHM9h5gIq55LvQkC34HUVB3DMV3D1wzbcmyd+o5lMt/UPds0qKb5bIJM3dMtz7WQobrMwYhZxEamausm0ZGlE9tBFjG1CTIN7CLVNFSHGb5BsekhQ3MhGoRBbZcSzUSOZmgTqK7ryGQmNajmIxtpNjOZZai65pquodvMJIahTpBtelQ3HebZtqZjHSPDdgyDUaIyojLVx7bpG7o9QQypqmupuqNqnkY11VMdkzquSollOKbHLNOnmuZMkKszzVOxo7mI6MTCmuFSZhLbUxk2POarVLWYZU6QZ3s29l3PQ1hzLc0htuMQA6nU833NtkybUcezvQnymatiEyNdo6ZGNdPTCdJN4iPL9DVLdTBlFOnuREWWTQzHNC3PYKbt2YaDVEQ0zWLEc3TbN4hjmARPVNX2qWWoqucRihjRdcZMWHbTI8zGtmu4hushY6I6FtZUzyee62o2drBpYEfVTYp0n+i+o5rMwD4xJipzXd+gRPc037RUT3U1jxiqTn3DcywL2x6zkYfdieo6qqrZlFDqaCZDjsGYqlmarmPP1JnlWZghk5oT1fVVzzVUA7tMcyzfJ8SwkOW5GiPY9VyDQjWsTVSPGL5qqxZTmYZNT1d1nRiOijTmM2oS02O+hh06wRRWyrGJSx1iaRoivuUgV8em7mmah3zX9airWxNs2pbn+q6uOQwbjoao6WmqbtuAPoRYvmYzxyFogm1dVzVkOcyzfJOY1EGI+czSTWRoluEaqut7FmUT7GHXtCnSKEGqgW1H1WyXMWQZFPnE8zzLQq6OtAlBnqU5LlEdVVVVk3nUND2H+kzzfUMlmoeYZlGsTYjq6SpCLjMc6tkOtR1fh09HPdVSXWJRzSXMIOqEYIIAp2ykqhqyscFU0zQcAyEPqZaDDVt3HRuZE0J9RzcQ1amj+aru+ZrtuDpzNeJhHcHADIZ1h0yIYRqabmBCYN/bjFGfIY2YhuMR5CFCCWUeZWxCDJsRYhOb6i42VMY8zXBdnTnEt5mnO0j1NZtQY0JMh7oqY4RYmol8DekOM3WXImzY1DKwavsOdRxrQizNNalvUdfGpsGQR13H9i2bedQwHctQEUMIW/6E2NjTdcps3TKYjrFnU+bbFBGDOMTTqcqQaqjEmRCHmYQ61GSYqcTQNZUavmO7hop8w8I+wjoxTNecEOZapqETZiLX1D2EmKMyyuDYYJ5t+sRBjk19dUI8RzUYsXzT0R1s+rZPqAdCVE8zbENHKowN6fqEqhrRGdE0ylwLtrWDfaT5KuwL36SOYzi+SRw2oZhZuuubBrMsw8QmU03NtV1MDZ/qlkWRa/sMM3dCialrvulqmsYwUn2PatjWbIoMhD3b0jFFnkEtNqHUosS3XVfXLYI9X9M9j7imptuWb9sutU3bR75rTahOiIlNh2JfN4iHPB1TQ0OapWHdZZhhiixVt9CEGrYLxxfEg2HIJL7PNMoYNplLXNXXqOMjx/fcCTVNyqjpOzrVdc1xTR02kK362MXYhdNctZnjWxNqqYRqSLU0VzUMShBWDWY7OnWIprtM93UH+dQkE2r5vutjrGJPQ8iCC8MjRNUs1/FNjTgao4ZGKJpQG7ad5avYd4jmeJ6t+75qIsNmholt1fUtwyKmMaE29RCcxY6uMctjusZMW1c1R3Us39E0W3WIp/ruhNqm53kONTRqE880GLFVw2AOUalBbYIsHRLMnFCPqrrhuTZivo+xj3WVIV3TPMuwHIsa1PF1gyE6gTgoBMOSGY7uecwybMOwiYZcR3eRCwcRc5HNoKJPdMtApuH4LvGxhXyKXYQt5umGrzLDIg527YmGXNc1DUI1rDq6qunUZx42iG96OtKo4Zum4zDHm2jY1DUHI9Nwke0wV7U02ySu71LHRVS1iUM9hxE60TCzqM0M3USM2Igw5uieTVRKNdel2HGRqzHb9CYaMVysM0Y9ppuqZevMtj3TMR3N0gxf85hvGzalzkQzqGrrGoCzbYtoqkaZZuimSlRkIs3RfKZilZGJZjrMR5pNMTM9BmSG7hq6r6ke9lRbpzaDG8dgE83RXUY9z/dcH+nMwY5PieVRHRm25ti+7lGNMVudaB7SsGmqDjWp4esm85npuaqNECNU9ZlKEbKY6k00HzFqOAwRy/FcpjrYcnTTUk3f0IiBGLYwMnRbn+jY9F3XYA61XKozX6Oa7cBPh6imCeQRor5lkIlONEMjhNm+71jUtjxbwzp0DjEULE/VNF31qG5NdNjODOmeqyJP94nlea6GAW2J7pqGg4iFXM9AE93QsOlbDjUcj2rUY1Q3kcUIcTH1MVMty/EoMt2JbvqUeLqhaobBfERsz0KqrRkmchkyTMMiRLWJbU10W1UJ0IK+pVuW4VqODben6ljIczUVaaZDDM/3J7pLbN1QXdthqm8alops1bZ1w/NMjSGMLM0lmo7pRPeoRanlarqLiOqYhkk0xgzdMAi1fc1GOvVUWyUT3Xc13fc0B3QrHdXwXI0g1fOw51vUYp6nOb5tGuoELkYVCDim6kRDHtNsU2eYuIaNTZOoBkGWpxF3YqjMMUwGZKmrqRh7ho8JMzWswWmqIsRcz3CpPTE0bFgUW1R1fF8lrq47VPcMG9ua7ruObRrEdyxPnxiGaxIP2VTVDBt5huFoqu3qyLEd3bIJ1ZlLfc8mE1hT1zFNjDTXNJGjU1fTPE11LAPrnuqq2DewrvoTw1ENSzVM3yWY6p6PLaxiZBFfRVizVZUhX8WG504MZrmeZ3i2A1e/gbHLDKY6pk515CMNGZ4Gl5M6MVXfxdT2LU2HwwYhYiDXMTWDedizme06noZsy5mYhBKVYtU3NMv2DJs4rs0QUPqa5aoEWRrTNRNpUNFxdc/0LF+zXGKaruNgnVKNeA4ybc31qOa5iExM4ujM0DDM1sU+jMTzXd1UPdeF3eK6po8c6Nr0NZ9qlkosEyhAX3N9gjDWDd3AuuMQH6m+o7GJaelMRzbDvuEj4lBfMzHChutiFRk+cRzdcH3dciemxSyHYFXVfNWyVdMD1LeQ7jLN8jG1LUKZY/loYlo+Ra5JGWIGZrqDsEMwMlTdMw3Dd1TdUDE0n5g20gklpukwBw5jg3gqswxHpQxhSizdcwl2VTwxmesSRFwdThDXYrYEQhhhrqGZhmMh27BNak9M5ukYAwWnEden2PfhzKY+tVxV81yPqJ5leIY1sZDGMNYwMX2HEMMk1FctaGJqGnINDEevQ4k5sTAhjgFravqMqbDQlNi+7cN30xn1XOa5qo4nFvaxo2oatRBVDR8ZlmFSi5kqtrBHLFP3HE83VXViEYtQBzlwgxiUGLpleY6ObdPCqq/B2jGiu4Y9sYjj+JaqeqZpqL7uM2aqBqLU9F3s+ppjeT6QjNrE0jzD1A3Nxa6jqy7TTVMzdQa7lWKEbawjCxk2ncCQCCW6ZmBk+J5p2jq2PNOz4dYAgt9gJnM8NLEMD1Gm6QRbGvUMwyRIZzajyCG+Bdc/cqhOHH1iWZbjmq5qYcu3bMcxVcOziG5SE7kWcWwH28gyiTuxmG6rpu6bLmGa7yLGTJf51NJN7NuAka5pGwbSJjaiPoLf2FeZSRzDVqnnYsswTZvZ1EcIOY4nK2qWYRm2xxzDcDF1PQdTEzCEuBa8Q4jjW7bJJjYyXBt5rmPphouw4REHY4SRabvI1HyXUcoIJurEptj3EDI8pMPxB88oz1MdYhHN1G3f96HAZXhi60gziGV4cCPqzMeaA4QKxb6p6zZjukYclfnqxDYIQWDkT21meaqneypQUr7v2LZHsGuqNsOaqU5swENLN2zXo4g6BhD6vqOZqmYTy2JI1xEjTCcT27Kpg+EwNg0Hjk9TB8oEOcj3ieVjgijcY+bEtonrUdPDsAGADFF9zSXEsyziu5Zm+bqquq6KJrbrqtSnmmo4lm0aLvVMqhLdoqrhaY7raL7r+yAstT2kE+Zbvq+5hmraru6rzMcU+7ZjM2I5GgzfsSe2jyxdcwyKiWH7PjEJs0yVIU01TINgZgH3QKVo4hDm6/BwsHTPdBFsRN8G8syH0xYbjmNSw7PciaNbiHiWrdkmY5prUFs3TddGtm7Yvo0dZFHqw3OPISASTcsnhmowDwhM7HtUx4ZlWPBRdZdYGNsTRm1TNTXswFtHZx7Cru1opuMYhgOAXUODE4tNmKYRy6ee4zHbsQ0LM0aRiwjcBJTatkQzV7UmzKLIN4lKNNfSVSD9fGpTSizVdV3fI47heCqxyIQxgm3ku6prmg5FhCDTs5njUReYBZrNNOpYxHMmzDVt32LUcF1PJ9jwTdeyiefbpuoTynRHZ1j3HGfCfIp8HZuWpanM8W3iwMaxsOfbhkHhUkK6RxmauIQavmH6BGu64VnIgae/xVTdQXBpU8/GuqVZaOJqBnY8omJs+LqLHHhEW5Qgx8Ou5mPVNVyL+ZY9cQ1GTMOyTdMyddX0iI2Jgzzd8R3dJqaLVB3pmOoTlxHLhAesppqOSbDPHOxil6nMMx1bpaavUUMzvYnLYFmY5QEpTTyCVfnAZMx1XOrolqmquot9OvFUQh1GbeT7OtMs5lEMZI9j6w52dV9zmU5Nj0FFqqnIdSyNAY2BHArHnIocZMOVbxLD0YBDM/FUw3SpT11dVw2GHBPocd1XLezaxFFNijHTkE4nnjxmseNjX3UNZnuebxHPpLbmeZqvu5j5rsMMa+Lpjoph3YCJ4hDV8TACLKAqXD+65dmG5iAfTzyTIMuxsG0z3wNiBrgiVNc9nziuozs6sQ1MTWfimQbRDc0xPUOjvuqovooxZo6FVc9WqUMJ0VTDciee7SLHt6nn6rpr2VhVsU1h79k2Bv8Yvm+ZyGb6xIO9QFXHRYSZngZHIyWqrqkmsG2QjhzfQMQmE8+jjqf5mLjY0yyPGiqwF6iuWVT1fIfaqgmzNCaeB6wwA1GX2kTXGNUp9TyqEwMZOjNV1wda2tYnvmozGxuWT3VEKfKogQzTtVTfIwa8P13XckwDWRMfG4RhwnRV9zWmAankOrYHHCg41ygyLaLbyJ34GqwzcXXMPKxSVbN9zSE+8V3T0LFjUI06pmnQia8jE5tY1bHtUuALaS5wp3zdp7rhQA+mB8s38Q3iW56PgLxCxLIwswkQcYaNmIMxcTXNUB3iT3wLm4ZNiKdqiJg+1izVx8TTPYtRU4fzyMAIu+7EZwy7TNfgRctsbHquYaiWZmGd6YavqxhbmGmMTHxgIjBgCWJdgysacMXAzKYm8y0NucyCU8ic+D5ybeq4pmGbJtUtR0UGoJPlGZZLHeQBmctgwX0TawaDa4k6NryXVeQTTJmlY01m6Zbq+nj6xdHnVwzuV5N46af5Fj73QCR0TINaJHQU6IFI6FjY46NgS7M7cJVBdHCOcecZqRlhWVbrNeZHnxx94W5yyeMo5LUJu1ikpWDpYsGTsADDoqK1OHp19OnO4shbiHwmkmA5MD06O/0Ec9jLdSyJAdjTUygYAh+ITna0q0UnOwqHopM9EMY7IfQNejZHWBv0bBn5wKBne7vxlnYyqBkE/H3GAtUQSmkroPmlk9FHGXsQR4vFuqHXV0fPdcu+rcrtcPrB09Gq5N4XRh9ZzanLvv3E9IN3+tW+OPpobw6DeuNevX5MUkPtx+gxTKVWLPr1k9Epe+nBAxnWyE6CeZrXVqrb7RU3qw4/w0Zx8xk2mw0/w9Z24y3tYFeAzp5CNXAFcucZcFGhUB21OqJ//cnRZ9hrb7wseFHl0i/P3UQ6Wo6SWRdS5s3NybFDzUafa21p7LLMo2lVOyQM4ioUr+bRLEqKQczq/dDqmNX76wxjVh+GNz4Er2+jfnAytY364TkPbNSPgjo+DLV2eGfWxvw1ymp4td8+zB48AAXeME8zP4pL6Xrji5sf9SNbag7k2RultTx7s9FAnr211XizlYzIpw+ChP/ayegj7MEDNyqymC/tJFq02rzfvzn8j22te8/oto6cwHr57SemH7uztaHZOZSpJ7Gt5Xhby/un4DJAk87HVaOZyj+8NXqOPXjgJaC/B2Z4rCrKdPFanmYib8zLlNYgMHyIb2PQId7bAup35hFQ//RA/YFjHbRuLXGweV/rYm/NWutiP7CB1sVBaOP90GrV+jqsaGuJPf/O//YXZdiv3z4ZfdC75mCd3vre2R5Njj148OrLb0biqj5FWZrmYZTwci2a3O5qdTS5PWAG0eT2wxnvgSNDN2pyp7QH+R+/NfoMNLgU+XmcXvlpfl9kMQ9W4eTNk3tkc9qfPdRseErvrdqc0vvBDU/pg/DGB+DVyuq1h0DNUqgJtrhwh2srG4FfvDX6HHvwAFwsldLTYJzmL/FSwLkaXwo7vuJLWB7vINn3faeHAQ3ujYO163vjMNDBvXEU1PFhqPfvnH6g9v4CxMEHamWi1mUYRLGsvbTzAvysRuXyQSaC6DwKirvJy6LkIS/5ziiWh5sOjL4PV6+Nvo8AOzD6Pg7u+Ai4tWdKqXmO1fquBVf3ikb01q3Bb56MPtbEKomjRDwI8igrWSojxn5h9P4WuUCl+nR7xUFA06016oCm2xsPAprubD3e3rrvebNx0tLMlpJ1lIB3GBN5yaPkjTxmMYTLPW9c5xT7UWJv002U2Fu9hxL7wW6ixEG44yPg9v0A6FghKxdBuF2y/+Nk9GztOLbWuJSuUsBrSv1uONtcqDu7GwxsMHZVqm0wdoIY2GDsgzHeCUO+taUdliWtUp8B4ymFYtzsg7d/95f/yu88/a0TuJSe6wPp+1N4mdePvu22U3tbDQmRfTUbQmQvsCEhcgjaeD80aZPRLI2mUO3OM6ZUC0dac1G//Qs//ws//v5vnYALlE/0QYGJSxyL+NUMNhDrKEFVvx0+++t//i+dQKwcCMdTSDeuD9OsOL2l6qCwuQPMkOuwvU7DddgBYMh12A1hvAuCxJTa948uXZ80a4MbAv/tn/9z/+1fe7Km0T7ah/EAgucm0g/3D2wiyMe3Vx64gNxWoXYBubXpwAXkrrbjrW2lJUDt5Emv47jLF7dUaZeT/M0/9e/++2CG8zeeGt1mHEjYl6PgtVxcRgL0/6PRM3Xuq+Vc5LfD6dub1UZfeUFI9zrSebcL6ty5CF0BjuseiLKMklnxQFqmgCXXJ14T+SKSxiEFBKvOygdVfimW99QuHi0s0LCL209MT+9sdHwPd+8/uTCbbcabbfpOAm4w7tpJwA0aDJ0E3LCn8Y166u+lHatb76VdSz/YS3sgjHdBuD8+/Z7GG7BFiaJSuG5aF84tl/O/OQHcSS55ISk+1kRt+trow80eqhE4eizMXjDgszqAmDk93Wy9hjPDwhZn1pqs4cxmm/FGm/oqbULh1sy3JvKr1XIOf0reJnWzRZaLongxmoocXkSh9Mix5zbZ02rtNtlTs71N9gFbu00OQBvvh1YvClwgmg6UGSxKbfrYuSD8g7dGd2oYwL16WSzSXJrzvxiFoUh2P3V3Nhk+dXdWa566u8EMn7p74Yz3wKmXoH7Q6c0S1G5n8SZevJpmjSeyF7Lq9SgWdVj6A3ixo9UWvNhRs48Xu4BtwYs90Mb7oQ3c0A1Cu7W+3E9/52T0vQ2Mqnz1vPGLVDePHrdctK9sLsuzo493fQ+q3/uh7nXRW49BldtPTJ+9s6v57+tO0P4KbLQf72hfW4lZtR9O+UrRdWtFZH+SQfwt4AZJ6hyMzJpgUjtdKe5sMfAdt7NW7TtuN5CB77i9UMa7ofRlElY9+caZFTXXJu/zOIaIxOz+S0dOfmuLjclvrbWa/HYgG5PfCWW8G0rf/U3D3a5jbGCz9bZYjj5aTw6ASIMseZubJ/e+r+9e8ePbq0GlVXiKj59urQTMNl0feNb4uSdHBCrV5WlVitfy9DIKBbjWeSFOp03TBja4I4Pq5sm910YfDxZ59pAn4cPZIjgPHopukBR23jagozu7Ya7tym3N2125rWx9V+5qP97Vvn9b7B5lfVvsmcXgttgPZ7wHjmSMgkX6U1ZLfP/S7/7G3z85/YWnRt8nZxDluZSMvRbzZVqV0pDeTkInKoGxbJ7c+8Mnow9ldeHq2+Avjr4XAvw9XLTtH7Z1QoDwcFGcPokRmt6rd9bWbk7PoOhBmQsO4kbvOksT8DjMY+n/qRmCHc/gOTlfbGzErUBXG3Fr8eZG3AllvAfKRRdHsx7K0ZO4/cT0q3duOO+486DWDPlGvY1v1lstOlPrw6W+UbWWsPg2CEf7wCTKSTBeEqR1jC59S5iZE8lW39sS2m1EmzmZfvb0ULueDGd6T12X4RxuP2Dn763asPP3gxuy8w/CGx+Ad/+TYNMKTAIDa6ZCtVqS84v/+6+97/SX5dum1/rNzJKM1Y377XSz4tozZljYPmPWmqw9YzbbjDfa1G7Va3RqpkHaAFC/+WE4HEqesHQxhVCyL6ZxCJceJi9i8+TeH3pyhUo97xu/e2v0WXuaXopXIDpM3DrxApcgtReft6JkNJb71I/yonxN+kWLgtfs16p8Jl7N74sg5tFi9NFVnToI0Fv8Qow+411ncRREJfCypHRACpghMJOE/Mm7yTyaRq2vQxnUpw4HMXpu5Sm1N6rOzeSXv1FFwQW4wkqK6FJAb29kr8/ztCzBLa4NQbYbqKPb90VYBYLJIHbgMnx0+41CrByW3Uuno+feKIQXpN9IH/hpvnJI1ZC2p5+5L4I0D1uuTBNDh+W8mH9dLIvTz7cjlZsuBodRJTywIeB23gkP+l4mD6167WXyUK2hl8ljYI4Pw+zzyQ9/+ppPfrjekE9+HNzxMXD7jLltWFgz5raVDBlzu9qOt7ftn3X70bw+6/bXGZ51h+GND8HrX/A7d1l9we8sHl7we6GM90Dpv3X37ur6rbu3yvCtexDa+AC0fkyv40+UOqbX8fWHMb1u1s/4Jv30cXL/iVXj5P46Q5w8DG98CF7/mlw/k+trcj13eE1uazPebNNXMj3mXK6VTI86wQdKpsfCHh8Hu7866/dTvTrrucPV2dZmvNmmvxv3Xnr1btxbZbgbD0Ib74dWR0Ox+uSy3tI3/8PJ6LMsXDTSEYiKMeWF6Fwwvp5HPN6tKrnRcKgquVHcqEpuNhuqSm5tN97Srg7hWTMbdFUhVs/ZbjPDt//k3/rZX3gKfKqcArOm3jughfLG3TfxTmFuw1PYaDAQ5u6qVAtzd4IYCHP3wRjvhFEzWepwcbo50HptNUF+8anWrdhm87fySD6b//Sm6yX8A6PP7WrmhZFkeBWda/v9PTTRIJ0jIB6ANFDPOQisVs85WG2onnMU1PERUDcdsu2YVN8h2655b3HItgfaeD+0vlt9s8Eao3ad2rLmfgt0N0Veviny6DwSuVNFcRnt0d2UoaXup00U7DcKEQ51NzfLG93NLQ2HupvbW463teyH9LZq1nqj5WM1DNe3/6uf/u//Aohzf2Ntgm3o6Cd2TXCz7nCCm+XNBLc0HE5we8vxtpa9aCRSnvKMCdOjhLTM9P/k1uhLPV60jCefcfAetpS3FHBTaqox3+m87sj2A+d1R7apndcd28HAed0Nehgf2wMYOlhUGjpIJRhdb6Oo/d1bo08zGY39BZE0aqtpKFYCWGDNj9oHv4pujyDExr4WUL/lGdX1T/fXHwQo/uo6r+hQ634ciH0V6zgQe0EN4kAcgjXeC6sXuRgbaqvq+xt/5Nfed/qdJ0fjumnrefCN+y+9UUgK/DyKo0497dVNpP3B0XPDpr0md5PzdPSpNwrx6vmLvJj3Snwh/ScPRYX7wDSiwn1V1kSFh6CND0Dre87cM4Xac+a+OQ48Zx6ANN4HqWcb1JimNN4e3/7Lf+1v/q33n/4/t0bPs7mQ7sHfSMR1Jv2tdjZfd8O7CewCkZS18uLdLCjOBoZEx9hvfcV72X9495U37Zfuug+9t1/3Xnlw99VXHt51H/qv3n/IXn3lde+V1x8+YPfvvvb6QKHlBu1qhZabdDRQaLlhT+Ob9HT/k6fvB9s4BSnqnQ/U/r1RE7vo37o1+v76arSzrHBFlotaIPdKWu+Xt6LkZR68FCXV9Q5lz9Px14XI5FTuJkUJ+mDha7mI2t8AeMDEOly9ZmIdAXbAxDoO7vgIuLVVB6qtOkjrY//J0We3LtVwif6JTeJYGY04GC/WHs4/y+N5MgsvHs0SEfKLME74IlrMzqP57GIhkul8+vzoY1s7Gh2zJAOt4m1QGq3ibUVrWsW7Wo93tP49+JFRbbqDWtOdH2qJUzhpExE/KOFTmif3bvdkzM/+mT/xl05A/tieZvPv/MQf+zGgE3/lydEnm/ax4EmVuVHRGXKZJ/f+8dGHGE94vgRBEFZVgm4/jb8w+ngAtUX+MEyvEvD3+RBe4tnpM4Gs/FVRiBJ/3+g0F1malyJ/mC74nD8s+ez0u+sqD6HKw6kU9+/qH6KmfKKeUD/UyhHDKGSjehif3TqMkexfXGeHxrAcfagZQtf1D/+j6fr+954+o0sFVWKYfQ/ErfHI2//TH/zTP3ly+r+AGncfyGt5WqbT6vzua2y3GveOBkM17h2VGjXuXSCGatx7YIx3woB72JDu21faq/Wc/+w//TN/43tOf/dk9IlB229UPOdJGSVip+/YHfWHqsjb6zSqyDsADFWRd0MY74IgZ4uls3oqY1K2qlJv/xs/+/d++dbp3wOFqX7T+2KapmWtgelGPE4hZt9Lo4/2819Jk5fTkEP0vk+PPrKAnw9zWf4wkxWaeHrA4dqEN+BwbRbXHK4tzQYcru3txlvaSftKGSLGaBVZ/teT0afWJl2I8sE8zcugkiZj2wMl7GkzIDj31KsJzn2ABgTnAUjjfZCkVogltULae/tvPjX6Ut3ibihqix+pZGuHIehhOtV0GosH0SyRDIs/fDL6RPtQe7kOsQkyexUh9fYJ/vJoJJKHRVSKh1F4+ulvvOSVby6WVyaqZsk98o4afB298UL4Cl+mEObgmSxPp22Ug6eQgkz5YD5uLPd+9KTVZAofvpRevdthfHQ4jKeRgm42jo8OtRfqIfTfm3z4tj8ObPO2P3IMw7f98T2Mj+3hvt09NTv2CL4lEvy0SM7eeCD/e8HBt85z/PR5fsZsfCsU+FZW4luP+Pw7f/vv/Nr7Tv/+U6Mf2NadGwXiLTGtO7KDQGSlCM2Te//sTVDt2isNoc2M+71v/OYPG6/Ql994YzuqfeUGowG0PxLZ9g5kO7LdaCRHoFswwnvRbSvg209Mv3LnBuMIO1nkDpTb2cv4+F7+kaGdKwIwAbwx2uGXnKx46fot2vva37z/6JsvZIs33z3ataO5AdrtHci7R7tuJO812rWAj0S7bhw3Q7t+L0egXVv9vUC7//gp4MBtdigVM+5XSXej/qGb4Nv9JXe+SV65KHuf+ZXim+GLr7yib8e3zx8zjHt/8GhE2zuC7Yh23BCOwLCBSexBiI1J7OGehyaxR8EdHwH3vcCiv/1Uy38a9tSyONeosx+7CS59U/8mPX/8gOe9L/nWK18Pffett7bj0pePH8y9f+ZojNo7ju0YdZOBHIFXvCOftuPVNri3n5h++c7xo5h2h+MOHNvVx/joPv5/xbda/eNlkVRuVIBNnrwrb4Rv9osPyDx7Gc973/n1198ocrp4+Wb4tmUwN8C3veO4Gb5tG8h7gW9b4O7Bt22jOIxvO/rYgW9bar8X+PZXnxp94UB/3eH2ozdBNs15U13O/BfD/qHy5jvi68tqx9PzS0eO5N4/dTSm7R3Edkw7ehRHoNmPdOpfB9GsO2++dOfI/v9AF975MIKtoI+Pg/5eoNafe3qk7unstSi4ELkdhk26Q7OfvAmahd+YX765LO3X+194EbDih6+T7WhG3sWo7v0LR6Pc3gFtR7l3NaIj0K8Yfe0Y9Nvewe0npuTOuxhXOXr+KLTc3ev45r2+F+j6K0+P6LaO6x5kFG14E7f91oHXaqnJT98EY73sYjp7ME/7zLC3zy8vv5lWi+0Yq7+7gd37qaORdu+YtiPtux3UEXi7HP3QXrw91MftJ6b6nXc3usej37cfe4/pe/yu+n4vcPhnnh4pu/sGBnxaE6x9GvLHb4K9Lz24SrI3X5npfVbuI+3eC1Fxbzv2opsOCUJnHom3e0ezHW9vPpwjMHbRmQnuw9ht0G8/MUV3bjqiZGQeg6W7+hvfsL/3AjP/1NMjtLVXqQXTbAY/l44UW1LEPLn3EzfBzWn12oOLB+VbL/ew4f4i+sbs8dfV7biJbz4o2C5HYufe8WzHznczoCPw852RtR8/98C//cQU37n5qPKO+NiFowf6HN+4z/cCT3/x6RG+Sb81PWKewGV7A0zVHqdvvHCfn/cxg19/3Vzm2XZMpe9mWLB9jsXVfSPajqvvbkhHYGvVkZA3w9a6h9tPTOmddzOyy9EPvjuMXfU7fhf9vhdY+5O3Wr2S++I8F8X89fRCJE6UNK796Kbk/HNtLOqd7bbEjN5Ztx8zejfALTGj90IcH4JYmzjrUlmmcbnbOYj8B0+1ugFv8TjOeAZq5xC4uV4C8+Ter56M7jRY92rSWjkDVlKkYXz7h6f/5snoM7V73+ix2AprdLa//G5SZFFN3zGeh6Nn18pfyHlYgV7x6bOvlNlaIUQeTpPTz61ltw7B3oyKqN6R9/6Dk9FpN5PVDJ7+vTODf+1k9EHAit7Yv31yemDspzcb++nusR8e4f3Pg02WdPlCVKxQXe7Teo/ev1UV8x/7K3/5V8HF8z9/6xDe/YWT0ft7RvW/V77Rffz+78Ac19ZhZZuGaa3R0luVb52c/sOTQ+vxJ08GfisoRMO7HU6//Z6vy+E57kaR+8rp94BzDqToENJc1dGdwTpomzP/o51CLgg4warnFX4ZzWoLxNoit/YT11leaOg2no4Pt4I27S1atzk93GbgcZ2sW2AcA6HvkOBQ5dohwUGQA4cEx8AcH4S55VJoIxC8/S//+k//7PtOfwYc1dVQolB4wTxlPAlEHNcMnSfuvdDfoPhTo49mtckpONs5j87Th9JJ4ZOqiqQDu52ghg7sdlZrHNjtBjN0YLcXzngPnPvPrQzKZMz6D9RuDhvjytot6Ei2TyKRgJOof/vWYCmc0QdFEmZplJRnVR6f4nlZZsXXvvrVWCTFeS5NIcKzjCuzNJ3FgmdRoQTp4quX6leDvMpAcRZ/avQRgD9Nr89EUubLMwmuMb78ymi8pfCMSx/hZ5ewUWPRaDJiNPqBQnrFPCvFdXmWXor8LBezKE3OyjyazUR+Vs6BZkjj8PRJpKj4zuijsuolWFQFPD5b8HwWJae3VIw/M/p4VYizlFfl/Ow8BVDvVKJo7UKnZ6NnXhJJAQ7gwYPSZ6Thx3UpmebyrPDTXPqP8XO+GDp82F+1cW6zH9zQuc1BeOND8L7UBaJJHvamdfuJ6Xff6c/z3vePPrbqeK3muF/z/hhuhtr3HyGKZsAB2RnzqlqDYn/3ydGHVihmR4uXiXly73tGo9dykUmnHert0b3JmksiE9Pbo9//x378vziZfvj0Q9BrPSnZftCa3B4N0vT26F665hhJQoMPHtSLVPE4eizO0uTsPA2q1hD4c6NP1l63zoIYxnpWRKE4mwseiryu8vv/uBzQaO+A9LUBGWtpcy1t3R7d+9DomdVyoNuj4eE9Xj+8tyzJV0fPDr5vrwwa3NlogDp/As13XmsxXm8hKaLVw4Wq6xTRd37+R2XQi99plbflF/fCypZ+bNk8yqQFbt8Z/Z3TZ3soNag7UFXfValWVd8JYqCqvg/GeCeM+3cGbqE0487TsApfbtD7//2pk9Fnd7V9XfDFeZoC1/Xnf6pH9bT5t0P8ydHtsFmOs+nybBZHQXvc/YNvn4w+XOXxmTTWOTuXkWqK09/69smPjAGtlRmPeVBGQVY9fhyLeZWUcPiOJ2OchEWZC1Eqj7LxZKwpEVj0NIWWFYoimiVFl17wct4kOFIWVSE987Y502DJ298BD8ViqaQ5D+KuQgBXZRgrBTh1UXisVEWXfRkrRTBP0zidLbv6Ir9Mg0WRKeCagwc8VYpMmaWXyjSX5fNIXAplen6VVdOuUZ254MEiimOexILnSZTMunIINRGAj/w8TRf93KhcBrXNXhHkQiQKTDcWZRS0K8DDkGNqdKlFlChBmmcKGGtkYpBfzNMsOl8O8h6LrEyVIFWiBPIEUhUeR0GYtLWifJp0M3kUpe3PmC86SPFU5KUSpfL3OQ9EUvIqjxSwEcqroKzybiBxNOVTvkqJ881FjiNxLf36bmbk1TBdf644FrM8VbK4SSTNbOIqSZVYlHklQTXfSOamcVSK817ugj9OE4WL1e+A936nNTquktVFP7kYJBReDdNTsZbOh+nF9TBd9srDXlsx6/0uVr/Pe/XrqTe/y9XvJF79znq/CznNJOFREW5+i6yPl7DOYpryPFTSXA4lyxTYLLG0OF7Hayi8KKPLXno658njqp8RJTMRK4UIAEkWPF5MRT5rBptlCuBSlS+VUgTzNmvO44uSxxc9MHKUF1GwkXeV5oO8UihFWV2I7ryBXHBSw5MkLeUl1e8LwmxFSpA2SbnpoU4m8nMR9GGIsIp5UUZBP68oah+KvChEUSyaXhv0yDLlHHyVwe7vNZrxfMqTx2mznyBHJBGYpfQnMsv5YsHz8zjK+rnL5PGyl55X0yJL+8N8JGL+iM+jeNH/Co/SKCmqTOSF6Fe+iMJQfvg2nc6SqOx3sJC+hpdZLvrDWCwhpNe8GGS1Oc3OyTIlETzP0rBXKxFXhYh5LycTeVXwOO5npXnJ44IX3RG82lJQnM0uwOsG7+NhlqczODC2oOgmykNeGvNpP0OIgl8pC9Gm02we8d4uKMoqFKlyHvXSy6iIedKfX1FNHw3RpkyzOe9nXEaLxbaddBU9FvlqAFd5JK35VjX6v9NtsyqUcMGDs9afWqEkomxLxHUWp/mWfot6JsUyCfqZ1VWax6uZFUoWVxC+ooNZKKUoyoR3mz8Hl16lPJ82LgV44FRxqixEHvAwjaNpXdZg3s7iS1EXp+fA84/FAoI09YZfhBhtrkSRdoi52paZ4HkoVudHVwKHQTJoX0kaKYmSR3yYVShLPk/T1WVRxfwyyoFkV6osgCNCyeSQIcIoYK5YpAl4r+LQUzpLI14oARdhGM2iknerWVdPxcYyy4JchKK3BS55yfNCOedFGS+VohR8AdTN6mC6jMq0uUiv+IWQ3xsO9iQs0vN1DLgqWsJp87tNlSIN8v75NeVFUc5XR/OUA5cjT5c8Fso0TtOL7nyZTpUgrx6LKE/DtKjidqG6iUwDQL61jzcV4ZSXc56EU7FMu701PY+DNCkgzp2yWCogKmpLomkcpaWASUhSqgrT9WWcRqEyzXkSnvEq6NrNolDw1YGx4LMoKK7WinvkJ3Dl4N96sryUqTy8jOJYbFvGR5UkZso8XS1knAYXIlRm6azieRjxZK2gWIZnqgIWCHAwtBNJ23MSbovVV4TUVRTORNli/jRXirmIOqg5j5IszbrkkifbyIFptRSiRu5ptVy0+D+9qisW22a3fFS1vQb8fB0NAh6LJOR5w4XpchfTHAacJqI5ZAO+yKYijoO0SsrlxsigGPrhuRB5wc9FKuN0daXJJe//LhReVLAhVxkBBNXgYTrMvVirlV7UT4VBZsbznCsBL+cQAlXuc94rrhbJsP5l2aXztCpEfb0p8wuZVfKr6KJFo2BaiHoHRoGSREFN3gXTap1QD4Kigb75DYIwKuS6delEqU84uBhDZTHvCJtejeFzIQgTVVWm0SxIF3AQd7A7hz/1CZhWRRCBj8S2fC5ms9XvjqgPLlTcftpYAS/h8fr1Iwk4pSqz1ckps4J0kfGkN9iavlKmeRTHEU/KjMe8gw2hx4J1rOuI2iHaddlZXF71IZTRoldpuf0eC+QNtKPkUuRdIgouqkypgHZWHnOZlRYir8OEKRBys7fucVqFxQXE2Z6maVE2I4aSxdZvnZzz7n0MzqGDef3OaZLzVdliAZukbOeZJqDsdZbONzdXmjyqZpVYpKvGSSL9yyiLYJbzq7N51NFosux6tenTNIZjcsYXYpWXi5Jfh48yJeOP+IUyS5UolAVVxlcIkFZ5IRSeiet11JAlefeVZRK2f8njZVECvjQVswIIkbZezh8vBwPJgV+8BN+VXc4yK9M5Dy7WcaaKy4KEXSqP0qp4lK/SQFdwuFhWd1hQFSGmG+sZamp7YG4U8VDsLsv54xo4rFU4fWdwyIXiXCSFwGvjDkVc8t49BfL+VJ7um8gTitUlAL+LqMzmadl99lAUF8pllMCHr6+cUBSLXnHJo1hRddMc5pSLFQkfzme8u57DCPwVFenW26algma54OUiSsKi+YxNQRbzEl5QSsGnUUvOhlGSFutLAMffrBtldNneDvLYAwWYtijukQ5hGsw7Jk6YBsXwsAizIgm2rWEuKa7VgRHm53naoWCYR9PpinAPc6CehoDzNJum122qCi7gjdWNvkqBkm75MOGy4Hm5sXSCv5PMFBEXEFks7zLzOFpA9SheZzeJKV/WHI/6V/NMaxKL3s/6apMpyZqQvyQDQv6SLAf5Wi2UIFs0sxbBtg8sgnRFcYlQiYLoElhbTZswiLv1F+F51Ht7ggN8hWeZ/FnzELuSShHXgRBhvChWec3tdhXFYgWjuuBKLMcb89XbXkkaEqe+j5QAYh9c8CKSB2N9TgkAXpRRc4+LODVWu14s+EzJ4ekiEnmciPCsnCZYmQ0IcZHM4qiYyys4msljaVjSHLLybVJ0t51IHqUi6vWWQGTfQt6RxaXMSKJtN4OoH87KY+DdCa5kwOQQeSS2fZlCKeIoFDJqYrOzRKFc8TjmqyNCFLVzxEIpE8kElcSJKEoeRKlS8JiHovd4WQ1Z0lAFj0W7a0VZdF1XEJ9aaa+8dh/AN40VeRsqiyjI0yI9ly2vQfQgkiDiWAXWTc1ME9clhASLFYgUUKb1/Gpel3hcpltInnNVqfk48BbrToQ6t1BEfL5RhLc2wLsb8KBM8+Xa4XTO83Pw8NYmo1lHV4PmVXHGs4gqj6rHFJ3BN754LEuSRElS+auYR8nsPBfA+lkDHZWzKI/PcpHx4KKQ7yKZfSlykBkW9XpG4PWoawJlbSLms1jku+6jcyBkVnWj7ALOljaZBnxaxT3IaV5ORRJuOwjO0+vzNA/rV+l5LkQMouAOdC5EFl2spST/cwbyhYzLB7Dkk+P1g01uq1nOk2lciXOelLxY1tsbSsJYKFfluUzlwKzZfi/OgmLbjpoJcVGcp7n8vzm1ZiJ5zMUlj6s+cs1ECoKJFQEzE+msEkWRd+lSZN3RMBMliPHLNO8Y7jNRlsBWm3W0yyxaXERllyhXl9VssVBmolykuehd/LNU6S9XnQUch058MUvjUMqR1/BolkuqJO3kCZABArKOKtuyNlIbJeDp4JWvzPIqS3uHbV0zikVLWAHlJfKcJxddRpYtkwr4wu3M59tYA/Pt32genkfxIhBzkSRiEdWc5bnIimha5Txst9o84glc2WUqE8ASaAoW86D9BvOEK/N0CcL7ooOfLoQ8B/giyvkamQqFQEeVXToN+7TYPC2Lkrc4MM+2bI35O0oo4jlsyy6rCIKtaAoaDlhZxINrBtAii5KLWnJWc5UjBe7qlVwtUuAAHjSLlGgxq/IulUVJv/6y7KU0uBiAsBjgZzTlClBqUfs2b3KnNVsvqs/yJvdCtF8iitNLkYXtm6UGeXYVXS+yMxESxE19agY04KZpGAbRLBpgRRYPWtScr6oQeXMyDErVIRUbLcJpVz4bPqzlPMOEhmEv4/xiWOHiapBGqvK45GULPQkjvlidjVEuJF+YZ2kcgyjqute44DxoX9D1iRJtxeuoFAsl5xdVKVbiKZlZ8nTKW5SNqm1tr1ti/JEos6rbao+itGwZto8u4oVyXsEGfbQomyPzUVWUZ8UiikUirhsJ7QXwPMua7rvgF+m0avH0gseXDW29qIB4gzwhsiG5eyHfIxt4fyHyaZqGq2pznqxkuLAuPXHPRRQW/Oxxm4pFlPDksYhmopGfXSTiquxO44skvd7NsboAptMMeog5sDqKXYy7mJd8ccavKoXHcAEMc5drufDQVFZ85bMsB1p8qeQCRG8FX7FM6roZD2sRVhx1Hyjmj+sjSymBeIun7zRrAfpLSlD1j/uGeE3zLM0lx+p8UCDiMpnFg6zz+LKlx+qMKAduOlyJ/dx3qijLusu5zit4EShJcbXifTX5UnjAM5AGS2yps8t5lFwEwM4fQLkMgqKhqOuMpSjS8/MoWOvt8bJ5X6zysAL8pF4GWc8ATlpNoHaH9JDTsjq7g2m2VnOTXFwVpQkv5v1hQ24GDgOBGg1SJXncK5HEYfvRpmkSpFEjv42FkEISpUh4lomyqyVKYNx0/ebpciFr1iRQLK6Bvs6F1tW4jniWXom8ao/EOKr58MHFlhsmji4FiEmLuVgxiOF9MxXlXK5ixmtBO2SGQRG2e6cZIORGs6im2JXsoskThZCkhZSpQ8a8WvBEqXiTjHm1wqwFcFGvs04UJt9XAHCW86zlSDX5l1HS0POAbZdJnU2QUoDPQtCw4JJFmJdRhzfpDJjd69tJ5kZnwHlbDvIeVVlUiryjSuI0itMkLUXz6IzTq+6ei6vpNA0u2nWVEr6u7PJRtWXJF8q0Sh7VjC7lIpc5C3jTng00ChaEK5fzGGTgOW/36oIHy6L7LUBsoETJeQoc3jLqxHRt0VDUseA5kHVLnoTiuia3IavMYs7LoiaOFrxIk3QepbvofnnIwD0aJessW1m0+aCFbIj8WAdF6WUWKw5WfWzIzIzLs3CVDDuGBIjLzzsiZREUBSDoxghF/a1Fzrsc4BhIZvTqsq0zo6JMgwvJ5+rye9o66xt/IUBLs0sspoKvihKuZDwJecavakpLCvbybHVVDYWY8UYOLES+LVc+a7vcy7wn85O5eSsyWETtPVkvYftY7opzHhVChGc1f30qEtBT6LgeiyjvZgfEyRI898uPVKMLBBFdSg0keUcu0ulywa+7FqBFJc67jbNIFykIEuRgyyvIKcKsAL7CI95eVQvYLyKZ8VmHMMv6iXAhVl9+qczzq1X55pEMlEHZEZqLJR/Q2oulPHrXz4DFspYZgZy3yxJh9+5aLBtuDCgENJyYsygpu+24jLY8RhbLXvV+Xq0wIW+IzcLmVNsokZfeQHFtsVykYQX8k7CMC6VKooJ30oTFEs6LrmYPUpqJpLckCa/yWqukPpUTfhkteI0GCb+8XK6vMDzKy5UYCpguytVVkuYr2ioRPG7oxkTAM7bTDEnEIlU6NkF9HiWiBGTtapTncXTdpa7L2XC84rosUyA+ilVOVSzSsEvP1hApiS5WP4uwvN5CxyXpavRpDuqHHfcgSXMRrpAlhYfnJoB0Fopk64ssvRA72flpzOF2zXg5B43xmhcj80F/LpltCjnTeFFfd2l8XetwwQ/JaYQf8hsCRQHCkJZ52LQEklaIi6h3aqSJSBNg1ZXLnUNMgH98pRRX0ayXCafBmRSryN2UxVUhlat6xZLyi0RxJcRFPGypVOElL+ZnVbIQxXxQ1MpxkqwUMeCzRLM0yfli+8JnIlFAw2lFxUFWUfLrhjhJ82lUKqA4PRdxN8Ri26sorcoirfJA6kOkswiUuYpFMlt0iwg3szwEMx7GHXck26o5l/G8iqsE3u1FzbcGWUhvY2awcik8CnmeiFm0fpVmvCildQ9QRhnvROJd8TbCIuPLjC9Bi6O76fsaKdm0gKdSszZZME3FJohatS2SUrxujrPtmJ/N5TIMJOfZPBVJdH0WBAulnAtpFaHEV1ddEylQOo/OhdJ/ng8ehU2ltbxlEQXFOoMvi8K8CubJsneQZbDrt4w2uhB5ysNd6J5FC7Hs6LosuuYr8QeQL2tssTorDcMNybUskYKgFQNb5g0nBDlF2TvgZE5ZSkJdavG02VWxditki0BJginwsxZKEs2B0obsZAbK022ltNMjaHj/jS4yQQj1C/JyXuVbCdgsLeaASqtkBPo+zbdoESktanZQAIlcKnYD7T8T5RWf9SbcFkWN9gEPAj6TYcm7GtLJSAEqiZciKUD1sRMvwotZQPz0dJUBPkoue80TkIXJX+k0EcrjJAX+V3261NnhGc8yRM+AHyy1pJpt1UwF9CFyaTdxVkW71COkOiFfRI9X6RbzqvnWiyCrsihee9llFXACwuhCuRCLMLqYVmEnCM+uFHiXjSfjd5SqTMFcKa3lczWDNkhAM0Jc8/bBLLOlCoa45gsw/2h6fqeK+OpnHDedv1NFjxV+CS+mcwFK+kWvYHW4Qar7bJIwH0/GOYKXdS6UhpUQzkRSrYRzOSL7i7X9xfr+YmN/sbm/2NpbrKL9xer+4v3Lou5fFpXuL96/aur+VVP3r5q6f9XU/auG968a3r9qeP+q4f2rxuV9PTyYc74oxHL9ogCZgsiLWZVXF1XcZS4zsU2MX58BSm2cES9bblqvSMRSniTAwABuv46w2q7IA7nlXIAAsN5oq4wV8QR5khfT1QmnVU9dIBcLUBKc1mWXIj6LCrF2NeRRMF+kSdiRO/kiKrfNMJ2K7epKoEzb3Vt5+mjG86sItC5reVNNlcmVhbNmlnbnr3zew+21jSLK08eivOCrWzqvLhYgVcDwKrgOVgzyvAId6JxHIVA/W8bXML2bF2UxZNgXRKm5KPyqvTkKDiKdJBRdHR7FUmFsNi9bdkOdH/MwPa/KKk+3m/gUvCyqBd+YXcEvxWIpz95VziUvciEN9tq8+oUHKjOSkOxBAQJLctl6eZdDTtuqRLK4V4zZttqChzkveBnVku4CtHODuQLfv74lChn6a0iGFOGiUKowKoSkMzo5fiHExRolLrPqyzQUMyA0oqhc9Io3TaYKEZ/XXOAuI4EBTmMeXNS2JG1+XosjG1Kl4KCxL7/Bams1CgfNQ2IazaXmaDfgAiKdKSIBzpR8ZZ/tem+3dRtCZne9Oecx56vUOa+Rt5hfwAMuStpkmkm6q76+V0nJqO+S7TRW6cVylc56dWvMrn/LNx/8nqVpeLXStCvmVQmqQcC/arOiRRXX7JimqyiPJE9ku3i8iIpSLHg6BYF0zs+j/mJHpVijwSVLFlbpiofzK55E57Dja9KowdGLuFN4KC7ytCofKzMJDSRgZ5KgKnN+fdYX/8kyUNQsynUOgSwqwHxeSfhKibJI0i0HQ5HG6dpdUKTJMu5skIo056vTtoDBD8aRxbyY9896eKKt2C4FUJyzIth2KBWZZEVuKYADmUclT5Z8IcoOl4pUGh4B/RdGFyEHYrCj//qlm9QhPA7OVyp1jeAW2IBh0q5bnXcVXQ/EuvC1d5tsFWUEkqRufSRi8TCdilVOLqRmcAathq/LphDkrlL80DEBZUGaFcosT89FXnNGpcVMUp4tsh285KbC2jXa5e4QuXflEpuA/dqdnm1JCXhdbjaReyNMH6Vr+es0R5d/vaeILyD26cVa/nkugosVOjW5jRXVkGXTFjbUhDyb10qqooh4KwsZlpWCB/MyiNaya9rioio5cISveL42FLhCZos+27QpuBJT5TIKl3wBSjJ5r1BySkPRW+Q0qHrl/aVZPoLrEQY8sJUrqinYpEgRclFN60tKvmXWp1GBeqY8EoBfW58mbVEGTFyp+yZNLooqg5sEhCciX7NzLaq8mvGzZcPrLNGaal7JL0R1nuZXK1NH0KEt5hzWoa2Tz7pTRo5zmVb5Ik3gW7aNxCITlVRV3zwXStHZ4JUiSfPud1H2jP1aVI3iuFpECS9Ft62gqNOB5aCiOO+u4vJ8CwVWznkp333N6ObdlkvDZVGIVT1QRo/hX5cTwamdzJJZtRTJSvBWzqMiSuYg0S3TMpnNI5GV86zs18hF2EkiQS92oPhURkCVr3jAZXqRZrWwpk0v00XaKGaWMiePakO7MgvOq2L1LC9zJZ/2lCmALl+JgGD8RdVqpJY5vHq7IpGEy24TlTlM8p13Oi3bEp7gEY8LLJXRL9udWEp3aTW+lXAa1JLEpgeoV85FTWVKkXd3fZXLbHX+NIlaQMYXNU5WKtgoXzbmtvKbVtveKVUwF9MkuljRlFWQPNr49FVwuUlyVY2UqP6dBi07ozqPt/UUhTxaUVvVRZZlM2WagWHoxg1VLUCfSCy2wUlW8qk6DednmKZ5yDt+MHBOwSJMyjbkDm7z6zt6lSw7ddQqS7bMMcvybNoMOSuC3gSynnFwVSjTKVg4d2afVTEwkaqKkJpo23RKvjVX8ma2FWyhfauy2AbkErfSjCRt2aeX5ExefZKB1pOuXAabpqyXYKIhHq+SIU/K9sypGWnl1UqP6zIS5SPekZKS8w8MOB53HPxGa4qX0qyGt1uyyW5FdW3y8eq31MJpfku1hea31Hxofs+r1e+oB1ZKpZvfWf93XSddnTWXkZRXRkGblDaFPL/gSbH1a1zGDWOh1uQQtRb1rCmR0ucrXivnNlNbqbUO6ZLLdX3Xy3zaHh+XhUiLy6hsng6XVTRPmyFekfbtVldtFOm61PKcd7ft1ZoA/Wo7T/5qm3bkVbCL6X0VbLkprgTPxRVfJtvY6HAHgnopB1qi1oeArFJywlfUoyQZRBJKCrFOA9LMWqS5EtMkvRTxqrrcj1WZzy6b5+qVKEoRt8+JqygOpyIHjfX6pLuSyhVbBgi6xJuq4pB9tdKFgN4uxLIIqjwHA+MKCOOWNyvtmwcX1dX/x97bxkiWXYdh081dSn67JEdNLnfZyyWHxQ9J5OvhPefcT1I2cz/Jae1yBz27JL2CNKrurpkpb3dVq7p7d2cRBApiQEZgWEoUJU6ExM4P2Y6AxIDlWFKc2LEtwNYPQ0BoJYAQO4iSADYkOY4D2FFiCMF5r6q6qruquka0DAnmj8VO1zvn3Pfux/m+5wzfvr0/HB5f0jfffvvtne7b3blkr/a38wW/Pb78G95+NPm8d85GreLVvsHj7pv94+7yNLXHR/2jLjOfMfhw6rF5PBy9CYswhudn5/u9nbNH58f7g27/aOfhqLu/P+Wt4+fzfzXxorlfpuiT/rJTAnyzYHA4LsTxbncqed8dB7D5Pd/ts7N58mA46Am1c6HqTlPhdqayl2EUXAfzbmOD/CA+35adaa7JTsvObP7AD+Jnqo/yA4627hzzkb5Umua9P9D5/O3OD+J/8dQVwHlif+KpH+h8z5f638sZXr1T3kj811F35o/uQXPbcvzXcXfQfcgVCaawnGI0AT05OXo8/vfEhT/o9ke98W9cEWH8z7mblhN0vht/NAGeLMj4z5O2duvkL76bOhmKC4eN/9mW3pj88Wh4MhmuO5q8MEvomc877h+e9UbH478e9Afdo/G/988nAxxwJ/rh+dn03Y9YDzqbPH44PD3tT0Y6HR70pyQOHnUnSJw9PXnfwWB4PjiYncWT7sW89d8d/5PtrvE/Z16YP52DueM/R829ldlpPOienB086k4GmyZpTqi+NX29i8z+zg9ys+KlpZe4wti4pJ/jMmNtb+v/6Rd++b1/aeO9v/k3/6O/+tvP/PDG1u9sVh+dqTfFZeSaeOnL3f3ekT/isnYfvKif+BberF74zT/1K00NsnGNLf6x7H6penZSoAlasN/6U7+ygd9RbfYPtzZg/+PVS7NvemWcWQLUEvjHMwToegLfX31mYcWrK5A3b+x/fPsaYi9PC9Jdqn21kFpnNbW9Fy6qFsO45O6jX/rz/3tT8OvffU/1iYsFuMPJ4TPV6Nr1PG07D8wWF/xo9aFJvbXhyeOd7ulOk1c+rsb20vQpe4Uvno6LY71UPddU9hv1Dnr9t/hyWP+4Nzw/23oKhRDcAm/me5a80lx/u+vB2/52a5Cd62+3Ht3OGnT3PsGVyHgNtEG6TbD9LFBTkdQJmJZ0/JOb1XMXi/HV3tulx/UeDt60G7u35wrR3qo+dlHi7V7/sHe3O+gdzWDM1TRcDdrWNLyG3FxNw+vpda6ht3dr61kgy3sSLNwWM0WhcTod/8Fm9dLFdNztjbjHQX84aHsetNxh4bSM12IBxpVpWQ56MS0ryF2ZltX0OtfQa6elLVgndTstyH9JQdNp+fH3VB+amZbh6dm0PO8PzR3ST1QvPOgPDnf6g52T7sPeXAnPcYPxneqa4pNzhTT/NSyUyed2pjIml1CmprKwMRcb9Rc3q48v2O9jCfX1/uEZL46a26rfXX36GpT4qDt42JvrTLYWRtuZbD3ic53J1qbeWY/60t1Mzkzn7qfmDvm9fmNU9nuH95pqsW3TplWHfAHGskO+APTKIV9EbtkhX0Kvcw29VhZwaV8jpLsNZvtiluR0Xv7NcWnpZlpeb8rxxkfnA65qM1OaEhYVptx/sfrIzCvMI+99YmvKa62YaAO//pPzetlf26woNj6OB49fHx29ys1muCbkXu/0ZDg47eW3eoOzl/uD83de6R74weHX+4PD4dusKOir/Qs+WX3iWlq796bVpQf3r4W+eWP/k9trEH1tqiQwg1mHaud6qu22bpmAEs22nojyi+X7tc2K7rKFxEVCPKeCDg56p5GN51dH/Yf9we6Qg8j+sOky1Ts9+/JoeH7CE/h9Vyfwe6vvLkccarkWf/d+JabTuCYON7ffXnuAH57243zw4ElG6Kw7QjO92KhKwphmeie13Mea0tZ//J7qg5EvX782jN2jo6/hvd7gsGntcufq5Onq0zOwMxKkDEftmewdvtZ756x63wzY63fmO0KuQ2DcEXId0EsdIdel3lmT+ueqDy969dfv3Lyx/4HtS99ZV88vfJUWujMPvffxrWcMq23Em91tP8ss5DY5a25b06zNb2xUH56inLD35fDusPFg243dz11dnheWge/+4erjV79iDuTmjf0Xtpeh/5Hq1oLvuoLfWYLfFGBvz7V11BRgJ3NRgP0bf/+X//p/+YGt/3az+lQ8aiXXYS/1zloW7w+4lNBXomcXy1H/lDVVd/XbP7Me8u4frXZmZ+JahJs39j+zvR7pN6a7vJ2ltWh31qI9yyd1K/5JN1LPqPFB/rXN6nsXkEq9/fOHD/uDh6/0zrqcBRC5qLHd2P0jV+fwc09AYfdg2ul34UQuxrp5Y/9z208wyGFFK6d0+Sid9UdpuaRqmnkI23LJtp47jIXQ1n+9WX1yEb3hwTn7du4ddLmII/ccslen9dNr4e5+fWouL57QS/A3b+x/enstwt+Ybvklk7iAcmcdynsf33rW8TaUWsvbaLafcax7SWMn8/anN6uPLSDUWPank51IV6fs1nVo8+bTStCx+bSa3Lz5dC29zjX02i3VilrR6jUo26YpY3V96//erHYWEPn+3uMmmjdmnS8PD97caxsy2I3dcHWmPv+EVHbfrPSqiVuOefPG/ue3n3Cwo2mHyMXTunq0zpON1hqY7TRzrxrLXLJRfrSdcMmf2qxeWED0leFh78hu7L5a3RpP8Nf7Z4++1j897x41z5A7KAgQ6G4e4vuq72x+/Or58daGZtftMpJzxfuXAbXF+5eSmCvev4pGZykNPqi2OagoxW2jtp+xzUElGovgrb+52ah2C7GbfZ2P93uHrWL4hau78LvXxL6sCV6PMdUE1yB+WRNcj3pnPerNkebTe1tZnr3tC816wu2+uVgEN7RePTnrH/ffbdz9Xz7vHz6RCF5M4ToRvBhrqQheMsh1Inj5KAtF8GLwvY/w5LJDQymWIdtPA9i2ucTW31q8Nb86vIhgcfvWk7Mn2ZpXsa/bmlcxlm7NBcSv25qLqS/cmldBW8Y3bcqFeMH4zMTO+z/fU9ULiL06OHqcOVp8cNZyu9Lrsup+2mzPaYcubsayf/vJKDD+JNrU4m89Gf5M85e7u1+63PzlSan1K7VqeZci8lDbTzbUH5uK2MWLvXKszhON1QSkZvyCrQvqv/tP/s57t356s7q1gNRe70FvNOqN4qNuf9C4Uq8cmc71iPNN0a4BHjdFu47kfFO0NWh2rqW59/zWUw5uf3b7mYmeOjkPf4F9pYuwz0aPX+4f95mbfKX6wHhqvoaTycGt6tkG6LX+ce+V7jtbm6A4ormS1lxEcyVkG9FcTWwuonkttc5qajPGD42jR2gb40eOu7Ju/eTiqbrHSUu9u63Zg1d30eJJucC6blIuIJdOygyx6yZlntrCSbkAad3K49iEHYcYW3tbT2blny7W39l99vJR97jbvs1rbUO2O4MHwyfR35dTuU5/X465VH9fMdh1+vvq0Rbq78tRWidHo787ja2TwzRhzanR9ItPV7cX0Xw0fLuhee+ge/y13uiwf3D29Ta1ym7s/ltXZ/3Nyi0gs/i12CN40D2eQj3pG+z+OxtVWLVk6w1788b+F7d/92+9+8c3qrhyLdd/jc638BrH0x21ePsun8ebN/bF9pPO/aCyqzfw6vE6Tzhe24TTtk04x9Hmyd79v55aqNsztYXb9keubtsfqmABhcD9Jvyg8f0Pzq5s1/UH3T2ddq9fuDirB7p5Y5+2n/z9ds+mfc8XL9H1o3Z+F6NeYzstnqClttOS+bzOdlo+ykLbaTF4u+dUs+fGaSBuHE/f+s82q48vIPS1/n7bH9if9Jf2Sb8Gb65P+jWwbZ/06wjO9Ulfg2LnOoqtbtO4cqUbC5RL/qB/1Erxk3zU5AN+re20Gjgz+vTO4OXhQfdobzg8+/7+0VHbz95u7HYrPWndeP+JcNkXvPXJNVAu+4Kvg5/6gq8lfNkXvA7lzjqUZxMUUMxmIZmJxvSXN6pnI3en+DqnwjauJFF9dDqV+bg3etgbHDyem7D3b83h7H7vNN2EZ+biwc0b++/fngf97DR013zrPGxnDrY5QyiaM4TMtzXixKvzZzaqZyIngPsTf95kn9y6el7eNwczlz0z83ubPTMLOJc9cwmyMwvZvCFA84aceXhbaT7srSt5o/rIDKg/O+sePPKnX2lqCzQ5H1fe98UVGLtx6nGce/t5qJs39l/cXkEkTXtAz3/ZVSqd5VTar255GzSpQwLcbWq++n/bqLYaxLtNNjJnaJT4ytLIaAtV4it3BlxGvr1ofDoXGV0M0kZGl6DPRUaX43eW4LNj9iIGLGH7GbbBbktBE63hn29U2zNf+fqg/yPnvVffHvRGX+0esxPx81e/9qPV9nSU4egSylxz7uVgbXPuFWTmmnOvptNZQaex0EVjobPmL5WbuKR/ldnF8LAXuDvfXkh2YzfNNTQmIVHcPMQPV+/nW6M7+wy4M9o/HHcp/uaf/bsb+++fJ/KjGxvzLGTm2ZiFzPxyiYVcgu3Mwe59ircqtVu1aTZuJpxvpjvwD29s/YccExse9ppAUeKSH/3hgMMNw/Oz0lwMWB4TW4l2KaVwFegkpXAluUsphdfR61xDb+9jW8/YZo2Bc7O2n7GmjUhMGO2fZUcvNwY9OPt6b//Ld1+/1167faV3NuofnM7J4C9Xn7qQwctxbh7uv7T14goAJjQzZ8sJ3dh/aXsloa9Un56drZWUOqsotTzPzvI8mojQ39ioXohcIu3uaMiX7kZ7vcP+qNV87MbuztVds70cYT44tQRoHJxaRmI+OLWCRmcpjb3nt56x1HC9xvX/lDNjz/+vblQfjMPj/f6gN8kaC8PDx0337Csf+txC2F1TfXTmG688v3lj/7nthYi2emn2yxZidhZhcl5PGwe3slWERCOvuVV1u4r//tPc5r1BPHyFL5W92mSrnV/9qP3qpfane6e9u90zztGNw5PHL8dH3dHpN6yuPv0y1xbuHoXzBw96o8jdjXr3zriyBJ+9PDgY8oX/avv1095Xzo6P/NnZqL9/ftZj1vvycPjm+cmcG27lYK0bbiXIvBvuWmqda6jN5euu86HjfN11QC/l665LvbMm9Vkpu3zyWym7YnHmpOxqOp0VdBrtXDRSCcabEtgSUnKqRP7sC9VnJpvyq923+g8bDWU2Uneaeqdvng25e/0ff+rqXv0n76k+4d8a9g9f59ITvdPT7uhx6D0YjnqvD9qs2d7Bm9WL45DOvbPHR717TZnw3B0dPb4z6J9V72v+yYfo5WH3sHp/8+dUqlR15j5Cd0f94ah/Nn6pVx/cHfW+2jQODUNugTHqnrzWPX3ztHqhgX6tu3931OPx/dk9rk54flJ9oDRFMu71ziL3tuxVn7wzODg6P+zdOTlgNfJRr3t4Z3AxCQ1e9eLFD+OEg7ujSWnU6tbFw9cejctszM1ddZtjOOz8vMdNWh/2xvrcySHf9ivD0SvDwya3uc3nPK2eH6P3mkm4IF996O6o13T58Vwfq39YhoOz0+qlu3zJdnTcZpLv9U65xdzd7sNe+/j513ijTmfIn46Xofrg63svv9xcvLz3eHDQGtRbn5mMzbune9R/s/f66GjuC4dcBH4u0fjapW8Tja8Fm080XotqZw2qs3J+xRZs5fwKgHk5fw2lzkpKs6mlcxu/TS2d+2k+tfQKdOcS9M4Uekx6eohu3ti/uX3pYO3erl64RHwOvnMZfjaW+iRnso2lPgnGfCz1ScfqPNlYs/rQMubR6kPLns7rQ6todJbT+Px0NQb3L3Gqmzf2v2v7MvvaFdVHZvLIr2J0rmDM+pfW4H2tf2kNwHn/0pqUO2tRnj3AKzhxe4BXAMwf4GsodVZSmo2BX8f/2xj4dVDzMfB1aHaupzkbcnkyIdSGXJ4MZz7k8uTjdZ50vC9NXTCD+0tk5s0b+x/ZXiZQd/+N6bHnF15OobOUwg9OVcmLV1gtOm/e2P+e7XXF7A9NL6LMvOD19Dvr0ndTU2dwf5FycfPG/oe3F6odu1+oPjbj/1qC21mMO2t4rNReWsNjJci84XEttc411Gb31BLNqd1TSx7O76kVFDpLKcwargt0tNZwXfBg3nBdgtlZhMlGwsRwlbdBseXKvnDpzPjG7Dd+8ef/q39W/fDG1j/aqF7kmXqlqUcxmuo0FxbC9141ED7MTvGrOHPbbxFAu/0Wos5tv2W4nYW4a3ztf/+n/+2/9vTWL/yh6tY1hhHajd2ffs+VL+bb+Yftcu6cdLnJ9M7++eHD3tnWUyCOT/HFautoOHh46dnTSvDDT1YfG5wf7zzu944OuWfd2aOdCa0x4IbGF6oPnLQb+T53je6/NS0MMPNk3Kly8uTD1fumT/rv9A4nv29XW5PfT7uD0/unvVH/wbgKwXMXOLM/f6S6Of35jHvVjCbU9v/kRvU9jVbeTHh+56w34OvZ95p6fOP7Vo3xzCbNi6nHHtz24T1unNB79cGDVpxVNydcLHXPuq+Pjk6r7fHhHR8VXt3SPe4f9Xun1XOcl3X4lddeefluM6uhmavd7vSq3dhSWeO1bt7Y/+z22h+xuz+NKE/sljXH6Kw/xrwVs3TOJlbMUoDLVsxKSp2VlGCqfF7IvslK3byxv7V9Zf12sdq+Ks9mcTpXcWb9KcuXv/WnLH8+709ZTaezis4Xp4xncH/hjrt5Y//57SWb8fumMScWDsuwO4uxm9w0wcloCqW7TZzsC9BkTtPYwfiN3/4z3/x5vnr8t99bPReH3HjwgI2gg96dwWlTptpu7P69Bfzqg9Wz3be6/aPufv+of/Z46z3dwWM8rL6jx3d178PWHx10j3tfOGFK9/tjUve5HsL9buPKvd8/GA7uH3GxlPv9wf2zNg/oi20HNQ7+fKE7ePzFt5sLzl8QXzxtFbwvkBb4x6r3taOMkX4vx+pWVTvW+WnvcOveEwzECGsN8anqow3FnQnFndNHXCFqXLNjzD8/WD07Lh59nyvoNvO9/33V5+7c/cr9ufVizaQtf3LnYDhoqtGMM4Cr980B7j6s5IVltz6Zmzf2d7afZNzdR1MfAFt6TzZS54lGmvWTzOG0fpL575/zk1yB7sxD7316630cahG3lXN0G2H7aQ7/f/Zq0O5nt6qP8knqnzXxvld6x8PR47uj3unp+aj39SaR+v/bqJ71R0ccmVQghL75LH626rRIvj1VR73IfoiD7hEzz9NHw6PDVwIrA0IsgOWrErwt5mCbsj4/sVHdDMPh6RnnMHAVg+7+afXdl9Hn3nFK5LT6RDPXR9xV+2w4alKBXx20yKXbP+JN9fxrXFBl0JoJTXHa1grbej4Oj0+Y5F2uhMiu+8b1fbr7721WHf76/A5fXb309Pf/nCz7rm9lspo5Wbpp/hXNybc8A1tXZnXr+jnZWjonf3GjeuESwZmZWLyKS19t3Y/7lt74b21U9eRZb6+pjtAbTbDzO4+6502lvUtf8XuxoRYsxbozsPtXNqqPjyPmyw/n/k9sfAtjLGUO39L0//kNDpa214jWm+Jv5RO+lTedua90c04tvvw6rVp8+dd5tXgRTucqzmyNkjW/sa1Rsu6+matR8gQjdNYeYdbXsmRFW1/LMtEz52tZQaGzlMJsHOvaDdDGsa4Fm49jrUW1swbVOc/U4o049kwt2aXznqnlFDrLKLQ1EMdVj8zkytlf/rlffu83fu1nf/wFrkS50XhMjrnS5+G9ey83RWlOz/pnXPZ8bHMsTUu8d+/lKe4c4lxa4lKoNi1xOZG5tMSVVDrLqXDSmmmS1vju723SVt+WjeX1Mz/6qz/zfr7b/2nea8PT3pjnfpVtt8LZb6cnvYPGf/2V/qC9svr+SSbTRTGTrU+N0dsJWoI9X8xkDYRxMZN1SM8XM1mTdmct2nuvc7KtbpNt5++asba9t7l/uLd50N3bfPhob7M/2Nt8s7e3efx4b3PwcG/z5NHe5smbe5unD/c2z97d2zx/uLf57vHe5rtvb/34ZvWh8fhf7g04R+Pu+f5R/6BJp9uaVD27/8pbJ43TYTBOg20x7vXOuDfCaZPf3Fi/82mwy6DGabBLicynwa6i0llOZU9tTfOSFVcomb3uK9Ulc+W/2Xjv3/j5/+Xnf2tz6+88tXRGfmq+COmr1Xf1xpmVTb1hLoy99YXz0/rW/mF966Bb33r4qL7VH9S33uzVt44f17cGD+tbJ4/qWydv1rdOH9a3zt6tb50/rG+9e1zfevftfV19x3jkJtepeYVR9/C8e9Y73FoxXx+vbl6e9Js39v/Q9oTa7q3qu65MKEN0phCzovfy0K3ovfzrvOhdhNO5ivP7aHt0t6b1RRTN3OHUQrTG7O/2TF1spX+2/HB9ca6k4M6Trfe/fou1c3Wx2mpDE8/D/Bn+nW9P/L80JjrHNvVFUR511edzsQD/4unqpTHRu6Nh28SxkWx3mwa2TeLtT29WH5tkphvFsZgR+xeVYJNFCkecp96ptk8mBO4PmML9xpV4cHb/vD/2CH626lyGOewddR/fP+ZmaU0r7sPTsSfg89VnLsM+4Fm6z67G+9y1duLHfVrcFkLi91S3LiNcAX1K3Ba4/3L1vD8/45LvR7E76p2Naw9wrOa5hXNRbbefP37Y/ObP4vnodDia01mXkG111iUP53XWFRQ6SynM+usXfkDrr1/4aN5fvxS7swR7NnKxfJLayMWKSZyLXKym01lBZ6/mU6CbO3F8tZ34UlyjWRhLi64rfLNNwmZCr/f3eg/6g+Ye2Kok7Muwl5OwLz+fJmFfQbychL0Is7MIsyk5OP5MbZuCMqxxTtjrH9yvepqlxWe32/9dWq0/0F/VXniY3M+kC3tg6x82lx5atJMH3L76zuDk/IzdnqcrLj0sRrh06WEx0OTSwxISly49LKfRWUpjb4sv7UKzkiTblfzhja1f4jttLcrX6JX+w/Yqq93Y/e6rH/mhRaC7ihMz5j9v5vHNG/sf2l6EpqerfvFJl/A6C/Daz2g3JLnpZ/zd6T7sn/X8g7Pe6G63f+0+nINdsA/nns/uw3nEBfvwCmZnEWZzFU02V9Gam0l6WsbwL7WO/BYhdA/ebHsntXcVeQsXTqSwG7vm6ud9ah3UuRYM14O3LRjWIDvXgmE9up016La10NpLbrotGjO+qSTstGzzf87tMK6Saq4B+UH/eLK1967GoZ+p3vuAq9Cfbm3IfZzY6Ezmy/Po1XOJy/3cOT45mr1XuUDrXIA8q3UueLxI61xCpbOCyqzmsfBlW81j8XfMaR5LsTuLsdsr2eOyNXK2AwLR5D7ZZrU9fXW+5c3XcGYXB+duXCphhLu5wRdMl2MxzqzDfoKztQpnxne+v7tzudbXatxZBWs5WKtgrSAzp2CtptNZQWemRQxOGEjrHt36fzdmtvK9g9Hw6GiOOS65p70MY/Euvwx1aZdfIbJ4ly+i0llOpWUIsmEI2G61calENSm4dT6Ryf2z4aiprDi7z+zyp7Py/OpTvj2oGycsKsW3B/VYzfvGz37zx//G5tbPceXexW7/O4MLrrS8cu/1uPPVGq6HH1drWIPwfLWG9Sh31qHcOLDxwoFtxKQW91/4F7/+E+/f+iurZq0MR73f7axd4K43axfw18zaDOH1Zm2e8opZuwDcu7X1vsl8sfDj6VPNn5ouqoJtXzSjicPBg/6o3ajcyctu7H7p6nzV1Qv8kEtpTPJf+BrEqH/Y+E7nSczrr0vQxvrrkqeX9NcVNDrLacy7j+bfceI+uvTml9xHV3E6V3DaZDfX3FQE2fb3GOsdZG6rsabx9zar74nDwemwkXy98T3qe73RW02uTxOzeXn48OXhw8Zt9oevLsFn1ycwl0q6LlKbSrr2EHOppE8yRmftMdoboW0aIdrZG6Ewuab8n3LxoAtyh3fucgGxu6PhO48nCanLiwetxpsvHrQadlw86BqC88WDrqfYuY7ibOmRsZtQjmXZN37yn/+Df/j01v/TiHPuA92bXo2b5sWuEOdLMC6J8yVQE3G+jMglcb6CSmc5FS7V4FRjA/Gl9UlddzutifMXuW/WsKmsNXG03hkc9t6xG7uvXP3qL1Qf+crw9KyB4DSotvb+l0fd5uLq+GeGuETyle7J3KQsJdJOytLH85OykkpnBZXZ+yorX7q9r7L6u+buq1xLrbOaWlvoqnFtoxt3MGj7GUwP8z9o8mXmEPd6hz3uoWs3duuri/aRpfCXMjYWwkwyNhYTuJSxsZRCZxmFvZvjuq5PORoXi/hzXO62hX79tHcaRsO3T3uj1x71eIMfNdfGesvL3V6DOF/u9hrgcbnb60jOl7tdg2bnWpqzFTQNzrYJcRedEf/Xih0uzb2C8+5RewEqDPnofuSS4WYRblYv/PiPtU1C7456J71Rf3iIN6u5v+nS3/LS3+rS3/rS3+ZmtfvbT10yNNuxf+LHfmWDG2R2z8+GOw+GB+enO6fNC+8P3xlHTD5Zvdh05Dto52Zn1F7T3Okf7jzoT4BuVS+cHD7YaVplHjzqclSgN9o54gK4W08pDqZ8rvrkaW9w2Hb346a9D4ajlmYzTeObPmNy31fJJp374vnFa+08fDQ8Pdtpmxk3/+sPHu6cnnF+d4vdqbbPm9uMO9wWnSu/7PS6B492fuS8N3o8htmuts5PezvdkwNu+TkeZ4r/EX7WHwx6o51HZ8dHsyDjyzxzMM1nX4W5Vb3AMDwxTSuFNlSyc/b4pDe93/NdE4i3+s0dpu7x+NGL1QfHj04XveE2P2y/8nC6NA+au0xjmE9VH2WYt3v7zZQveYOXqudmoS69xX6c6193eVfvD9+pPjHzvC2dPW0eN75SOrcX3c1q9wPVM9O/uXD5rHPixu5rl50Tceuad9ha4x2WtNlbQO1Km70FMEvb7C2h17mO3myC3LUf0ybIXf/Ncwlya1HtXE9170c3xi7pJjiy2RuMAyTN//zrzf+ib/735dD8785X39g8P31j82DwxubB+Rubb568sXk2emPzweiNzf2jNzYHB29snjx4Y/Pk+I3NswdvbL794I3Nh/zfyRubxw/e2Dz+kTc2R703Nh+fPfqln/knf5tbAf/GUh77MQ6gtjy2D3Ywy+z+h38VjPZ3nqqemzDayy/wzW9z229z229z229z298lt/2XxUb3N3uD/ZZr77dce7/l2vst195vufaU2/719y7jtn//qeqjE2731SEnhxRmay3T0wDNHe3F/G58bL/N8BYxvE+sYnjTOfmDzu9uXcfvfp8zkr1PT6Lz7xtXBkchbyNeTfL56e9cdoJ+7Onqk5MTdHF+/MkBV2m5/3p/wi8Ov604fFtx+D1VHH5/n7U/SEKbUwBn+AHNJAaoRSmAv7VZ1fzJ/cF527b7tf4xNx7oHQxHfGy+/qg3aMth3jkt50fcSNFfdbPdfjIi8w27ngBx3LDrSYaab9j1hGN1nmispmf8JDFRt1c5mtQZre1t1Vwi2vqfNxoj8a3eiIOIr/S6fGNunMj32asT+/wS6EtJrwsgJkmvi5AvJb0uwe4sxt770NZ3Ni0mRVPQd3wL4Ru//nP/9P/Y2PrHm/zCJ49f7g/efG3IXbYvygbFq58nqvfPQ1cfuzvq9Y5POMX24tc2Ob6JBc1WP5xHbasfzv82X/3wKnznMvwsI1r9Ji0juuZt5xjR9fQ619Bj17g1jWt8nNW+9T82vv8FE/41XFqp6d6j7oivaz18dMSlEvqDh1/DuUpNiwDaSk0LUecqNS3D7SzEnWniNg5DTYrj/+Rf3eQcs9H+tFG2Pzod+pMTrtHy2vBVric84hzRP/eLm1ezzH7zFzar56eYTfHh0b7nDLXR6dav/MKmEOATuVjIe2CLoZADnb0KIUurvItABYStBahgnI6RgpcuRyTtU/IqYgaFwZMC0KZQLcBE57zJyoC3CDGXrIJVJmowaL2D7IhUqQVkIJVRgDXkyGXrQdqco7FKSnAh2JiDZorFC6O0NrkIFQUooyUKSwgUhKPghVPOy1qgDCXFlFEGDuRJDxFDphyTcMYWbYVUxpRaoC6CRPQqJUupSBuSg0BkcvLeBTLKOGlULbBgVlSkdSgSfxU5F6IrJVm0MTolAaJJDKhIONCSiExQImMKJL0h5aUWsUhZIJlYCwIpfCAfITlK2ubkRImCvEsJlCEkwOJULchHqVIoykSnPLpAGKl4MqjJhRLBOqPI1EJSIbCCihYafLCgogk+ae1CMTE541KiyIAWcjSxJA8UUWmQXhWH0fIiRpFIEmGJtZApKe8waXTGUCFtFSiDqA2Z5Mko0gGDqIXM4K2jrCkBaXIuOwKlXQSHpYAtWSoyUAulExaNquikAGwOKvIHOePJeFIekgpRUS2UC9Ja4yAkMgKcdMaiDUGUbHPQwqaSKWsGTEIKCgjeKgmaio9SRJQWvHAYpRGIxdZCleBQOAXWEnrlQHgXjcVIWiQvtNdUwJpaaEjeUyhFJyEUJAlKo1URsMSgRHDWSlUYUGJwQqBN1hilgEoOQXuNshTKLieUyttYC+0oFItB5Bi1y6loBVZr0kJqp6UvUYMrWAvNOz3lUHIpKDUiJu2jNGSF0zpYBzJYirUwYIqzRWIGMBCNVwgFpC9e+RJkFkop4v1ojIjBmhCNtoJMFll7UCVYFAaUJWlj0nwKTQhGWlJRoUoUNUhyOpiggklCKueil1LZWlhBOWZQNkeRIggjg/ekCqUonRKJghY+lVpYAFNMdlmbqLQAlfnwK+mDsNlYRT7FaHQtrDS2FKFlyDpEoGh0YKaAJRrekNYYGyTVwpqIFgMgKmmsEjqTNAlEymRKcZBTCVByLazLGZLQVnolldIgEAhz5n0DXlsLPifja2GDMzELn8hH7aPWBYIJIehgVOETHUiT4I8JJfoUAiBIwhi0E85B9AZJSkGKovSmMGBCrUwCR04XX3Qx2bhQgs3ohCkajTfRYs07sRSlZMnMNZMsWkVpUJQQCURUGJJAUrVwPkiJaEIha6PLSlhhkwnkreCTjmSVtq4WrmRgZtnsr0SYQSiS5KxLxgYrCuoUeGhv0cmITpoQFFgjTSITSFhZisHATNJ7VLXwTllSifepUBSMLTk6F7IlnZNKvpQgJO9H76Ig5WxMIULBlIXPHiKhcl5bSUomCIqH9i6SKTr7ADFGbb0UPikThUHnQpBJJQoMmK2OISQTBe8/k5WOKCg6Z73MsWQXnAmlFoGKAm2l0FHJ4ERUMqGUwktLMhT0GpRk/hi0KGh5O5WsKYIgoQXKmJVEyyfXgQgEtYigQ/GWIDmwZJWDJAVIyFYXG40TJaASoRYxpxxD8jlnjFok7kYmjBWxWGOcysyqsjS1iMUVG1Cp5GLUUmIpypeYorUYvbOiQNFZ1iIpV4pROirtAbKN2oTIElV6ITwUbW32JGqRNBBvkBK8tiInSkL5GJULVqRoVUSZEagWyajkijcySivRZxZiZHTWvrggRWKJS1IxoGVBCoK8F2izJuOi1c7GLJQtKmgdLW+zFB1JEkZRcs644mUy2UjSJLU2ISBoISHUfCxBiABGEYTko9G5kCQpdUlELkqSaD3WIguKgDKCTBgxFQrCkxCpcHmxEEEoI9CZWmTjDBG3l9CYbMlkIpCgoLX2JRaXVVSqQC1yoxAE6ZITKgf0nJ1rY8SgPISUCIMDDAyovUnSR9TkSyDtLEYlfRIQpPI+ajQyZgYMSWbMWgkTyWXpTZTgQFpvnDes2jhBVItcCBRFZzJ6hOCdFySKdZhsMMmlWCRKrWpRnCYVwakSiwJMKZpiBEURoeSkUchoIlINQkHxOpGyfESTQRGdiiVYsomFR84u6ZhqEDalRFQy6mJlCoosJhFUSSx9RfEZrLG6BhGKthhiiVFH/qSIBq3WRfnsvLFakkJkwJQL5MifkHwyBYQOxXqntUCfVQ7SOJFUDSidMFoqnQSUUIShHMCSRi0CGqOFS9YaXwMak5KRKeWiZCxZg3FExScNKhnttVGBrKgBYyhOZ1V8cKFA8caonA0F57MslItwMSZRAwFaG7LNtuG3ohiXVApZoXYiJSuV9BhVDYS8ZFmyN9yLnEwsqNEjSeOi90qGUlzRNRClgFqq7HWRJhgLCowMVoLRpPhMeFAJapBCBuF9yQ4yBHSS5byTIIyQMvgQcyInSw2StJbWgIkhMb+3VFRxIRiyKNBgkjLZEGuQklCZoIxw1mUbpVFBG6GS9j4l6WV0RTpkQBlYc+RTyjoAseQrIG0gqbNHK7EooxkweBwzdgFWWpOi9zJLEkEYrbVTlPljFIbiC48hKUYfZRFBQVJKOcSSsjEhxUg1KMKomO9HpazSEsBoVQxYlYJNUiQbg8myBqWsjMY677PNLkpNKciM6BAlBpt4B5moa1A6WdCiKIXFiwhAJoUcXWLWHlA6Z52SqQY2JggQibJKaLxNGVPmDWOt9cqTM1kWUYNykqeAtSXULBUdZhVl8BpMUhSDiyFnxYAmBLDIIiVErSAD6sCr7RAUaWZKIWINqsjkg8VgrRWEHozXMnorjPRGJQhFBGcsAzrBu94ZJBGcCoG84OXW0UcVXPLFO08120meWIXS1otgEliZiAVUyMx4rFDglJY1aGt0MJRtClEZK03WzjvUWhBL+iRYy7VM0YMtoLNA6dGUkgwqk6z2KtoSMNpgdAJVg5GYhfTFMTfzLHB4X0frgQ+WwhBThqQZUPrkM2s1BhQWMLGwpAmR2ZkTBchkL2swJmkwWhQrHTotpC0QKQSWx9JbZH1MaF+DsZGiYLmsQ7SJgiPMGCKiL85g1iIohbYGk5WMSCVFsqXExv7SNpK1wpEVHly2LWDxJrmiUonkc0JvDUEgr4wtycTowAoNoQYrTJTZSVUoxGQKsyHkNEIHBSw67zUL4xosCzZvMmbpkpVEoSSThS3WRp002JSlzVSDoxR1LiUrRV6LyDIEJBGLBSmEFjrE7KAGp41yyRpRrBFZi+hylpQxg/Vs6SpuT5MFAxaMJWkVISEvUckpCUzJJfDOS+RRnKvBRSuN9AkyaGsdYlRKpCA96EZvzaz78+7xNsQQvFVOs9poIUIqMQQXfU45iKwj+FhqYN0MeEMbUNpIzfo72qhdzCLLSCGKpBQDBkoJqcQSSvIEwiuLikxhGZCLigI1KFVDEDqboihaYwxkVgxVViGSTFQIU+EphgbQEjhFxaC2iMx6RbYlEu/mYlTCIkrwNQQkXWROEDVvJ6tD8EU7tgiFcEkYE4Jgjsu/aC3RuCiUCmhSEdbZJClplTF7b2MBrCEYZWSjq2XLu62kGL3IObpQnAAZoyQnoYbgpBM2GpKetNLeW0o+ZiGF01CMiV5LrRpAZVUuAFb7ApDJU1SAirwsFlmjSkkwN4tgSBiTi7TJax9SYd09OhFYFQmUrZSkqAG0kLKwNhEZpXzyAphfETlLShqDJqlYQ1TRYTCaZARge82yySJiAp2jiw61N41gj1lZQTomfmchHZHxCGhdSEnKLIqJMsRQQwIRUHgpkkZMHpH1rMJ83+VIxvgowOlUQyLlmXcWjUQBbCYpKOUcLBTWVkJgJcTXkKQ1QYIGI0VkH5ONJhpWyjy4GJTBbIAlV9KYoxWaWWnWVkPWpkiNRhUTbREWpUI+1ymwKUlZyKRU0tmEYnz0PN9eZTbuSiBHNaRYcgk+OROLi4p1NyFdyKqgUEYVRzInyUOnFCiR83wo0QuLRgfvtVM66BKdoaAzKx8peyzJGKlLMWRt8eAVhKIJfXJaRC8RhKshQ1RW2oDeRkRkKQ1G6uSjd2CT1UIVB1BD1s4I9EQ6uaBASchCZSWLAmGMRxackaVrtpGyNAFJZFcEBuUgFhYf6MmyY04Vr6mGnKVET0KFZJrDF4n4TVBpEwWBjZlAiRoyv73X2hSBymLI2SaJBslaq0QMYAqrvTUUKC5FHZQyXhnUQZWgLAggD0TOmGKKsraGEmRUkcAZk1MuGtFmjJIoWR8zUAnCS3I1lBwjOxK9UsmDCNGqjFAMCZ9LdppAQcixRvZeBQDAmKKTYJhrWa1Ro4SEPlhJRptUI88DK+EiASsamJREU/jOB/tMlEmRovGiRsEGTibmA4WlhtPJGmuMRMyNBaFDyTHUCFgoJWNyJhvAF9BRRyjRJQsya2mdItIMqFmt8GyFWZm9KjIkdmuUkoP1VITzIhLUyO46xVY9oAyBVPY+qRBiZI08FubTTqNmwITRFAUGhHMZI3tq2cNTpFWGbc6SvLE1grOOZMoqGpcDm31KJKcx64BWJ0pGAK81AtvpIqLlw5aiLkk6rZwuIQeCZJxl94OuEdEoKUhro0FmR0EZB1FaCgZK1pqksUHnGtGDjc5LEFpGm1i2kxQBQQb2yFpvjXM+1oiszQsUUkslG2er0gHQROLPNyYp6QmhZi0PMkgFwktThEYnsQgLVqkYmVPEyKpxjcS8VQAYF2KRrEBllhiGgo3EHiCngkBTIxntk7Y8fhJgg0MfHWgVjMdiiY3OLCjXSNEnr0X2gTKokrQuqRiVhSpWKyklWxum1EhJ8gk0Dqg4GzzvOse2O5Skk5dZuSwc1iiNCSY6SlbJLNBIqQ2y6UOB5TWbVAW0rVFRdk55k0Ryil1pxjBHDhpJZy2ztiVKgBqVIocqBQUoCFRSQXqptaOUC7vptBG5BFejMhiM8AJ14jqGzjhwCj3mnL13SoYghCYe2uRgQiq85D6yCBJes00Y2GVKnm2AKBjQgWdVFYiVBiNs41IPVEIsSgbSABSjqFF5AlmS54mJOckCmGPSJJLBpJUrbEwD1agyRqNIOgjFRg+OGQRhwGTBcfvbhOxFr1GVAAoRlGZxVRx5DKxBYyE+mTKbhFJQjZqSjMLrHE1GGa2NwqhMJdoYQBgSysqcQ42aXenZYFBWaM9YVrOoCUWVokUG5w04X6N2gTI78L0PmQVO8qi8M0kWaxXy4WGvUY1GAOmYVcCopSoJKBiDKfK2Tcokk1NymBlQs2ZlNAJqI62RMSWFosicfVCFNxRCqVmN8ZIdrCpFx34wZr0ggtQseNgHLGOz1rbdNdEpJ31QvJ6WVQAdMzu0SWRPVqkaLcQUHFkDEmPRQUgDSQtjoTjjSwZJqvD0sEJfQmY1FaT3kEN2NpAuRrOiJDOaDMhDa62MU8ooK0xI3kehjE2CbfPkQpRWF6V8jdYkq7Qh5I2RlS0kkshSOGZCLH8UCtZI0bNnX0jyzkT2DwqvtYMSbDRgtUkRLPtRa+RdLINQ0luZtElW+KBzwAJghEWWYaYIV7MPXhTKGjmQIIzm3spWgHM2KAMQcvZsn9UYIEcWqBzFinwAQpIJEIL2LDnJI5vFosZgilW+sBKJJkTWYByFjJhNdNGyvxSihhrZjxfY8DWSXezKZbDWB3asUrE2sXvV2FRjVElIDcJpViLZHcb6hAdBughWlRVKnxjQiyy0Thg1h+mKUtGQlmzCJgeEAJQyH64EhjToVKySJSAGAy6rkIv3yEaB0jkH72pMKEABCaNdQCQXeJ5DyD4XR60TScSANfIkK9Z2kJVZXfhaddTZ6iR8CEF4kWQQpcZkRCHHMSbWcp0FNjZZebUqGKGjzo60lzUmq0ugwvEjtnETyABZQ5DaQHZN9CQFZ2tM2YPPrBuz65YNoqwSizdPTurgXLYQNdaYyQdNkNl6Y18YZAfWYg5AGgUImY3J1taYrcm+OErFOWejDdllZ4XOzPKIBK9ShFhjTpkQENFpZPds5kiAdGSzBStCsM4I70SNBVGJrHghOQQZrXNsq8pgsvAlOBKYo1MMSIl9GaJYH4nDD0VFKkKymk1OREDwkhjQooumCMEezYRWBSdQa2LlUdmsvLOhpAawCfg4m3RJNiUlRSHJUSnUNkpnks0u11h0oRJdjKwCC8g5iOBNUeiNitahlYp9lzWHvWIW7FiK0TfBW6sK5BKS88QmtFI2MDdjRS2jIWKHcIqS3Vs2QRFFKAgmE0d6ZKoJwLMr1poMUorkIZugOD7ShKeclIl4Z9cECAAai85FGql9yJoDf0kFn4z3RbkCOVPNQqbRNozWUhmdUWufXCpeSqO9cimhi2BqAiOyC0oGDzkXTblIAipsTZSoUYHSEj0PHTguUBAFQvaOfMRoyIJElo9oHGS292vi0ExA3wQkc4ou2Gx4qbwVVjL3sDISvyPzlOI5fmK8EMmZXJIwHMxiaymK7BxLewYkxZpBJi2MFj4kZzVEZodRkQ8lBtTCMqCR7FpmD55UCVRMyeXC4dwsG68rmRgTAxYwgodTSN6CjVqUCF4JdmkmobRI2ACmAjHKEp0ryVqbRTRRRMEhBsduM618UPwxWRcvS06YXNTkvIKsiuBgiUneZOakEFVNhODY5yW1l0E7liGW47wpGKMxqqLIlexrIrbgOaJmi+NIbGGVMSUyXibvMinpDftIiaKMaKTJIansgggc9WXvpTBs0JhIEozxNUkZSWqhlQ0skXMqSSXlOOzttRSGI+bKqZqaMKwqlor1mEK0wE59HwQHSlmpN4CeGDB5J7CQAmMFygK2tcaK5Mh/VpTY12hrUiSF9hyS41PBFAN6b52J3hnQjSIrgq6JeTYGywpTsFYI4zLxHnLIvvUgtGa3YapJsdcPErJDUmlf2GfmXXCYJOvbMmTQnidcWSFN0knLQjFKxbMoM3DuQM6+aCmc8r7UpIIq7FSUxjoQZDU7NISwHqNgbsiR35JzzUFhmwk4Uq4I2P1fosw6JjLotcnOBUXS16TYx4eCMgjW2lGXIF1s7HBCZQWkJIPij8nSYGbOalg3B2MygkisLfGpjA5cKdnVLAxQSh1KwibEw55LTzmQVCZRkJzYAD7X7AilGMFyACPIYFNms5ajkYm8sia4ZFWhmnQUlg82++YjkGRfXWTVzpE0oZTE8dfga9K8dEkrpOw9WJEyx9Ks8jlF4AiwJKMcUyzsATPZagxZckAHvYREJSvwmJMFLN5FBsTkyBRUrJu7gsJztMtC9N5nzp1AhWRqMsKGpFjyWe2tA0cUEm/HwkzESY4hOQ01GR9klKqIkNCFQlFYJCrooAg+Cz5z1DvWZCJrs7Jozy5SJwoKkbQOkvmoDMBmfuElNDkGrwWHIdiOTDEH4YO0WhXBKntks0FQTRa0sxy3t0J4zvwpSIgyc+6CicEogbl4U5O1MqjIHl5hOfDXCH/ntPGQJDlORfAiMSD7fg3PkUkWi05RqGCzkqk4GXKwORlZMgNaR2DZ5xxBWvJOcxzbS5k0eG04MBlDqonPC/hI2TjrQ9SCUzokcEjLOLTRRQVBlZqYfuZlKiliMi5o9sZkY4tIKRsvbUZtqSZnUQAKR0U6oVVmf0cj0YsCdps5ESmrUJMH3oVQFAQvZNAsakHHwFE3CJkZYfSuMGA2IILxZG3j6zBJUkSfEmdtCE2mSMEsxSsOcmglQDhlRUSfpbOYZZZCRicoG9Ja1cSha/Yn+egDKGGsyrbxKUshE1qeE6M01hSQiMODjvNKMiiHjryS6Nnv6dGXLEWRtqbAvElz3JXNLO2cz54Vn5AxeUKO2nEAsKYoNestXhCLrcxOQOLMq5IpELJXRHCMs6aorTFFsxWUo9dClFKipQRJK6U4yyCrxMc1GhbxIdkcXFYmg41WSCB2aCIU9MgJaKam6KIhlbxwzBux8VZ6kByyUCXn4rQtkKCmGIRXZIndTVGKHH1JOnutyUbgvK5CLvPGjZm0jLyqsoSMvKIyWEXJJgcpgXYqOqtqSpyu7yJHRQxr6tHyl5DSOntPOmmTdWG2lwLFYCIlcOyRBZGSCqlwgIL9+kICBnZzU8pG5eQKm1GJE5M8h+U1SeFsVMJA9KrwV7O4Rp1NNoEVEyEcaRmcKVoWp9hLJznAUlOzlF42UV8DwFk5WqsEUfrggIQTXiHrZk1kO2TOtfIegfM2ioSQOdMHiqToQHCwibInbPzLiaNrIIWxmogcB+5zDtKyfzPqmrk1R/aNAdIupOCEL0pTFMmQAcXiIxliwFhKaoyWKDj5LKYsQ/Eyc96CoJC1NjJDTTmj1zH4ZAMSBxEDRyRKscaHYKJ1PkWfYk3sbiAUiDGorF1iQSijCZxSwSZBLqBZvabC8X9tQy6SnW1R8gHLwdrGU2iRU6yydTUVliPE/MZZkzShCqH4mDVkE62JSrI3jQGZ1ShvNGIRQSQOjhvNFoTL1rrkFL+3rImdaIU9b+itAAuM6R0Gn5S3ko+65XBQLYW0zhpvNaWsOWNFJI4SuUyOveGa9UMjSy0RDEW0JgjNrx2d06J4z6qZhph8gIKFXC3R6uyEzSkoFEJxnJGUsl5p4YBfGzknSdSS0EopIVgRkyK2G5N2BVBrTSF4ZVk7jVhL4iRBjhZxvCZYpzh9hXNeoirsKtVBquRlLaUxhhMLbHTgnU5sQVFOqQjNFEISofA2k9KxmRBJxWgzZ4xalZz0RUkbHas5/KJJ1xxlFJScEF46b7wBoUllYXUBKiI2vieIoZayeLDYJmY4q6VHClJpaX0xyTrjOVchu5rDJzo3Fm/WGJJONvC/A7qgOXbCeo8PDOjA+SKTMjlzxpQLUYOCmFG6QEKXqDntkwG9yyWmoKNNaAJpIzndyoFuIqlZkcxgaqlFsBwIA41kk5ZWKgKlTHFRNm4R7YKPtm78BCZhSgqSES7qJG1hf6FP3iRUHLcVWTKg4/yExPtLohPsio5CxBK9NiBd0Gizo1pqIp+k4SQuIqkpJ5sgFVRBEUFB1LmkgrXUKkg2jQALKJBgVJYclXdRoAUM0ipHQtTso3KcVlk4IOuVNNoh52Rx4gBkzfF0Z9HXUgedvC2ch2R95lhp5jTIJCFTMc4VzNYTA+YgMJbIibbILDCgsiHG4lDLbDklUwWkWnKwDLwyJmYpRKDosvExcQIqO3w5Zi2IMgM6GWSRSWrlZNYc6w+a3XiC01I4FKSkkHWTeJg5R4x8cUlpm9EHih4TOBGySJmVT1NLE7LjKBpGD43YLoaiFIYNfw1JSKtTkKmWVllPnOhmTchFEETFeqErnvXbwlqVl17U0mFUmhNAMqAvzKMsoRLsaYfAOb/Sa/RUS6djUkp4pYy1HD/XUeSA2WMAWbJOGYmYpTiTIrtlc1aeuRAnrrC3DrUuHpMKxVLIUEvncyxWC88WjlQ6ZmrSssGBl1YkxVlZ1tTSc4ZntsqxXiY0Z1QLcEWRdsRfqWwuWulaehc5JTdpq/l4Wc0WgXfM4NnHYihnocHVMoiAgaM8nFYtHXrtNfs9IyeLaJYNzCgYUHIqC6sOLD2sz0qpwFmkISBrtblk44urZYTs0PlG3yqJ0FgFBos1idPghQYCTvSrJSdEgGZzGSHxunP6sIMcSmQZH3Ph9BFVy5gEMMEmC0hrA+wOwFg8R9o4bcspBUXVMrHlCyXJTMBu+xyLD0lpTppgLqJVST4xoFRSCKNTiZwngCZCZlmLRrA/KyOyDzvWMrGBxPYl55kqUB6ktjbIFKh4ESSlxNo9A0YjlbFsFvgiXcEYg+Fdao0SGkmG1IiPpIMK3mhXROHUWnI+O5ssQQjZxmLYzUahlimwEgR81L2PmCiopAiJvZugWBsOhpKoZcrZBEEggi7Gsd9XkvPeggaris4sE72VDaBnNio9ZLZCMCdiNuXY+cHqFymHvIRZU+FV0IE99jFRFt4ZlmGFM2hYiVVRMKBBdmLwugbeRyoxA9XSGMGGXWFfXvCqljkIsCqy+JFYvGHHRWG1hWOr4CBmx34FBixBFrJGF52sjMUoZmdCSiSrvc7ZEvtxZS5BcZaJ8UTBZg7fkw4JKajAoQfwpWira1l4MWURghKxEVu8BbBJJE4Ns5wJqzPYXMtCQoEPPjr0nIKYOInVu5DBkzI6KTYivatl0RldVBQTkmJTJTpOgtCmWBGyEsAar0u1ZB6ISSL7TJFFazTE7xKxsJciCZ+DjfyOfPoSpyXwUkWIyjmVENnPVLIsTqdUsmJAilZbb4q12umQNOeJJBsSgGAbLvEm5nf0yRAVU0wiSs6j5awt7w3ZpIyOEok1+VoJj4LdupyXqDi3LRG6pGWy0SZllbXADt5aCY7gWGEs+0AFCAqkOSdMBFE4tYrDL5wSo0AQHxIIINimcE4IKQ1iqyKGpIiFRqwVAKfI8mUCRNLBavJWGh901BJVYS3JW8eAxKHibKSwqDngqzW1nIB9nLpYn0NwolbgjIdApbBZC94ZytJ5CE0mNIAhqUtUuVYQpSQfi1OeSi7sPw6OfC4+ixwpBwO8a2qFbDzGJtYctcLm/gBqKTlXkiInm2aQQdcKSSNwDJGiL8aaLMgLJB8JUYPPxA7gGGqFOkeLnEcUOSCQsLnnEKXAYBCsjmACZ4cp9o860sUqnQXnBCiRTfZJJNLspJeO3Z++VhijNZZ5HTlD3mf2QbO2nHQMnPrJeQwIteIwgA9OlQIm8St5gJCi0zkENHz2VdZItZJOGW+E9h7Jc3pvBk6Zs+n/J+1dmjVLrvO8bhAkxQOQLBQhUSySItEUbQlKtvOeuRS+rcyVS0SZJDq6QUrhCVSXg0ZFFKoQVdWEoAhPPJA98MRzj+zf4AiHBx574rB/gGYOe+CfYE8czz4NoNFoXEgFEcHuPnm+73z72zsva73v8yau+OhH0a3WgJZX15w0VZCSahHE59PqXCor2dVE6hpa9ZpbQTyxEPbiwDDE9kevIkPK3mTWHlpjU5R7TSpH8sjNBocQRA6rrT3WiOvUE1qb/HmcJsrVbRpR2vAyWqYmzX1MEbOF1iQlpMvKaXz3fnrbl3imNzoHvSc0RDG0hrhkt7FmrJ4aTaS6OcXY6hHBotedE2+96qrIUo6XeU5tlQcRxf7sC68NiqQ5GLiqlVOsL3TWaMlR69XREU70MTx1ZrPWUx8iKPq3+REvNaKPFi9DI/pfqYPeR+uiCd3UWqOzf41ZqMEN2VYMjYNVnX0GpDRVEwsfxRmX3XcpKVLmPKu25kejO6+4xzmIo1UHp666tS3cA5FiWzLt2yMzRT/CVqk3W2VF770M9odxHw6h0mtViV5DGzWuuU03e++V0pQqct3td5UgOV6Ta2g0UsaJVjZSubLWwgQWp8d6TrYmOdFEDI3GQKRO0DZ7AlT5I0Yrs7fSe2zY1WTv0ObVvIv7KCeg7u0I845btuKTKaZ7Kzm0WTwfHfnsSF1/DQrXR+mOrFzbOoMygIY2OzVB6aL4HTL7MksrIvaK1FnK1R46oU06nO2UkaWXQuOWckaP07XtVM9SWzPyYfTwADAvZskrU9yQvunNIUBdOdeReRRQ6GJxm1pzq55bRqgwJLfZsDosH53eR9Oe1PL2etxL5eDZnCKwaEVOM9Jkx5tDUy1ZVqrJd5M5l0ZeoNEU7DJLrme2ISvgdigeO42U0lKVLWo5ntNLrNYpB8xxPTNIrmi3lZio7fVsfRWsSWmX5K2dhPighLaQcTTXM72ktXXNyG8u01id44KUltMObTU9u7ZVkHgX8YoSiclw9dw31deFByS0NYdaFsuWVukzLS11jyHJULNxDUZxPsyaHh0ZQIu9SJ+jZRdsAmXUHVc9aKoyAw0pDpvwo0zTFGWZ7mrZGi/BWBLlUVjWnEmpIE6OcXPEdjnYdkpHBR1P0hpD29ILipI661hy3MbyWIQ+U+q181SstSW0rbunOUaR1mMvcdGeXmXgx8OSl+LZvmtoG+29sKNOluflO6SxfFhrztZZaE2ahYaLNLMRwi9XHAemnmXWIgaTcpDQoS1sdvUd5kgtJllJrHjdlntMeqbGOdPUa1UwPnGXfrYu6zuOdfI8+4yex6oj2liVNmCzkfpGcG6ma83pZxynmV5Qw+xVpsj2zcAal2MNWDaFYtUYhzIPkmtE6bhyRgntpJpWyj0WyWrK7d0jTpNpLc9UmsepZYV2qpZjI47Ob6utNno3j1l3YiLfEof1HtqRtvO2Qt19oclNyUuUPQseqjzTnNShQ3O6+vT2Y+EIEnsaJ/aWhe9mSqbX0U4KzbsgtKxK9y9mTl+5RVRDK3WzlFCx5BaaD08ba6t5bSyC3bH8lBp1FkVnPYsv3loiHk38Epq1dsH2ebVGW6o9U1BFjx56jHEUS92mD6NJ3FtNY3Z8TZttFUJjS4Ej5rBpwxfSDsvuUinw9bPSiDuNI11KZSA1MMqkMXmvPfPsNlt01hR5t+6jOkKPx/usFdEac4jTFm7zUGD2OZI06hp1hZ72YpGPtrZ64dRVz5GzOwaklWNFIWoWOopWNUlR4om8PyqJtnvE48Rcma/SWujZkblNNgmaemkpo2LiyRvVVxPjgO8tdCorMozipfrZHQszK14sw2yuy71Ma6hTf9VDG6dcZUTPs8bTdHKv4UTbuUwZoddIx6COtK7mX2e3uWfr2C1V2alEO+eETofUVsxZrkGYiTwaSwaWOtTb5jp76Mzvs64iOdnp3YbmXhrLYaWN3S/f2Niht1bTqvvMZrXqiNZM7vaWlRbdQFZrrYbeelpLz5ic9OY+ss+efeN1XsX77kuLthw63bVWL8daS9qXUP9JR9S01IhDNK7rK2xri7YmRdhaudmRawcsBx+crhNTbtwUbXsvPnNp2NDSQWXUraKjyJ49Sp3YwkPvQhskX+15rIp1iUdsAktqQsKIwGpoYNdmVPadMkW3qXn0eJUvZQ6f69g1NwSqAz7o1aYltuiiYF2NiES2jl79zCI8M6Ps0w67jrQkpVm4sNWUahJWwkmlOs7QxyU+lVQa5/6xrI6xc1tenB32NrPWhYFG3XTivNJBJ66ozJSvO9x35cjZUrPQhw+NOMJXWtNWvebyowVX7SmpUNbCNdRnTJeotK84hMap1HxWHC2uLphIEp6KxkBXTMlc4bh1OA4fyq6AAuIayJWil9BnGhPV34mr+pKZqas3PEGnDyTVs+tevCJ9anO6mBsh6UxM8rXlo/QwxWeX1mbos2ynBNzQkp3KRNu1y4lIXBCCIcpIJ/TZFh6KpmyulyxEHFhZFrq71pvxUSJv3c/IbUfLyelttZIU40en6MnmVJIa13HS30yp0ou+JGb7aEy9eaYYcnDiYxMLXQaifh6Tk1qh8uLocBadqdPEllLlGgzcmL3y2Xj3N9rOy4rAOrFwlSyecQldVsXUynO44q4UrHfKh34hlvbrL+qnh65pVx/qOD2Tj2TXxn53bidcm5HZcOSAU7L0nPtVCyj8slZ35PZ7RvRcskfSFbruSW3dfLdMXcQ2TYvr7kvqzS99WyuhK2LQ2ONgbcml4soyL2orHsmLrnusnb/R1z7uvoTNjqP3XXXkIr2r4uqa3MYzcIqJ3ejAt0Yv9eTibK+2Nm48m9Fr7Ro61ZCBl/iSMPmZyVY0/Lt3recds+Ek6Yvqcd8lY7oceixO79eRaa+0SpLlvaca+upr0+GgKqTJl+o2n6kb4uCYji6bc5XQ0SJ0S2svHXFPR6l5nWcQq8qp5m3nuMOlvJNz/MzhaMrpJ6lXXWP3eRyhRz0xBlxGCG9t4BTOMZ19eNcZY7x0TFXtNCuh04dvfh2/s5n6lnpK5rMOwcpwPXSaQt8F5daZS6k/d4+ZIpq79qia9se+cQ991+Rxx6TRUivlYO7meCh77X0ZN9deIqHTgsNrw/EsAkqI+3SUNqNe5ofcMmiAwA46n5E5i13nsNgP/dSNFoSO2CmXUyh0cB42teQ9aSHjtu6rcrtfSA38UaWtzMCtzurju45dVbydfdkmmQ3KwvRjTUM3gArMNhWB8Gh1InBZVrGw4NYvNr3X0K3bynPVeakDLXaxdnElcPvNUxPO+Outp2rEw553OnvtTAO45kQLmlJ6Pni1+DCerHA0Xgvr3ErbWj2680FVNwu9lV576Ce1ndrYnpAzMnFuPGM042hrUbrb7iv0w1SEe9PQYrRobQjW15XQvpzde/e6d8CDw3anxdzbjOrcNYVun+WGAh+d7J4l9GMc1EAQRFpbmrvxp61WZ4vFZx915zxCPx5pT5xarKCLSEKJU3JZeyeOWL3GZho6nzTuprsh8WuXyXIcibHt1guHI0VDELpfjR2+CesidL104pC2PCM1Xp/du8TQ+Qfzs0qhIoLpsk2jnrritd2siY1JDt0N1waFOG5gKq/Kjt1z2SeK0sxlzQkjHttR697Di0XLZ58lyLhySq13z4MvzgJK14IobEjetEc10sbPjogA03tOEAA0jIT4Jjed/TQcWYXqng0aujpkZPO2zvEwEqWO7EdbHrnOnkeqc8rwLYXe+6VUrSWM1Ba995xzKmPOQrXdqlPxstMuw/O008JIs2Sr1gQZcfaJFYnHtCFw6Li4ZI8Vw0h7r5hQVeR9MLeJz1p7zO3wcKtsKWtYGMlq27pKX0XHTqv3szhBMwO0jYpp5VMsQGDrqgW1de9oFmrKNBiporZTMm9UYg0DOTdijq15nzYQNEMpSEf6PrGMSRtdUxgZd2U1zCBici7bGmqRtY5vxZiDi1MDlrK5quDUS0sWZUSazrh2MQ7P3rhHYxjZyzbE0bvkAs5itdTVKhOauTO5tsSHQTgI/4KZWfiWqFzVmaXrKAt/Se3o9tjtR/eBXtdp3FdfNOMHG12ceinm3RhYm1Z6f7bmVGwA50LTVBAodEFLpc8eA9MlTQbOG6uuYWoxxzKWlZl3zwUnQs7CQOwGxjrf+uJQE+O+mgCnJXZBWHUsh1FPbmpjcC5U/IPJh9oBMFNFUo46tezJwN1rWk2j9ioUxbWgvfReTWkKeS6xaRh0HtsslJG099TP4TRKoyFVi+IIVcupYXDKSCyVBWEMnnVNYrKX1GJ7Ov0izTOMtgea6Bz3YksdM8Zgmb5id6TQNnKmPEuB50xLPa7ENttna6cdtB1xjRnTPhgXVxidr3RiM884hCKC9lPLBWoqF1agD1VeEQ/V6FJEERVhPaKTjsPuSDTqzfQwwuhahx0ObEgCq9D2mDA1bF021BZjVSlhjOQj7j0SZ4k5F0yr2vpEqkXzKJ5L/B/GKKnQfqKmHFFebHQssjboCyTGetKoJ4zRZOeqitmEVp4gUl4asfLxdKBcrCOHQRv9utqyEWpb1HLOdDOBTyP5+r6sh4FZKVZJSTEPwChq0mwqlTtrs0rpPXkN1EOO5L2V1gWGG3AEw/JlvOagHOumDTiGr+PTeVRa3LUvWFqas9Zau8x6vFH0CQMRDOp2OhpWKtKGHG1ShXUITEVj3FHDkAy6AzvcgKxUrBgsKe/Tp58eyyi7LGHgzrHJXt2SmlB24ipsV5pwe7SN3mSHIZIkyomAhphw85CJtN9RLpvkeCGteOvFzq17jyjiluRz6TC1y46pNjtUppkfxWaEpJZbr+dsq2numumrNvoNBTv81slAmdnZR+AAm8n6klZHYZeKaw211HQPQ/PsMZZJ2zmiGY/Hc82Z1nKvo9pJa5QUhp6D3DC3hRdcBwycKE5M5eY4N9DBxxzGqpFSLP72syMqLMVJmqOZGr4E8ymjMtBiPqjJJKFA3tXL6tKTtBr7qmujiOYVJbl4xiwbY+0OgYiTkrOb3ClPRRRywoCMZSrUo6RrdENgPC3T2sTLe5nFooWxB06kSD3bsi/KM0w2+FJ7UlVYBUVaGFsWEIassdKp8ZiSRUqPvmyv3UFf5VjC2H54EJLROR29lbMW05Zv2garNV8uusIwekyr5b4zMzimt5m8cEisbHlLtpR3ZKBLG1lO7gneRotzzITKKGW/ejZ2iTmHVQ4lNZ5URkknV6yuNY7VUKurRDGpzGbWzlws+vHEwS2scSBnpBHO8pHXREIdwDxY2ToTKIU8S2rnLLBGGN5RdnOUGImBzq1c2ogVPQ/lhYKqYNGTWLu1dXrKYZhVEehCtYFqQ0TBNm+dgdzlxIp2p80wDhP/jgJVzVQ4pNtl0F11U89nH9W6hXE2vZU8DsoGJjqEa2vgSNEOZyxjvMsB/l7VgnbVvehuZ1PnQTSuWBZlG3tOD+Mcn1sYWWahhrLH6mMlEXaOZrRQxFsYBzEErcbrgGUrOaUE6CnSZuWwhj0hhuGXzE5OSiVz3tk8IdptWWsj2THkAX7C8LKg5JSJ0MZUpPRULE1r0sX7MMEyOxiI7GXRTy1nVDnDIRHiopkZUyyV58Fb15lKjXPjhkupJ3ZeZZY7+z+FhhG5PBOl0IjKhDNOW44vcLczd57sdmNDMhpLmHHNiiX3WgkzFgWgaNxGGViT5JHoZYQZdzuRfo3EXRENaxfbEdxSXLSUoo4sHpD6nARIIZZepqaozuGIMx5Xbk9aI7rCTHHMMmjCaR7XvlEL0oPY5rCMb7YNcw8T2TjSnaMtZgQgAw12GVLW7j5NpcXsNcwMcA8yHPLyeaJ7TwW7MPLILNOpKqcdZl4chR3TMy4b8XUplgEaRP7k5SeDLZvI1qXM7mVmHX7Swdgzcl79lLZzq1voiM8Sa4uWMEg2QHxle7fV2Q21sks8wzBGhFk2pQrVNiK0kuyWOTTpGF46R+++1RsDcexjuvCYKst4697T7sJ2WFJMy8Y6M8xaaooc9Tha7lwNZJT2Bf/Lo9JWQokWJsRDBdJBLm1RnPWHcyQ7sw3Rc/V5ygyzJaRPwpnXcDmfrQkTeqSHlnNu0nJsPUwON2PMdIzDWC39qOU1xb2eVofCD6i1hgnfYcSGL9/x3iAyhR+RZhl59aayllsMky5RLXthMpz0DaKDA2ixXbS4goq1moc5ABkJ1h69QIXnWnf8YuYJ1mrU3G0ycEfvHP9ZY9DtJYNB0E4C7mXScl2lhDnAw0zu+rkKqo/LMb9mZAM+/frTp/OKZ6aYc/PN3oozcq2rYQ2ydnW6Y+S7CXNCOTFHgaM4tLofrCrVLgOEDKeFxeWRcqBmnTQXU70Ip7iWDQ+VtTr8sI31MOVaYybECUXMo7kiU8U6SscenBjAqDCFyQh62trwNFpZ2hFHbmR0bCkorKgx0CnaYtZNtSv28XJAXZ7mO2LfN11cRzknO72GIzkX5DYNfk3tRY5szwPh4vQw1fLxXdV2atOk9LjPtVeoaWgaxfHg7cHAoYJUUHvq83Cm28wJcJnUNsCrWedhoPVTizsV1dGp56eKxSZ1iHZV8PXpDBOrwpyyFjrXKXQkqS7mva5Vi412Tzxca9C4kFw4GQwqT6Wcvuvmino5CB2SepjcgYhAWDUHbtzTj86+WRZ6PQ0saOXDrKPs3w+arkrbmu/qNPQsppjFNU9KdnP31HX5iRtemrRduUtwRUjWk0u2bL0ycPY8dQwB2UlJQdvsUNhyOjtVlGq19cXApa12GqeH1sTUamWfOWKUWuIx393WDvPy/xXhqIMHtK3YmlWELDiSCk/lGXWFica1xJJlH+hfm4OzSE1mGKNR5FkqKYVp4KFymSex28NqJRLP9SzKbnsoBuIjYVIG8iwDWWW+VHW7s7lqazj1xTaANcYwDYGTUrdL6AOglmxKoy1PUB+S8oh15jDNuy8qwjprhOmTU220BDHu6RRL8ySbYR7ap4rXosyZC991pteThWJfx14aLUYGzth9FskL3h1H3qOsTFNbSmkoq8vgFXXSobDkqPCrZd+ZbzNC6W0ZaWHsLJqeKmasjJ6CFnNJCzOryrZ6dTdqjG3UMB1u7jajHTN8UZl2qZ4wh9j0NDdqYAaCjkAaBS9gsakagLw2Cgf2sKkO3N2BgyoTuESWf7dM660tQasyesPe7JTLwkTFkctIQ1eOGwU2TQUFLQCDUXKS2LoEzEmKdnZt7H3HPKFuijws8VREqjuvVYMkBCW7N6i4a1BpgtPY5DS+LDt0l6drEGSzLQFpAYeQ7BKKrsqMgcPK2K/PMYIkmSehd4uFLS5V9t2bbFeKVGuUbjSMgrDANb7lKF48r7garehxKugOTCZsNlegnzZ91dXmRKQIQS8X7oORZ9y9x5lKGjVIjr4v8tN2SFjNIXJ5N5jHAlXAiohZwOCCWkvOYUJKG4ww2/pBTRtgbMW+MINkwdM+SuwAOtZKFcRpseLjOs7QvpXJW2Nbr2co4De0fXRfUqzV6qrtcnluOj6ChbDVmNR7hpW9pKUoq1jUjR1tVB9rpACmYSi1cNrMy6T3XIA1UY4eo8aYovdWAyquhl/oqrmeVUZaYLJ79l0Ws7PMmIYFKc2SSd29N4ismEhHyou6XC3aqOEsCFVyoajRZ5XWce2sXnfJE9bLcGxJS0Uqb72zxXhH8LAz1SoYwbasRsfT2NnV9R2ksvk19hLzoEPb3fdutOStQjOmas6WS2qHhTdbhAImcg473VSS1JFbwjzLKnSC1Nla6db2bPgtz2n4WiYUzGH0DPvGcxek7eTumEELwstY9YwDkXueqyOhFWhmYaBQlwPPNK3MkS2zgjXoD53aATvBFAMqtVW7Cz53jfVCn/bLz7WkNFQGO2U9gZMT/M/pbOFQkcim9jDSAI9aEMvH7j2wOu2CCh1heTUONxdu0LXqAkpx0NVbwGfpGJGtx7NGSzPJyD1jEKh0RMdIcfJhOht3dNIIztYaMqJlVM5JJWsG1NFrT0EGrQwkhXAr4WEb6KReV1tFdoy5nng8Bhm9yp5npeRzNew7skRz6xO84PKU4GoUBrqowiGYXWXXNGFX6YjAJ1ZKtqhkW5Ax6xHxuE7pu5wup6Vr6wEcENn0mLqTBhm6naYQVtAkmvl+PGXOYuzlKX/mNnlFpuJuDUhpouKpNeG3Gk7ZZrWMFNlikJkzHn26OjPFGPHPRRqgZsm7IymZu2UG3vE9Y4WgwYZF82re1ziQM7U31MQasMBaOlmsZjZzSygDNBNYLfEC5gLASEHkWEONysYsMQp0HV7ZepDFpUHHOc4gWu6WAV0CKJRm/kQQAu/GIwzZklCRwHg1WZdNqBTI2KXUFvPQmOkaIU9fo7fAhM7h8aBJmscb0yfW9hY3KscskuliB9T8OZ6dMNBVF2lpF8reu5UOMsZOZBcZRDdPM5TQLIX+LBDAyAOTkZattg2DSRC1hU7IVPYpvuiWejzaacBhcFDuHi1BrrN8qmsbcHdB49iKisyzDQQzCMtuPQCH2r1NQDej2Vo+mOrSgnUxNGvOqPx2kF1mc6mUoUgvwLzbXTk50BigNtBh3DNwodI+ERuJ+Rot08NC2rQ4aeREo68H2bUO3xsGdYt1gREpfqixN/eNBavvuGuQ3bjKktqkETvbxfJYfeF2UtneDOuDBro0xTimWr3E+LVmcEoZfvuCzY7wvK0g+wzPOs7RiYxIp3uK9Pt3d79wuseE1ZUN+Z7AB202wBxYM+gLINqAIkulN80gpjAhaGprhclCXTYnNE6xAfNnvZ5lBqGkgOC2OQsP4I4NN6z0GnFacmziiBbkKC24QVd8XwdH/ipqE9E5VM6rZWQnyMESPe/0J/VwAFDzbg5zBx59zJs7Jsgx3U3oo6HjoKAw86yYJTEu7YFvdHH3OGqcluqFbzLcnwNt1mCDo3L0TPIvFgM3DhxNh20GZbhdaPMvvB+HbshGVx/E6bE3WHusJDtpTReZMre1wf57RZ7egvjlE0JXNma5Ci1F5cQqF4oJMc5FNAniU9Eegulxen5SCqJXZphrS+7wrusJ4qcfuBJsS/uZ0JehGMBBHaWM0UVkzBQ4x06c5VNzBqQC7E6jUe5sB5HDatLHDgpLvy89fLNrosoZsBim+dBRwRb70ZGCJrocw1AVpio9nZavv3Ip7ibgn2ttLwxccNb5r63xhOrMfnpbqWcoTjAXV7RATkG6FEk+T0xxQiIjOuCUq0IV4TKl6kFTzwjGqq1aliQUwsMX0SCltsxGrWZSJUCjFvG5GwiVlnre4jPXkvDc562borSewDJYQUDHlgcf4fIQgiQ8kZu41rx72jVorjKr7uNDs+gpfBspwvuzwgWZ6eCsZSCySW/FvS461kBfN6kHY24+QISlZUFzzweeTkz1mDJDgROLwNIm8v1tbO4iA3ei09m348BJte+28DTgnzrJm6EV3UGz+JjR0DrXSLmsrdI5f5nLich1KxuVoNko0LrWCPjlHIoA2VKpfR81jqd9J2tBS4SuvR0/SVpQYOFwrNYBY00K4yONMQJs2O4HBQrFD6krq5TVtbbedXi9aMrXKyYshGxsSQaR2JAtA9FfYyPJiBcRal4Dd9F4etp3utcjdohCyX2DeRib4skJmNOz2MLeh3BfpfGLdCGVnAok9zubhqs+DctPR44RpRMuPJMFfneMMYHhpZSDIsyXi5U8hASPBZ0qmzi409l9xEw7Iih8fSDXk14Yqj1YxQrkp+W8vB0+v0rQmorXtKUAJhyrCWeIRoW59hy1IyIBPKW1bI7387C3UkCD1jGLXM9aE5DcIyYPWrvSm1kQcwyTcsvnUPuj/9Xv+LD5zKBVN6w6iwgb6+qrlDTWkDvzLIybYrmNcLFTep0sGKNoqXrSNsCTpyGoqRV7G1MKJLyUzjYKFcsrSTldAY93Le1i6vWxNSgyvLniVaVJPnIcVZaOwoYKHX+kB5yugXU2O3Ack7W5IyZAyqeJr+ekmpGQGwNRhLAPTO2C6c8jjmoOCeC5mu4IDYJ27p6iyP9yo3JE3o5iY8u9gsDdvdfRAz+mjAoOnPga2YCQW2abMqu3vEFMnBiQzNIwAbFvZ6H6K0C8c6QX0aKzh4wpBh0l1QTgQAZPThtCgQFMTEv9UrN7Lu0EHS0eVtDtY+Ri1rUqmARlpSk+2u4n7xTunhDFuFrbLoaddtejUZv3UrrogBW+gg5NF4Y2LbzkDToaW8UBGH0BjY8oiTzogIDmKV+zM+kKkWo4u9Sxo3Rbnil4BfYlCX1YH9vawULULcciEaLISd1Kx5QYdPaaYkGSleV6ju/CE4b2lcflRJuLejhFN7frqG9lHN994HNMqDR9lFoGmE6XoBM8uLFHjX17ifHUvHeVegnJrj131RaDSoxpNKG0ZMTZ5CLlzBhrpnXPJlR4+6AgJwsQ4oyr0q/ecpEsUAZ6TtbIGhAGCsI9oc+MzXmtcc4kqmpr363UitCnW1AEDNfmb1fA+XOmPWJOCFbzQGq7urRhQTVeLSHOx2OuyP2SzLqhBaFqBorEew+qqrv2jM9+T9RsOlbPV7oEGi5srKJVgkIQi+g6VjuwckEUUWCdCCyBBdeKITqonlqVmsbapeE6qJTL61h9zqtpMeaxOcLFkCngttFYlQ0HUSpqMyhoESwL+4gVdFHgLaZ19zVi5rveJlZor8PwXDLanCfgwkOqSE1I5OrHoTDLF6uzzcg+R6OkoOvkAjRv6S6Yfa8amI2F7ynPA8JVtdagu5SRy8ILo0TopNKS+UkRY1eCvV015hWI+CHDAgScYQptluTaJmD7toVHALdRoLICtArZWXbiuGyn3lAUroMlxw5YfQ8KWGmNIoY1qWcpTXEEYUne47js02ApBtJZ4sCLoZcTsBao832m3g+NGLhNabFo7kM0Duw/txUHeSKctyaLMWIuT57P8KDYKcqAXl5oNZ2GK3W2vNtKeZcqGQt1Dnq5OKN3Ubw9FKOx0ozct5BUAAqCAAIGtrhqMqwmQ1nVluZD6BhuLPTO4qNLUPoqkGYhpAjRFA7HlJ7wWKLQqtGgpaC4eTYgfCTaVRpuZvBfvZ3BEg5yb/IV2ulH2tk1UyPLCK1Z9qggI6XJI5mntIIen6WmzopivjdpG7j+LsfTjJlKumIwVk9ElnXJ3Q0pAlEjyWXiJQW1iTRslRbUmQc4ICHvxQAx+GRZU6n0OqBQUdIJ6poQwqJCOIPzSlx0nteqraLs00n/nbfei719LWDFJZOKkfGMAIDrfaMIqHXMoACxkIYUpMSycK4M0lw4Oiya7UD26giLLChIBIaiv5R9MMVgSRf6fPhZ+pK1wkJXGVkjTztAiUEH50ZpeyQZi4MoC1VYKZdZOqroXC4ddF61HFrP/ULUqE886WElLZruTjd6yJ3S3MeerS0yQopS3J51h5V2A+acEBBeyp2mCC6RFDuhL2Pg3JCAFObOehGpch9f2RD2roQbgslm5VNnZ+BCykN3WLDPeQFqu+aOJdI1LDO3KAwEV47OuuShCfa8lOaAzYtBeUyFA+IMsFssHrLM7KycqOjb/hhIKCDtKwasGNYlQlSp+NFOQ1GBUvwoZeGMqr+SSrUZiIS3IhU0lmlvQxwsDkIEpwY+3WSFle2y7tGtSKOrDkegpHSoKMWybiHHCquAc551yZU2V2Paa0aWGsn9CvmRlKT2sEqieYkMD8PXRTC0g5fWCsAdH1feRA2L9hdYU2j+nW6v5QRCAkTZxKY1qGl74GDgZAus6qP5ugxJHRHCGBndORfZcwurDGtNBz2gnKrHeZzndNI3ieyzau1z8YpQK0HpJM7WENl0nzzENNO4biS10C+EoRL32XXuRVEKyRQIBsG7lS8CEVYNXtFROg36iGRayTSg/QcjE8/KxBTR+BtrApRztZKxiWEql5RrKlf/Hkl8y0lLIF/NpGxp3FYDzENERN0ql9Vxi5sMnpnqDpVjc0qYWop26IFCZEyPVHd7hB4dVgOjOA9rEm3uJmRfbACYZcVTdq2WMaetBlWgpuzH9cIVDkmgp5lhtFK2R4/hAe+I3aHxSa7x5nlLuTTTwkOzaFKNuRmY65bRzGdpg0AvkBbTFqyWWc05Udn1iq1dMYCZtJSz1yDxMm91z7Ms2O1zHGPgGYNdQj1ptYKlgKeDbsZGcE5IROPyNNO8S1YQSYVQzKbRCnQpciFJCV1ztxlWc2y72rJiehQDJ9BowJAFUxTM2LJTGQgb3w/FXKIqKJOYYKXJvSFg9kTTPHAWa4te8wYbz6U46VI0ds+tZSXz0LSF1UvuuV/ZO4WdN9mPSSQVwSa9tZ0E1ScschgmQksvO1kZzuYw+Wi449s+GHtBbi+IkhQSyRVwAQG7PZbpMvfo+aIVz5pKWKOQYpUNX4yMtLht2d07011SA1YYu4c1qOt1q90amvVFumiCQdemWiQFsJFAEha7Yhq32bGwrp5pocY5YtVB3GSCsdZGIPVFRp0KfgvFsO9GGAnOt17jjKlLbOuEBT/yePHZBlr5uAohpu3YQca9ViZxtFYGnlE3gREHQvUmEfQiaWKiWhNiJcfvsLjlekSvoC3naMgItNdafRyQV5algttaU61E0gLIcqAxjNLmOibVLLuSRcMhNixB71iw7VY5aOEWh5XeEftUu7KigFqEJYm0LXw1bZyrzp0TWXGQUbq3Wo280xUWZCLs9WMVQQE8qCbP66QwMkzEieLGwmKNa22ewbFy7XS3beeMjKtRmuFz4zpiQyVQKV5qJJwpPgVkJNLHUhoJX11TWAtyWBlru08haBBK/tAhFesSVLsqPhoDF64EotMmRcmmfeZ0ZiRRlBISLnjPAUSBD/ZIG00e/xrbqtVrp3ehcRhBmCWsnXAIjIKox8fOfSWu5epFzc1ZCMxTDWurxphoH61LYG/dkfZf0ZcIc8ld3WeGhZNgCalJJS4EHHo1Iik9YXWmh8jpLSywp2tpVfb669LawyCklZabXx61JSkGGj0NTLHXK110ARUeibTVzB6U9BirPNenSaQoa52jt5FNMGMFx5VjLesO75V5xeYRr/DupK8AlJIxZ72C8NDV760sUWERajht0qAnvPMMZj8q+pbRV4tdq3IO68y42L235HKSjUMyV5XGjvNsEuD2XMkDZfkC0xGdF/j31Jed0c8UL7g9R9095x1IXtHRC8oSLGtkc3YOKiTm8OAC3lyLT013jc1mJZPAtJ1DdmRGccZ7zN4n5OLl2SKEA3bL2o0EDbhlVFySFDR2G2JsWESUWpkO66B7B8U/XCsF7zSmeSW0cGlY5N3smFPMerHGyiCsisJJPa1PsjcQPYV9IRqSzzZrnFfSqlFW1onzLS24Ig0VyY6p17xHhtsQLyzumTlPTyRzGR63TBBA2DySmJV67xZHyUKGIZI5/DXu8OWxloSdGye4SQwTSbprMSezZZmj6kwbQC9Jp7gidCiahAjnAHm9uAu6OTC9nQiV0XvYWboAsM+tC2QRYZUnsiOPdjQniCG977Az1CJIjqvRvSdQ68IN+OmzJAxkJ0kvYedltCIUyAa7GocZfQEpV7p2IVZl2wybGjadlU19rI8FImkqABiewU4v0XOSsEu0ZZFCAC1SBa7YlncKtTLBp+FZSznsIgtMVjLl0M11XAdmNckhHCtlrpp2CWzKTj0EZ1RYBeyKDNmS1oZu2dcla19hN6Th3Q8RF6B9JhsGO34XWrEy1e+2Y9itk5EY+SC4oXQd9NlJNmRnsogPbEULu1GgHaNxlqM/uGhsd4G04xRS9lTkBgykq2NRUxHMIHlJG7WcSbxTSQ2hPDvS3cgYrlkRYF3ni+NZsdIyzaexgf/EXANYAkrqu5zpTDobMWIsKSMYinNicpN2wkaNCaFEzUrnk2a6cq2l051iA97f6Cdswn/HZVeFuJ5ajWQ6j4talGkEWpKeR9izeoUZkeeccG9kuDXU+KrgaFZ2nTN62Li6etSsVzKHu2ByHX6UvF+jV4sPvIctEfmTE8/FITyeyzotMCC2e7n8L1laAFMsQApHTmCvY6kEDxItfYT4kMtQe3bYUvcWRbmh0dNa1XvPkYb9YkpCrHR28oCHDNJe7VVgaVSRWBqQxAofPzaDyL1q2LDrcBAivzpIJetG+1ZBx25KiCyUXJ6r99tjxWkGYfRqYYKiXEBa/PI2qLYAtqbbZP9GLA5rqxKrWMFJ7F4ck9DRHLizWyNIZnLQzWkPotyBtawJSuWqsNUT9kKN6ZsQsFrolqcBIWtLRqzRkLTnuHfY60qbLDE7mvC0T5LFYcJj1Tlmjg0z8gh7XSY8OnCF9N9a2kjnOl8Z4VPCEdZGC1iy5RBsUPtpiPNOdDLJiI2C3bOjkbYeKLutbqTFwuE7iXRmUk9Z4rWlbqo1MuNuj2gH1r5qqPviR/aTykXaSyW1Wol5DNswY3N/tBa9cJ6Bh1ivdLyMD4r+HA+XlUuNjW1smWUyDcjM1Bb3XhknX4+xjrCtp8g5ilQPbICES7WWy+rbU5yn0snfmYEed05ydkKzNsYBaHQVmKDsJ+IZ8uGt3Utvlg/8EIfR1G2K40cC63JJI6aXcAWPcXU40x8ij7rTOY4Dy3JzdukU+MI+jQYgG8LMqldBMhH9uC8s3tzQ3mrNDKzkPkN3pfboujmnwYxKWavgq4lNZtgILIdKqWw+S9moT6+uLjJkTUl8wSMI+7DXaoARKhHz6Pnx7Vc9dUDNyVQcWgz7bNoJG+GtphKdCSuevKirF9yyPXfdEnCE0/FvSmNz5C6HlgQ5quvQ65LVWUQYSJsMEv3KhkRdc7no5iqZbPHSLylv2M7HgJ97qZpQ+ERLKEIHqJEaaUHGWAJdojwBBMPDRx4/qGsxj9hBkKW0pViQrtoDMalXesLYoD36Ik/VCaSjIbt6amG7cZahuD0wQmxT7ckJcx4OEQC/T+nOQHxj7RyW78HhrmBnnIfkaB2E3ViOGixGLeSfLFFtOqds5/yHo4v2qG1EuHMHiyC/CvZdeBdUTxzLy6g9k5wUj4nF7cFSIvG2JTRn6QqS50f94JiE+8Ucm7IGS3OXtGvL+woIxPLN7h20uxBeHg/UBQZyJXrGDWg+akIQQ4wvEtaSKidi3TEzcGx2EfOIZil0wL0n7DDK5p4WaFoWgyU90KWgHVodraincgnrYKMo7yruawTja6ltxDnU+xlXmPd1zkvkraAxtl7XCUC+2gDrnmQJ8bt0yK8TSaf2TOp8ry0GCp/WIoG46C8pSyrxMV4Huew9HS/cVIEltRpc5IpsEbqUV0XMSXVNkOte+6CA9plwVpSdlDlqO3vWPqidQ9dGo7+YH40azvC2Mt0bS22MvDcBdEKKaXcxK7N6sFrhxDYcV0U4JJD3Lhixxjl7JPfK1MrAS6VVZAFWX2QaSYouPMErzb36RVcMBviiI5AedRV65gNzxu496y5aEuveSRaszjJhH3hdamY4oisWPvhqcZ+mzte6glXokcgOAMBCEQDRAoU32qVjL2PgaAlGmwVN/u4zHS2U1KKKcSEQm1IRESZS7OFTYG3tdMXQxOF4Qg7kAIIYkFo30QBXQAgSH+zOCftGe8qxcyzvboOApDFOgJqzgLtmGNvAsDAzI3xsmdzMRZ9djwTMj4njVaNcVJhSqaKuAbsP6v+aJ1ploOAHl3qqbYIKNF1JgzqZoNH25Xn8emtSAMBvR41YSkkvgEQc0yxy7VSph/KKphvKfJquNMbwoyR8IV06kTOF7YblYBDv2ITFcSYoMj/Lj91V50arbKjjzjVYZ3t0ZdOXM2WeepVoiV0h0MCu5q/xKCD32RSWoaUNzqNAqnHOHJRcCVZv1hk4HgxUCPgnaFEhOR6DuMSJebfWvFivAh382hDQJzQG7tDqcKCnAyhLOj6xsyzYJBFA6kyxXo5lgjXqpr2aZqZkM5T8j2B0jhk7Vx2HkKTD3jsmY81xgAbgXVYwyfiyykbDeRl75iikfMG7rmJjI8OcLXB8hxQ7xuD6opiO2A9JJB2HUHQezlaDST880NrWiXsqWmkSFMpmYmBNo/TeUzBUYz1VgZzR/eLTkAVYMgV8HLJ5kaoXTIV8aiyFOUH8xWm+I7nvwKgBQ5BXqoECgRuBHx3abY/p9Nj7joIriGzYyp/rwZZSP0PFBxYBhxaa58lDg/Lf8EWmEYNtLNLzaCI5gzwehFq7sKLPqt2xItG/NuvtwijNOa7kRNXuzTch97NFMn06N3sw81JGMVuVzTo9XiQhLLNzwJWJeJ15ZtD0YM6ro+ZKzfao09IHTO7EPNDd2yvYyYRFl0o9w/PWzDXmMN5tw7+hNEPlw0j/qwZjjBrn3uDfi5iozW2wUxsb9Rhg/9GtjiRUj75qGTSRB8X6ft18TdB3hRPLhT5LyLHpu/d5BtXDSiKxNUUGkHIKNLd3HrOTd6hdaC4livhT8iDnrKvl1Es48bqZN5EBpaNQKg3FC+JO8CBDu/upPZw4pY8rXY+eInbAsSesiMmWWFIDebw8XLzFsSCns/5XokRlkAgzLwI+X9RopQUMKnQ99pXPvEcVr+ibxmHGUNxPUoZIOMkB7rSYy97HZdDP7hmePJU11FAVzWQ4BDniGpiFNsS0pKi/qxMgtYkLx0uTLJyMdWdVxykwUGGjmY/05ZRMuZzIky09nIxTMeeCTUdoSxEOlEhHBovIgt0NusnJlIYjLSq+A+D3VtUy6IDoeXockrt5oFaG+los5ZYMs/o6XTkWHigxFQE3cmUA6X3odVQ7NDmqETHfIIJYHlpw7qqlcDiHG+aI4wlO0IWULsug9S0vV5zfjidA4hJcA3HYRVuj3gOeZ5HvMqS6sD9ioHOc25gJC96VZDhO5umH+0Gr6qxqMWDqTwhmLBE9V7IujFh9qcvZUNp9Ex8fKMOIanFffbgB9ksppzK3HG3wF4k+jynwyORmyh7sgtKOk9AK4UE4WH/6guMRw8FyASGZzsy8YtrJrl40PmMbKWc8D7EG6BZLVtePvxMdlwQGb06p18Q7jf5vYOOEFoyzzlwJxkXqiR1A0mlQnkbkqoQzYm+ITvfO0AELB/BIC5HgXqwJ/AkSwxltSo4Rkp9eZvGMYwQtKfJdgtVAXV8DDwoOWoXDr9nzyqzISZjoQECkU88KV5X1sA6YriGxkPpdVjsovzpJHtS1pjGwxJEUe1I/F1lRcZTztInRzCangq8Q1CvA1I11ZYONG8ISzlNf9mxs9pLUcCbBqxE9NKT4MvsAzBNBWI4sCa0Yc0fg0G/EDhnxzXjIeaxGWtIjBSQKS5QoA99mnwPt1aLGTijrZnIYm7VZY4xYzjwcjdEHzujqINPvCGc2c1fumwXKMF/fjPKtg7c8Z14SKm4Z8jo45xN5Wdq2tMJRYgO8tGh5DT29KWIHLBXk0sY6hOomA0fbtg0NI4dqsGYrJ12eZ3NOvaQWXa+4eyb6oGAwQepEtWVymTSaEiZ7hNTvo6dMqZp7OyXusk18lSorl8mZBEoD5pdwlrPGLUQSBBAVy5hfC64NujoX4Abc/1muaQ1cF4gBlLLhGRMhGTrXKXMmtvDh7EjnJ9V6BZhFjAWKLPTE2hGdQcoRvpm96jQ0c2efHJXb+NTSiC+amO42fMiar4HE4LVCmS1e/VdyDjKJp2UbXXrqHYF1ETU033883D4m9eroYSdbmVSCc9oI59oXo/oFLjopwMxo28h506s9W5bPJAzc0y3j9qDApYWESSVzCyFPouW+9igBrfs5hWcIMbJmVuLIUQi1DZF6eji8hmNWd+eoDI1PNqTETe8KTI2MUwxpm/A3wk9LxBVwgB6OXdBZQQHrkXsXFcNYOEe07TMqIQNzexcWrl69zki8VEVc76IBGMWeURNetdanVzxDTCURDBPpUmOWw0D3BdRrllZrUspwF6gGeyqlXshlqjWgKO+XIDMlG7gcyBSGuIusI+0ecRJLD8fh45FRUHOcMlVsOUX4LeA5C+qUOMXCccmcllsrsaG7IyqKbhQ6CsfDAPkgj3DcseFs4F6b5JbSdV+ccZD2p6hiJIopXCTlvJOmOseql0aP2ZQZBiQ4NrjlWQL452TbG/yKSSuyDwitEe5hOVnSLqg7A6FLTS4NISHaNuIeh51S7Qn1ieWqfWpioPRRcPRZo40Ct143CVGnwFSq3TWqBE/oM7NBrckXSF+P9tVwMQwO563DE+QV+xnLDnfF8KZ7ScaKHPGabLLOnXt8Bp6BUQamWSI5k9TccM/ElqJhoeRSimtwgpcHijVC0TIauFao+cJuwJedr5TgEq4ezFwXAiExY9dCguGp2aLgh8gxKVIOz0hibXrJPi1fWq+1u3IrIus4dPS1afCs0YnEprvGj/HuTYJbO9J+NVtKwyKQuTEjjSp4IdRj8AlG3bHvDUecKXuPGPwKXOarIF2iLM65ku2Q6hIxZMCDkZICSbXisygzvGXQRKmRn4yYOlVi1joJoQFkhKB8BMAwOzzEZBCDYGkT+G7Ys2oPXmDwIv5bHN4qKmzodQjZQNGW5OjfR8D+ljqkpNaBTmDyoyFEjN+0k9FFalwWyEIgnpb8SQ7gy+F6FKBhKNR6QnQI7JdDA+4JUuAHyFXKYTNJw1t4VFliAeMzkE2GRwLVyza8UgRPN1PoMAIjDt9zcOZu/FaTnj7pbxFNu5WDiaDsZjWzZASvTWXnnombr5dstclBgFLXzFT8scBM3pp+ehMYfn11dNPSNkfyPlefDXeRo6gEwEKI0FIaqnlf82ly2aW3ZeWuZ5NHC94GUBi8oeh/mcAX/o7V06ExPUunqrsDrZKYBThioTy0T6Gg1KPBYkhrxYRmoAZ6J+K8W1dPUOOOcrYfl7UZGSecoO7BsZXlVmxjx7BZ2HNmu5KVz7n+earkGGj/mdd9JGZ8s26HnFBlWWqeCvdbn1ODj3RQKumUnBIFE4JjAZ4SDhvdlcJdacEHtnkK/7jnO0wchI6ZUm4aF7F9I5UNROJEDPzQVgVeNyAup1ZD1nluPbVBu8CH950v6M0gvU+3Wx1eB4j4q3ooZRcerllKt0jIjBJ8GzOc9kTf5QpDkLgqIXgMZPOZG/P4FbHcybVEeryXL5poGbRZIO6JnhSwlIjsZayRkQr2ODhzHvanZ57geMjHQJZLBi1lWqXGVFf2fCImD52ptuCX7bzjb+W0Euu0UXEMZruCsElK0517cOm9nLizszFInOxpRWKfa7iIZLswGwXOGDGXQYSPgRFNpIfEabkzT27HKgZ93sXxWJKvjPJTR0I3MwaJftMoIMGYbjNQCDDwTmzjCdaYScqosUMdqIrUAaRVCUT8NXY4Pi2iRJiO+yeSzAZTQibehjqDr95rJjkY6Dfb1KY2GzkX8SorJ870XgLuSBXbraA4R3jTcCankivlb5CkgARycKsEy+JxiYcgP0dqXZyS6blO9rC/1INbAwTUYGZw7rjyVfqhN5JVyBTgi+y8InP3hGkwNq2vi09u5MfEC1mGRzwWDU44os2C0PfAFBwyKoYuWN8IkoEduDFwn9OrgtwaU1HfjgpGgiYiqKkda47uwa8UM4+JvKael4tYtOveqX2PMeJAVVkY6OwPV85c+3hk7sTaQPqAZSUJlyJtwCuX2SlSVOsID1e/wjxqLRsBKRNi0RX8HBWdRK66gb+2TdSDoqKscXVAtNOKBPcyZq5GkH20EadT2M3CrdnA2fkm4sKDu56crHA+Q1ubTxW4GZOdT08A04a0OB7/0c1X9stXj/X585ffe/7s9Rt9/vqlfve7z5/dvv7Gy6+/fLVfvnr98IObr/rLV09un379xTd/7uh7bz3+owe/wIt+4+affPyi3/rWL/aq7/z8V33/t+9/frZ3v/rgC3O8G9/Faftu+sNf+kdv3f8f3r652a8ePX78ff3rZ9+abz/8g5tfPy8ePX5++/TPHn304sm37z19/MVPDnn4xze/9aNP/cP/fO+tx1988Mlh/97Nlz/xOX5s3DufGPf+V+7/esr8UWgj363twRdTyfzrjO3dyN/4L9++/28+d/P39qtHr7/9/u13X756s17dPnr65NVH33n8+vqLf+3jv/ib7d4vP7538xt/9vLDTwxhwH754s2rl8/vBtz/9IAv3Pyq3X7r0UfP39z7zx9+5eYL39Qnb5799aM3z16+uPfWZ4z/k5vf/uEl+PEfMfzBp4e/e/P3f3QpfnL8O58a//7v3P/lFOe7X33wxZQTlyLFfncp7v93v3RT96vbR29u/+Llm2ffevbk+iNf65Mnt999c/t0P392++LNB4++dbtevfze62cvPry7YFymr/3kF9v/di/28Ps3/9EnboG/+Qvce+txf/C3e+t/ffMff/K2+tu99zt/q/d+/w/vfzHlwhdS+EL4eu5u3BQ//nr+37dvfufupd979fJbz57ffu1bf/Hyxe35V89eX9/Buz/5Hfzuz/iNh/vmnU9d6M8Yde+tx7/74Ge8iN380acv2U95lXd++qtcH77Gd+O7o/BgPvhiqjymxL9+/OH/78/d/JO73//nj169ePbiww++/fJ7L37WLfmf/uTl+JO/0Ws8/PCmfuoC/UK/d++tx3/y4G/0Rt++aZ++iL/wO73zN3mnu7usXndZLZ95l/3vb9/c36+evXn25NHzu9f502cv3sy3H/77P3k9v/xZQx+2m9/9xGX79I/vvfX4yw8+69f6ze998iJ81u+98xm/9/4f3P+i8NzUNsa7qT34gnT+rfcffKL/5XM3f7Rfv37v0bMXb569+NBfvvrgu7fPnz978eE/e/XoO9959Oq8evXyFbfMP7v5PXv2+m62P9+5ffXh7Ysn3//Pnj1//sH3nr25PvEf3/9FXurhP78JP7oEP3/8vbce//GDX+iF/8XNn3ziIv1ir/zOL/LK17qQ4rUuJJ49sOgfX8D/j1vio9dvXn7n2b/m4nxw+/z2CbfEX97c+8HimGNuMed4763H/8lnjb750tdefPejN/v5y9e3rz/+T3/v7v+/9+jV69tX798+f/Svrsny4eOb3//By/7liyd3i+uPvcfTf/f3+PLNvY9X5U/86e/fv//LqdR3v/rgl1Np7371+vT/zds3XyK6Jv3Vs6e3L+32ycunt6/mWw8f3fyduwfh9um9p48/+IxB9x/8xH+6+1z6V+n+Oz/lZ3/1nnw8Q+b3793//JR3v/rg85Lv/ph/+fb9/+LmN+1r733w9Rd/+o1vvLcfPfn27Xz7odzc0xffXy8/evHk9huvnn344e2re0/z79586c3dvzx78eE3Hz3hg9//lcfXqMe/cvN5XufaIpVrRhCZ7+bEFokHaESeqrs3/S/f/tW/83/+2//t7Xu/dP//ePvmPr/33qvb17ev/vr2vQ/s0ZtHP3V6+MmhPzY9/OSP76aHz/i1H5sePvv33vmM37ub8a49H/vSa8Zr/FuN+ePb+399++b3+cUP3jx6c/utj57fXcbz4lu83Xdur8nv6z/x6fJv3PzK09vnt29u73/+zauPbvM//Kyr/ZuvP37Rb/74ZX/4Oze//mNX4d5bj3/lwd2PHtz8xo9/Un72zt239fv3fy0lvp5491H4hx7b/Pij/JvP3XyZcd/44R+i198x3374375985vv3b56/ez1m9sXT26//uL59+89zfdvvvjhq0dPbr/53dtXz14+vf+59O38929+89mLN7ev7j7CN9+8eX5tGSM/uffdu5f45tNHbx49fvT6Bx/+SzdfePPsO7evvvn09vmj718v8zufdT0+/+Lli3/ni8Aty+T0bmu9vFu5ZRN3cBvcyNeF+O/fvvnyn758/eb929cvn//17St79vq7j948+fZ8++F/ePPlGP/pp/7X672nj9+5+UP7iw/2yxdPPnr1iqnfXt6+/ouXb/7y9a3fPnrz0avb1w9/++a3PuO377318Es3vx7/6Y/+r9976/1/cP8Lk3utpsla+wXh26pFfvBH/tefu/ld+/qf381NX3vx5PlHT28/+Pajpy+/9/7Ll9cept586YcLUkZOMWK699bj37//s37vod78gx/+1qcn0LuXePrzXqL82Mz+w/f9mX/vp+bUj3/p/d+//4NHroz8bnzwaynPuxuYi/Av/uf/8f/6t1+8/z997uYf//Cl//L17es//caf/5k/ev3mvUdvvn33X3+0Ds+3H+6fu1J/5f4f/JwXfPj1m3/0o9vvZ4+999bjrzz4uS/43s0//sRN+/Nf8Z2f94p3B9i7U0HK77b84EfXsn58yP6vPnfzFfv6n3/w5uWrRx/evn/7/Nmjx8+eP3vz/fPi249e3M1g3Ev/wU9O0L9382C/fPT89vWT249/XV/dPtovv/Od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","variations_safe_seed_date":"13404954528124181","variations_safe_seed_fetch_time":"13404954528135814","variations_safe_seed_locale":"en-US","variations_safe_seed_milestone":0,"variations_safe_seed_signature":"MEYCIQCHtZeRLkqjEaTRcJfRSiaSTu0+oasl1//ghoAKv0tnywIhAKKT0DhC/UA5u+wVQyHjLEpuD0eUsbuvnE3DoTmzeGK7","variations_seed_date":"13404954529000000","variations_seed_milestone":141,"variations_seed_serial_number":"SMChYyMDI1MTAxNC0wOTAwNTYuMDE0MDAwEgkIABADGI0BIAA=#Y/JZV+wno8E=","variations_seed_signature":"MEUCIH5GqFBN0MrvgqHloOy7+1bp5yjsGEy1Y+3y+viYg7phAiEAqfsc18GRWHRsYOTP4e13og+1o6Rx0ogEmei+pzuwY/U=","was":{"restarted":false}} \ No newline at end of file diff --git a/libs/nemesis_dpapi/tests/fixtures/chrome/v137after/C/Users/itadmin/AppData/Roaming/Microsoft/Protect/S-1-5-21-3821320868-1508310791-3575676346-1103/ed93694f-5a6d-46e2-b821-219f2c0ecd4d b/libs/nemesis_dpapi/tests/fixtures/chrome/v137after/C/Users/itadmin/AppData/Roaming/Microsoft/Protect/S-1-5-21-3821320868-1508310791-3575676346-1103/ed93694f-5a6d-46e2-b821-219f2c0ecd4d new file mode 100644 index 0000000..7f5f9a4 Binary files /dev/null and b/libs/nemesis_dpapi/tests/fixtures/chrome/v137after/C/Users/itadmin/AppData/Roaming/Microsoft/Protect/S-1-5-21-3821320868-1508310791-3575676346-1103/ed93694f-5a6d-46e2-b821-219f2c0ecd4d differ diff --git a/libs/nemesis_dpapi/tests/fixtures/chrome/v137after/C/Windows/System32/Microsoft/Protect/S-1-5-18/User/fb1190c1-123d-45f8-95f4-32aee28fe2eb b/libs/nemesis_dpapi/tests/fixtures/chrome/v137after/C/Windows/System32/Microsoft/Protect/S-1-5-18/User/fb1190c1-123d-45f8-95f4-32aee28fe2eb new file mode 100644 index 0000000..6b03fef Binary files /dev/null and b/libs/nemesis_dpapi/tests/fixtures/chrome/v137after/C/Windows/System32/Microsoft/Protect/S-1-5-18/User/fb1190c1-123d-45f8-95f4-32aee28fe2eb differ diff --git a/libs/nemesis_dpapi/tests/fixtures/masterkey_domain.bin b/libs/nemesis_dpapi/tests/fixtures/masterkey_domain.bin new file mode 100644 index 0000000..7f5f9a4 Binary files /dev/null and b/libs/nemesis_dpapi/tests/fixtures/masterkey_domain.bin differ diff --git a/libs/nemesis_dpapi/tests/fixtures/masterkey_local.bin b/libs/nemesis_dpapi/tests/fixtures/masterkey_local.bin new file mode 100644 index 0000000..c7ae743 Binary files /dev/null and b/libs/nemesis_dpapi/tests/fixtures/masterkey_local.bin differ diff --git a/libs/nemesis_dpapi/tests/fixtures/masterkey_system.bin b/libs/nemesis_dpapi/tests/fixtures/masterkey_system.bin new file mode 100644 index 0000000..174b4ad Binary files /dev/null and b/libs/nemesis_dpapi/tests/fixtures/masterkey_system.bin differ diff --git a/libs/nemesis_dpapi/tests/fixtures/masterkey_systemuser.bin b/libs/nemesis_dpapi/tests/fixtures/masterkey_systemuser.bin new file mode 100644 index 0000000..6b03fef Binary files /dev/null and b/libs/nemesis_dpapi/tests/fixtures/masterkey_systemuser.bin differ diff --git a/libs/nemesis_dpapi/tests/fixtures/old_format/ab998260-e99d-4871-8f4b-d922b2848ce6 b/libs/nemesis_dpapi/tests/fixtures/old_format/ab998260-e99d-4871-8f4b-d922b2848ce6 new file mode 100644 index 0000000..742b808 Binary files /dev/null and b/libs/nemesis_dpapi/tests/fixtures/old_format/ab998260-e99d-4871-8f4b-d922b2848ce6 differ diff --git a/libs/nemesis_dpapi/tests/fixtures/old_format/dpapi_blob.bin b/libs/nemesis_dpapi/tests/fixtures/old_format/dpapi_blob.bin new file mode 100644 index 0000000..c60ed3d Binary files /dev/null and b/libs/nemesis_dpapi/tests/fixtures/old_format/dpapi_blob.bin differ diff --git a/libs/nemesis_dpapi/tests/fixtures/old_format/dpapi_blob_b64.txt b/libs/nemesis_dpapi/tests/fixtures/old_format/dpapi_blob_b64.txt new file mode 100644 index 0000000..181dc73 --- /dev/null +++ b/libs/nemesis_dpapi/tests/fixtures/old_format/dpapi_blob_b64.txt @@ -0,0 +1,3 @@ +This is a DPAPI blob: AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAAYIKZq53pcUiPS9kisoSM5gAAAAACAAAAAAADZgAAwAAAABAAAAAwl49Q5xqPHn3EHNNXysI6AAAAAASAAACgAAAAEAAAAEnxLXXgSNJZ6YSYwFKCspYQAAAA30KckUebACyn0orHO7aonxQAAACzZ1VzrkCEXdIHsTkbqva5ndDxmw== + +This is not: blah. diff --git a/libs/nemesis_dpapi/tests/fixtures/old_format/dpapi_domain_backupkey.json b/libs/nemesis_dpapi/tests/fixtures/old_format/dpapi_domain_backupkey.json new file mode 100644 index 0000000..b7b1c47 --- /dev/null +++ b/libs/nemesis_dpapi/tests/fixtures/old_format/dpapi_domain_backupkey.json @@ -0,0 +1,5 @@ +{ + "domain_controller": "testdc.domain.local", + "domain_backupkey_guid": "45cbf2fb-b468-471a-a374-3ca17b50cf3b", + "domain_backupkey_b64": "HvG1sAAAAAABAAAAAAAAAAAAAACUBAAABwIAAACkAABSU0EyAAgAAAEAAQAR+ACjxly5/B43a5YYGh09Ya4EMc/mYSHOD+u4IE9qDSfV2/TLEQOj3zgKyyim+6iNqMBQRBFmL89K17eB+Lofc8uGJwkQ2vL+9hs6ylohrfWaF+gua9Xpb/3iyUiF1bPKccxfu0+1MWaoc+f2+kLOg39SsaETO+9Q8BKxy2xVkYq1npMSGMefaSrurdoKYLy2STa5c/Qd/FQ5/1Mp3GWIUo9igz8XHKUVNkndeENvJqWlI9Das1lxhMl+UGpwXGCRTPirjwAFF3fDCdLI5c+EKCLz4/TBy71rul1BxDvwQIEZI3oaAS82zUp9e60VXmQyMoztrLAoMJwMl4U9RgaNUWSu9CgU73nYaVdiTIAbWaJLrwWWq508rnqhsIFc4HnIoQbFQAuCb6O46skUQK35qbVv9hyCeieJrPHgoIs/mDzm+TLIauXZZD1T2XMb/GnCrN8XQicDsRjcQvh+NKDD6vh60lUPYLEGvHOxVxmBfEpTKVri1wWjuEH77El6U7vBJ49BrR3hdH3uEcTJ3LRQ9ycu0WtfI6ye6Qb2r1hhLviPBwKbtx1Qg4rek0IU05sbdiGsADppSONEeIKO55IkBs7/92bTlls5C0FPX6vK4q/FRadknHtHzAoNMrY0ImwCf+fMVzUE2+K2R5q+C80nTNCSIhHFlQQ6CesHxGa5wIFaH8kb7L6Xy1ZTlkXe++XFjUgPMVla185o4UVDMhka6ruaLAJukk6zlSZLfObXVV4rrksJ+JPSj84SszoIteDSBVsU5GYgYNh7Wp1OiQfeANctD5cRQYJLebEP+9LCU3qKZFL5uotnnsCsB49AZu32sKhindPmoclvyCQ069pbAR+RwRATlIU34Q6TUE1TR09o8Qzy/dMH/6engfq3m/ypHQ3UOboQsDeB3jhV83tsYagZnj/naWlTaoWO9lL9holOx2OECBNDile8WTJynVlvglrhHZHEicNaT1lJA27xckFDjGeKa7pRffh4B+/oHKnAB/zWwaE2d/aj/6jKK7BnpAPkslzoBtWFRUeBCcjjk1ydi488/23zmLKX7lf0DAcjLSJneA2wq0iVlCuVjONfLQMx9IBHeCGMduJsIMkc450W+FAithsKKH3Pl9uQcKD9k3ENM6gCfcgjHQimZCOvmEWeL4O/BycMK+6XwaisF3p6AK/BhxXifwSl0V8jT0F4GFrvbuOXShQjtEUHwygkxL8h3c4BRonvg2/wqCvgnRJYb6SUHJ+s32eaQ8+t76tL7E7v9o02BqZMNclGRlRYEyNwROvF7TXfGFf84E+V3q798vtxcAJ7vZaO5LiwPSx3oifQFWUqvQzRA6dxzKw5S7O/lwLigvPXbbrFkwJ5TOL038R+P9KCYFp1OCSBSrbms8cZkFaSAhqzyXXl+SvCT+OLHUL8qQeo4cpmMPK2/kethFMzg4pF+QkeSSGtwqLFW3MS3q42iTEDeZwnO9COsInIhKhoF0ugfvTkebufz2sAchQzbgnFQP0AJD+8+BNt5QC4+DYKR+N5nvl6QQA=" +} \ No newline at end of file diff --git a/libs/nemesis_dpapi/tests/test_core.py b/libs/nemesis_dpapi/tests/test_core.py new file mode 100644 index 0000000..0d1d758 --- /dev/null +++ b/libs/nemesis_dpapi/tests/test_core.py @@ -0,0 +1,643 @@ +"""Tests for DPAPI core models.""" + +import base64 +from pathlib import Path +from uuid import UUID, uuid4 + +import pytest +from impacket.dpapi import DPAPI_BLOB +from nemesis_dpapi.core import Blob, MasterKey, MasterKeyFile, MasterKeyPolicy, MasterKeyType +from nemesis_dpapi.exceptions import BlobDecryptionError +from nemesis_dpapi.keys import CredKey, CredKeyHashType, MasterKeyEncryptionKey +from pydantic import ValidationError + +masterkey_uuid = UUID("ed93694f-5a6d-46e2-b821-219f2c0ecd4d") +masterkey_bytes = bytes.fromhex( + "36BD60CB9E7E52433169DB00E93ED0A82D3C30C65D948BD8596FB32C267671020B02026B0AE03479DD18374ADBDD7658F45CCE6ED2A45319EFF7A96C411C85F5" +) +masterkey_sha1_bytes = bytes.fromhex("17FD87F91D25A18ABD9BCD66B6D9F3C6BFC16778") +masterkey_entry = "{ed93694f-5a6d-46e2-b821-219f2c0ecd4d}:17FD87F91D25A18ABD9BCD66B6D9F3C6BFC16778" +masterkey_entries = """ +{ed93694f-5a6d-46e2-b821-219f2c0ecd4d}:17FD87F91D25A18ABD9BCD66B6D9F3C6BFC16778 +{12345678-1234-1234-1234-123456789012}:ABCDEF1234567890ABCDEF1234567890ABCDEF12 +""".strip() + +dpapi_system_secret_hex = "01000000dcfd03644f501805c189e15e9367b01415dea75a4e25d96d26879ded571f5d48a6887455d28f66f5" +dpapi_system_secret_bytes = bytes.fromhex(dpapi_system_secret_hex) +dpapi_system_machine_user_key_hex = dpapi_system_secret_hex[8:] # Skip version header +dpapi_system_machine_key_hex = "dcfd03644f501805c189e15e9367b01415dea75a" +dpapi_system_user_key_hex = "4e25d96d26879ded571f5d48a6887455d28f66f5" + +system_masterkey_hex = "020000000000000000000000640064003200360066003800310061002d0034006500640039002d0034003900660064002d0038006200340035002d00340032003700320033006400380061006500300030003600000000000000000006000000b00000000000000090000000000000001400000000000000000000000000000002000000bd2a4e8a1f66c1c29d972ad32534a801401f00000e80000010660000a6fddbe74e2b8975fe896c075bee61c5aee3112b35abc39f96d8229b3c3afe92f9c1db1242edcb84ff61bc3c70f955e73d99b6c1adbc1c8d258a2afd55d553c485eeae7515552ce7805af784b9c02e7cc3dc7dac56fa8fa59f61ebb7f4ad0378ade2f6e456db662ba2ba38441e542c071a2a60aa179835a90eb1f16fa2057808731937b2c4eb30db2b1cee02fff052290200000014c3adfc8d44ee78b8a652bf98e840e8401f00000e80000010660000587695a3307eb92ce3a55de58fb2ee6eee3d8561bd18bea34d44d01b3dba8dea0036cdd5d882c412ce293a3c5316fe6b7e7e2eae7ec11c4d46bbb6654b3f89c77a38fc4d340ea0be7733385a2577fb5b3acdf22fbafc19b9f697f3fa50ba5f1a1d3407c51c0ae14aa8f394f5122def910300000000000000000000000000000000000000" +system_masterkey_bytes = bytes.fromhex(system_masterkey_hex) +system_masterkey_guid = UUID("dd26f81a-4ed9-49fd-8b45-42723d8ae006") +system_masterkey_plaintext_sha1 = "b848ddc68f5250e5977bc52fd9671811ba3bc3b1" + +systemuser_masterkey_hex = "020000000000000000000000660062003100310039003000630031002d0031003200330064002d0034003500660038002d0039003500660034002d00330032006100650065003200380066006500320065006200000000000000000006000000b00000000000000090000000000000001400000000000000000000000000000002000000a4c506cfc4a0e9bcfeefcb8bfcc7f33e401f00000e80000010660000ab63a720b2e4c46bcbe3eb7c7259e7ad746d4e7f1566cdde0716e284a4b4a3f8851895a97db04c512963e11728d19db58873bc8dd8dae54937afbce49f9c723a5dbeba62c3a8b839410fa20a109a9de8857a6dd551052a201d9885365060323cd1d168715de699071e25e9f5c1ec11adf97160ef475d43ff0042c3f93ca47ff7756f335ec733acfc1b522afbeae07ac802000000732740ced42b4ebb05f8273b2f55f317401f00000e800000106600009451eb3cb464cd77ba53c19c792793ce19b7e7e9f1bc6512396b9f3325753741d4076b820e591b4a60418d1b6c6e15b6f30be25026b0ceceb9957e6b46ff5eb173c6642a83dd7bae6a7e5db7332ecbe1c13b0b88cf268ec479a351c0c37c8d425d61f61907b0e9658c832e397dfd67640300000000000000000000000000000000000000" +systemuser_masterkey_bytes = bytes.fromhex(systemuser_masterkey_hex) +systemuser_masterkey_guid = UUID("fb1190c1-123d-45f8-95f4-32aee28fe2eb") +systemuser_masterkey_plaintext_sha1 = "8a6f191d551750fa51324a6b8f3afc7086658888" + + +class TestMasterKeyFile: + """Tests for MasterKeyFile class.""" + + def test_parse_valid_masterkey_file_domain(self): + """Test parsing a valid masterkey file from a domain user.""" + test_file = Path("tests/fixtures/masterkey_domain.bin") + + masterkey = MasterKeyFile.from_file(test_file) + + # Verify basic structure + assert isinstance(masterkey, MasterKeyFile) + assert isinstance(masterkey.version, int) + assert masterkey.version == 2 + assert isinstance(masterkey.modified, bool) + assert isinstance(masterkey.masterkey_guid, UUID) + assert masterkey.masterkey_guid == UUID("ed93694f-5a6d-46e2-b821-219f2c0ecd4d") + assert isinstance(masterkey.policy, MasterKeyPolicy) + assert masterkey.policy == (MasterKeyPolicy.NONE) + + # File path should be set to the parsed file path + assert masterkey.file_path is None + assert masterkey.master_key and len(masterkey.master_key) == 176 + assert masterkey.local_key and len(masterkey.local_key) == 144 + + # Check backup key struct + assert not masterkey.backup_key + + # Check domain backup key struct + assert masterkey.domain_backup_key + assert len(masterkey.domain_backup_key.raw_bytes) == 428 + assert masterkey.domain_backup_key.version == 3 + assert masterkey.domain_backup_key.cb_encrypted_master_key == 256 + assert masterkey.domain_backup_key.cb_encrypted_payload == 144 + assert str(masterkey.domain_backup_key.guid_key) == "7efa51b1-2523-45bf-acba-2e15ecf4f1e7" + assert masterkey.domain_backup_key.encrypted_master_key.hex().startswith("e200130192") + assert masterkey.domain_backup_key.encrypted_payload.hex().startswith("132f05f5") + + def test_parse_valid_masterkey_file_local(self): + """Test parsing a valid masterkey file from a local account.""" + test_file = Path("tests/fixtures/masterkey_local.bin") + + masterkey = MasterKeyFile.from_file(test_file) + + # Verify basic structure + assert isinstance(masterkey, MasterKeyFile) + assert isinstance(masterkey.version, int) + assert masterkey.version == 2 + assert isinstance(masterkey.modified, bool) + assert isinstance(masterkey.masterkey_guid, UUID) + assert masterkey.masterkey_guid == UUID("387a062d-f8b6-4661-b2c5-eecbb9f80afb") + assert isinstance(masterkey.policy, MasterKeyPolicy) + assert masterkey.policy == (MasterKeyPolicy.LOCAL_BACKUP | MasterKeyPolicy.DPAPI_OWF) + + # File path should be set to the parsed file path + assert masterkey.file_path is None + assert masterkey.master_key and len(masterkey.master_key) == 176 + assert masterkey.local_key and len(masterkey.local_key) == 144 + assert masterkey.backup_key and len(masterkey.backup_key) == 20 + assert not masterkey.domain_backup_key + + def test_parse_nonexistent_file(self): + """Test parsing a non-existent file raises FileNotFoundError.""" + with pytest.raises(FileNotFoundError): + MasterKeyFile.from_file("nonexistent_file.bin") + + def test_parse_empty_file(self, tmp_path): + """Test parsing an empty file raises ValueError.""" + empty_file = tmp_path / "empty.bin" + empty_file.write_bytes(b"") + + with pytest.raises(ValueError, match="File too small"): + MasterKeyFile.from_file(empty_file) + + def test_parse_truncated_header(self, tmp_path): + """Test parsing a file with truncated header raises ValueError.""" + truncated_file = tmp_path / "truncated.bin" + truncated_file.write_bytes(b"truncated_data_too_short") + + with pytest.raises(ValueError, match="File too small"): + MasterKeyFile.from_file(truncated_file) + + def test_parse_invalid_size(self, tmp_path): + """Test parsing a file with invalid key data size raises ValueError.""" + invalid_file = tmp_path / "invalid.bin" + + # Create a minimal header that claims more data than available + import struct + + header = struct.pack( + " 0 + assert len(blob.mac) > 0 + + +class TestMasterKey: + """Tests for MasterKey class.""" + + def test_masterkey_decrypt_with_password(self): + """Test MasterKey.decrypt using NTLM credential key with real masterkey data.""" + password = "Qwerty12345" + user_sid = "S-1-5-21-3821320868-1508310791-3575676346-1103" + + expected_masterkey_bytes = bytes.fromhex( + "36BD60CB9E7E52433169DB00E93ED0A82D3C30C65D948BD8596FB32C267671020B02026B0AE03479DD18374ADBDD7658F45CCE6ED2A45319EFF7A96C411C85F5" + ) + expected_masterkey_sha1_bytes = bytes.fromhex("17FD87F91D25A18ABD9BCD66B6D9F3C6BFC16778") + + masterkey_file = MasterKeyFile.from_file(Path("tests/fixtures/masterkey_domain.bin")) + + masterkey = MasterKey( + guid=masterkey_file.masterkey_guid, + masterkey_type=MasterKeyType.UNKNOWN, + encrypted_key_usercred=masterkey_file.master_key, + ) + + cred_key = CredKey.from_password(password, CredKeyHashType.PBKDF2, user_sid) + mk_encryption_key = MasterKeyEncryptionKey.from_cred_key(cred_key, user_sid) + decrypted_masterkey = masterkey.decrypt(mk_encryption_key) + + assert decrypted_masterkey is not None + assert decrypted_masterkey.is_decrypted + assert decrypted_masterkey.guid == masterkey_file.masterkey_guid + assert decrypted_masterkey.plaintext_key is not None + assert decrypted_masterkey.plaintext_key_sha1 is not None + assert len(decrypted_masterkey.plaintext_key_sha1) == 20 + assert decrypted_masterkey.plaintext_key.hex().upper() == expected_masterkey_bytes.hex().upper() + assert decrypted_masterkey.plaintext_key_sha1.hex().upper() == expected_masterkey_sha1_bytes.hex().upper() + + def test_masterkey_decrypt_with_system_credential(self, get_file_path): + """Test MasterKey.decrypt using NTLM credential key with real masterkey data.""" + + masterkey_file = MasterKeyFile.from_file(get_file_path("masterkey_system.bin")) + masterkey = MasterKey( + guid=masterkey_file.masterkey_guid, + masterkey_type=MasterKeyType.UNKNOWN, + encrypted_key_usercred=masterkey_file.master_key, + ) + + mk_encryption_key = MasterKeyEncryptionKey.from_dpapi_system_cred(bytes.fromhex(dpapi_system_machine_key_hex)) + decrypted_masterkey = masterkey.decrypt(mk_encryption_key) + + assert decrypted_masterkey is not None + assert decrypted_masterkey.is_decrypted + assert decrypted_masterkey.guid == system_masterkey_guid + assert decrypted_masterkey.plaintext_key is not None + assert decrypted_masterkey.plaintext_key_sha1 is not None + assert len(decrypted_masterkey.plaintext_key_sha1) == 20 # SHA1 is 20 bytes + assert decrypted_masterkey.plaintext_key_sha1 == bytes.fromhex(system_masterkey_plaintext_sha1) + + def test_masterkey_decrypt_with_systemuser_credential(self, get_file_path): + """Test MasterKey.decrypt using NTLM credential key with real masterkey data.""" + + masterkey_file = MasterKeyFile.from_file(get_file_path("masterkey_systemuser.bin")) + masterkey = MasterKey( + guid=masterkey_file.masterkey_guid, + masterkey_type=MasterKeyType.UNKNOWN, + encrypted_key_usercred=masterkey_file.master_key, + ) + + mk_encryption_key = MasterKeyEncryptionKey.from_dpapi_system_cred(bytes.fromhex(dpapi_system_user_key_hex)) + decrypted_masterkey = masterkey.decrypt(mk_encryption_key) + + assert decrypted_masterkey is not None + assert decrypted_masterkey.is_decrypted + assert decrypted_masterkey.guid == systemuser_masterkey_guid + assert decrypted_masterkey.plaintext_key is not None + assert decrypted_masterkey.plaintext_key_sha1 is not None + assert len(decrypted_masterkey.plaintext_key_sha1) == 20 # SHA1 is 20 bytes + assert decrypted_masterkey.plaintext_key_sha1 == bytes.fromhex(systemuser_masterkey_plaintext_sha1) + + def test_masterkey_decrypt_no_encrypted_key_raises_error(self): + """Test MasterKey.decrypt raises ValueError when no encrypted key is available.""" + from uuid import uuid4 + + # Create MasterKey without encrypted_key_usercred + masterkey = MasterKey(guid=uuid4(), masterkey_type=MasterKeyType.UNKNOWN) + + # Create dummy encryption key + cred_key = CredKey.from_password("dummy", CredKeyHashType.NTLM) + mk_encryption_key = MasterKeyEncryptionKey.from_cred_key(cred_key, "S-1-5-21-1-1-1-1000") + + # Should raise ValueError + with pytest.raises(ValueError, match="No encrypted user credential key available for decryption"): + masterkey.decrypt(mk_encryption_key) + + def test_masterkey_auto_calculates_sha1(self): + """Test that MasterKey auto-calculates plaintext_key_sha1 when only plaintext_key is provided.""" + + # Create MasterKey with only plaintext_key (no plaintext_key_sha1) + masterkey = MasterKey( + guid=uuid4(), + masterkey_type=MasterKeyType.UNKNOWN, + plaintext_key=masterkey_bytes, + ) + + # Verify plaintext_key_sha1 was auto-calculated + assert masterkey.plaintext_key_sha1 is not None + assert masterkey.plaintext_key_sha1 == masterkey_sha1_bytes + assert masterkey.is_decrypted + + def test_masterkey_frozen(self): + """Test that MasterKey is frozen (immutable).""" + + masterkey = MasterKey(guid=uuid4(), masterkey_type=MasterKeyType.UNKNOWN) + + # Should not be able to modify frozen model + with pytest.raises(ValidationError): + masterkey.guid = uuid4() # type: ignore + + def test_masterkey_validates_correct_sha1(self): + """Test that MasterKey accepts correct plaintext_key_sha1.""" + from uuid import uuid4 + + plaintext_key = masterkey_bytes + correct_sha1 = masterkey_sha1_bytes + + # Should accept correct SHA1 + masterkey = MasterKey( + guid=uuid4(), + masterkey_type=MasterKeyType.UNKNOWN, + plaintext_key=plaintext_key, + plaintext_key_sha1=correct_sha1, + ) + + assert masterkey.plaintext_key_sha1 == correct_sha1 + assert masterkey.is_decrypted + + def test_masterkey_rejects_incorrect_sha1(self): + """Test that MasterKey rejects incorrect plaintext_key_sha1.""" + + plaintext_key = masterkey_bytes + incorrect_sha1 = b"0" * 20 # Wrong SHA1 + + # Should reject incorrect SHA1 + with pytest.raises(ValidationError, match="plaintext_key_sha1 does not match"): + MasterKey( + guid=uuid4(), + masterkey_type=MasterKeyType.UNKNOWN, + plaintext_key=plaintext_key, + plaintext_key_sha1=incorrect_sha1, + ) + + +class TestBlobDecrypt: + """Test Blob.decrypt() method.""" + + def test_decrypt_blob_with_unencrypted_masterkey(self, blob_without_entropy: bytes): + """Test DPAPI blob decryption with unencrypted master key.""" + blob = Blob.from_bytes(blob_without_entropy) + # Create an unencrypted master key + masterkey = MasterKey(guid=blob.masterkey_guid, masterkey_type=MasterKeyType.UNKNOWN) + + with pytest.raises(ValueError, match="Master key must be decrypted before use"): + blob.decrypt(masterkey) + + def test_decrypt_blob_with_wrong_masterkey(self, blob_without_entropy: bytes): + """Test DPAPI blob decryption with wrong masterkey.""" + blob = Blob.from_bytes(blob_without_entropy) + # Create a master key with wrong SHA1 hash + + masterkey = MasterKey( + guid=blob.masterkey_guid, + masterkey_type=MasterKeyType.UNKNOWN, + plaintext_key=b"d" * 20, + ) + + with pytest.raises(BlobDecryptionError): + blob.decrypt(masterkey) + + def test_decrypt_blob_with_entropy(self, blob_with_entropy: bytes): + blob = Blob.from_bytes(blob_with_entropy) + assert blob.masterkey_guid == masterkey_uuid + + masterkey_sha1_bytes = bytes.fromhex("17FD87F91D25A18ABD9BCD66B6D9F3C6BFC16778") + masterkey = MasterKey( + guid=blob.masterkey_guid, + masterkey_type=MasterKeyType.UNKNOWN, + plaintext_key=masterkey_bytes, + plaintext_key_sha1=masterkey_sha1_bytes, + ) + + entropy = bytes([1, 2, 3, 4, 5]) + decrypted_data = blob.decrypt(masterkey, entropy=entropy) + + assert isinstance(decrypted_data, bytes) + assert len(decrypted_data) > 0 + assert decrypted_data.decode("utf-8") == "test" + + def test_decrypt_blob_without_entropy(self, blob_without_entropy: bytes): + blob = Blob.from_bytes(blob_without_entropy) + + assert blob.masterkey_guid == masterkey_uuid + + masterkey_sha1_bytes = bytes.fromhex("17FD87F91D25A18ABD9BCD66B6D9F3C6BFC16778") + masterkey = MasterKey( + guid=blob.masterkey_guid, + masterkey_type=MasterKeyType.UNKNOWN, + plaintext_key=masterkey_bytes, + plaintext_key_sha1=masterkey_sha1_bytes, + ) + + decrypted_data = blob.decrypt(masterkey) + + assert isinstance(decrypted_data, bytes) + assert len(decrypted_data) > 0 + assert decrypted_data.decode("utf-8") == "test" + + def test_decrypt_blob_app_bound_enc_key(self, read_file_text): + """Test DPAPI blob decryption with app-bound encrypted key from fixture.""" + blob_b64 = read_file_text("blob_app_bound_enc_key.txt").strip() + blob_data = base64.b64decode(blob_b64)[4:] + blob = Blob.from_bytes(blob_data) + + assert blob.description == "Google Chrome" + assert str(blob.masterkey_guid).lower() == "f752e2e1-1726-454b-a632-0718d94ca677" + masterkey_sha1_bytes = bytes.fromhex("9DED7C56C3FE577B84780908ADAC346F38F1D114") + + # Create a proper master key object + masterkey = MasterKey( + guid=blob.masterkey_guid, + masterkey_type=MasterKeyType.UNKNOWN, + plaintext_key_sha1=masterkey_sha1_bytes, + ) + + # Test our new Blob.decrypt method + try: + decrypted_data = blob.decrypt(masterkey) + assert isinstance(decrypted_data, bytes) + assert len(decrypted_data) > 0 + except ValueError: + pytest.skip("Unable to decrypt app-bound encrypted key blob with provided masterkey") + + # Also verify against dpapick3 reference implementation + from dpapick3 import blob as dpapick3_blob + + blob_dpapick = dpapick3_blob.DPAPIBlob(blob.raw_bytes) + assert blob_dpapick.decrypt(masterkey_sha1_bytes) + + def test_decrypt_chrome_cng_blob(self): + """Test DPAPI blob decryption with app-bound encrypted key from fixture.""" + + masterkey = MasterKey( + guid=UUID("fb1190c1-123d-45f8-95f4-32aee28fe2eb"), + masterkey_type=MasterKeyType.SYSTEM, + plaintext_key_sha1=bytes.fromhex(systemuser_masterkey_plaintext_sha1), + ) + + # CNG SystemKey: 0100000000000000220000000200010004010000520100001C0100000000000000000000000000000000000047006F006F0067006C00650020004300680072006F006D0065006B006500790031002C000000000000000000000010000000080000004D006F00640069006600690065006400438FDAE8593DDC01D8000000200000000000000024000000A0000000430072006500610074006F007200500072006F0063006500730073004E0061006D00650043003A005C00500072006F006700720061006D002000460069006C00650073005C0047006F006F0067006C0065005C004300680072006F006D0065005C004100700070006C00690063006100740069006F006E005C003100340031002E0030002E0037003300390030002E003100300038005C0065006C00650076006100740069006F006E005F0073006500720076006900630065002E00650078006500000001000000D08C9DDF0115D1118C7A00C04FC297EB01000000C19011FB3D12F84595F432AEE28FE2EB000000002E000000500072006900760061007400650020004B00650079002000500072006F0070006500720074006900650073000000106600000001000020000000E1451697BACA6528F7D630D9BAF15ED201FEA6204EDFFF8AFA1087E91EFCE242000000000E8000000002000020000000410FB7DD8537700968CCBD606CD3983E9CF688B10ED971F89678CFE9ABCA6640500000005EA8B351046F51253A04F31AD7837DDEFB65C0B0E1BB420CE46D0C37DBCCA98894F021BC8023E46AF932BAE65228B0FA0D3E851B11B9E604E38AD805A1DD3EB93723EA9590C54E4DFBA06B0433B1A31D400000006AB35EAA9F103166A3154DA144C01A48D2109B9F2BA4C41C08F26B2D53878AF4FE010F623925CA197E1AF740D73D163A03D024DD29DD40976978F4E9C1A23B3701000000D08C9DDF0115D1118C7A00C04FC297EB01000000C19011FB3D12F84595F432AEE28FE2EB0000000018000000500072006900760061007400650020004B006500790000001066000000010000200000007F05374264DEFCD4065DB19AAE164B6AA6B7E51B63EBEE2A24B2682423FC9CF7000000000E8000000002000020000000FA9A99E7200A93C34D63CA3F94336490B50AC245138C9940681706B56CB045DC30000000D49A90D77306D54AB002BE9C31AB6CF84B574450CDABB4C6E58C5FBADE9D8D3C3D024D4DB24DC683CDA249E15FDBFFBF40000000780903E736CFACC632709C2A76DAD0FD862010CCDA01BF7B9256F336CCF67623780F0CB46D37E3B91C5EE20D070EB4FE699997B212037265039671532CB20672 + blob = Blob.from_bytes( + bytes.fromhex( + "01000000D08C9DDF0115D1118C7A00C04FC297EB01000000C19011FB3D12F84595F432AEE28FE2EB0000000018000000500072006900760061007400650020004B006500790000001066000000010000200000007F05374264DEFCD4065DB19AAE164B6AA6B7E51B63EBEE2A24B2682423FC9CF7000000000E8000000002000020000000FA9A99E7200A93C34D63CA3F94336490B50AC245138C9940681706B56CB045DC30000000D49A90D77306D54AB002BE9C31AB6CF84B574450CDABB4C6E58C5FBADE9D8D3C3D024D4DB24DC683CDA249E15FDBFFBF40000000780903E736CFACC632709C2A76DAD0FD862010CCDA01BF7B9256F336CCF67623780F0CB46D37E3B91C5EE20D070EB4FE699997B212037265039671532CB20672" + ) + ) + + assert blob.masterkey_guid == masterkey.guid + + # Test our new Blob.decrypt method + entropy = b"xT5rZW5qVVbrvpuA\x00" + decrypted_data = blob.decrypt(masterkey, entropy=entropy) + assert isinstance(decrypted_data, bytes) + assert len(decrypted_data) > 0 + assert decrypted_data == bytes.fromhex( + "4b44424d0100000020000000442ad62f22c9bc1bf2c9a67a4cfc5ac0d3b660b4e431f6b2232c8a730fbc1e21" + ) + + +class TestBlobParse: + """Test Blob.parse() method.""" + + def test_parse_blob(self, blob_without_entropy): + """Test parsing DPAPI blob data against reference implementations.""" + from dpapick3 import blob as dpapick3_blob + + blob = Blob.from_bytes(blob_without_entropy) + blob_impacket = DPAPI_BLOB(blob_without_entropy) + blob_dpapick = dpapick3_blob.DPAPIBlob(blob_without_entropy) + + # Basic metadata + assert blob.version == blob_impacket["Version"] == blob_dpapick.version + assert blob.prompt_flags == blob_impacket["Flags"] == blob_dpapick.flags + + # GUIDs + assert blob.provider_guid.bytes_le == blob_impacket["GuidCredential"] + assert str(blob.provider_guid).lower() == blob_dpapick.provider.lower() # type: ignore + assert blob.masterkey_guid.bytes_le == blob_impacket["GuidMasterKey"] + assert str(blob.masterkey_guid).lower() == blob_dpapick.mkguid.lower() # type: ignore + + # Description (handle null-terminated UTF-16LE strings) + impacket_desc = blob_impacket["Description"].decode("utf-16le").rstrip("\x00") + assert blob.description == impacket_desc + + if blob_dpapick.description != b"\x00": + dpapick_desc = blob_dpapick.description.decode("utf-16le").rstrip("\x00") # type: ignore + assert blob.description == dpapick_desc + + # Encryption algorithm + assert blob.encryption_algorithm_id == blob_impacket["CryptAlgo"] == blob_dpapick.cipherAlgo.algnum # type: ignore + assert blob.encryption_algorithm_key_size == blob_impacket["CryptAlgoLen"] + + # MAC algorithm + assert blob.mac_algorithm_id == blob_impacket["HashAlgo"] == blob_dpapick.hashAlgo.algnum # type: ignore + assert blob.mac_algorithm_key_size == blob_impacket["HashAlgoLen"] + + # Data + assert blob.encryption_salt == blob_impacket["Salt"] == blob_dpapick.salt + assert blob.encrypted_data == blob_impacket["Data"] == blob_dpapick.cipherText + + +class TestMasterKeyType: + """Tests for MasterKeyType.from_path() method.""" + + def test_from_path_none(self): + """Test from_path returns UNKNOWN for None path.""" + assert MasterKeyType.from_path(None) == MasterKeyType.UNKNOWN + + def test_from_path_empty_string(self): + """Test from_path returns UNKNOWN for empty string.""" + assert MasterKeyType.from_path("") == MasterKeyType.UNKNOWN + + def test_from_path_system_user(self): + """Test from_path correctly identifies SYSTEM_USER paths.""" + test_paths = [ + r"C:/Windows/System32/Microsoft/Protect/S-1-5-18/User/ed93694f-5a6d-46e2-b821-219f2c0ecd4d", + r"C:/WINDOWS/SYSTEM32/MICROSOFT/PROTECT/S-1-5-18/USER/ed93694f-5a6d-46e2-b821-219f2c0ecd4d", + r"/Windows/System32/Microsoft/Protect/S-1-5-18/User/ed93694f-5a6d-46e2-b821-219f2c0ecd4d", + ] + for path in test_paths: + assert MasterKeyType.from_path(path) == MasterKeyType.SYSTEM_USER, f"Failed for path: {path}" + + def test_from_path_system(self): + """Test from_path correctly identifies SYSTEM paths.""" + test_paths = [ + r"C:/Windows/System32/Microsoft/Protect/S-1-5-18/ed93694f-5a6d-46e2-b821-219f2c0ecd4d", + r"C:/WINDOWS/SYSTEM32/MICROSOFT/PROTECT/S-1-5-18/ed93694f-5a6d-46e2-b821-219f2c0ecd4d", + r"/Windows/System32/Microsoft/Protect/S-1-5-18/ed93694f-5a6d-46e2-b821-219f2c0ecd4d", + ] + for path in test_paths: + assert MasterKeyType.from_path(path) == MasterKeyType.SYSTEM, f"Failed for path: {path}" + + def test_from_path_system_service_profiles(self): + """Test from_path correctly identifies LocalService and NetworkService paths.""" + test_paths = [ + r"C:/Windows/ServiceProfiles/LocalService/AppData/Roaming/Microsoft/Protect/S-1-5-19/ed93694f-5a6d-46e2-b821-219f2c0ecd4d", + r"C:/Windows/ServiceProfiles/NetworkService/AppData/Roaming/Microsoft/Protect/S-1-5-20/ed93694f-5a6d-46e2-b821-219f2c0ecd4d", + r"C:/WINDOWS/SERVICEPROFILES/LOCALSERVICE/APPDATA/ROAMING/MICROSOFT/PROTECT/S-1-5-19/ed93694f-5a6d-46e2-b821-219f2c0ecd4d", + r"/Windows/ServiceProfiles/LocalService/AppData/Roaming/Microsoft/Protect/S-1-5-19/ed93694f-5a6d-46e2-b821-219f2c0ecd4d", + ] + for path in test_paths: + assert MasterKeyType.from_path(path) == MasterKeyType.SYSTEM, f"Failed for path: {path}" + + def test_from_path_user_with_sid(self): + """Test from_path correctly identifies USER paths with SID.""" + test_paths = [ + r"C:/Users/john.doe/AppData/Roaming/Microsoft/Protect/S-1-5-21-3821320868-1508310791-3575676346-1103/ed93694f-5a6d-46e2-b821-219f2c0ecd4d", + r"C:/Users/Administrator/AppData/Roaming/Microsoft/Protect/S-1-5-21-1234567890-1234567890-1234567890-500/387a062d-f8b6-4661-b2c5-eecbb9f80afb", + r"C:/USERS/TESTUSER/APPDATA/ROAMING/MICROSOFT/PROTECT/S-1-5-21-111-222-333-1001/ed93694f-5a6d-46e2-b821-219f2c0ecd4d", + r"/Users/john.doe/AppData/Roaming/Microsoft/Protect/S-1-5-21-3821320868-1508310791-3575676346-1103/ed93694f-5a6d-46e2-b821-219f2c0ecd4d", + ] + for path in test_paths: + assert MasterKeyType.from_path(path) == MasterKeyType.USER, f"Failed for path: {path}" + + def test_from_path_user_with_protect_fallback(self): + """Test from_path correctly identifies USER paths using fallback pattern.""" + test_paths = [ + r"C:/Users/john.doe/AppData/Roaming/Microsoft/Protect/somefile", + r"C:/Users/Administrator/AppData/Roaming/Microsoft/Protect/somedir", + ] + for path in test_paths: + assert MasterKeyType.from_path(path) == MasterKeyType.USER, f"Failed for path: {path}" + + def test_from_path_unknown_patterns(self): + """Test from_path returns UNKNOWN for unrecognized patterns.""" + test_paths = [ + r"C:\SomeOtherPath\ed93694f-5a6d-46e2-b821-219f2c0ecd4d", + r"C:\Program Files\MyApp\data", + r"/opt/data/masterkey.bin", + r"D:\Temp\test.bin", + ] + for path in test_paths: + assert MasterKeyType.from_path(path) == MasterKeyType.UNKNOWN, f"Failed for path: {path}" + + def test_from_path_case_insensitive(self): + """Test from_path is case-insensitive.""" + paths_and_expected = [ + (r"c:/windows/system32/microsoft/protect/s-1-5-18/user/guid", MasterKeyType.SYSTEM_USER), + (r"C:/WINDOWS/SYSTEM32/MICROSOFT/PROTECT/S-1-5-18/GUID", MasterKeyType.SYSTEM), + (r"C:/users/JohnDoe/appdata/roaming/microsoft/protect/s-1-5-21-111-222-333-1001/guid", MasterKeyType.USER), + ] + for path, expected in paths_and_expected: + assert MasterKeyType.from_path(path) == expected, f"Failed for path: {path}" + + def test_from_path_mixed_slashes(self): + """Test from_path handles mixed forward and backward slashes.""" + test_paths = [ + r"C:/Windows/System32/Microsoft/Protect/S-1-5-18/User/ed93694f-5a6d-46e2-b821-219f2c0ecd4d", + r"C:/Users/john.doe/AppData/Roaming/Microsoft/Protect/S-1-5-21-111-222-333-1001/ed93694f-5a6d-46e2-b821-219f2c0ecd4d", + ] + assert MasterKeyType.from_path(test_paths[0]) == MasterKeyType.SYSTEM_USER + assert MasterKeyType.from_path(test_paths[1]) == MasterKeyType.USER diff --git a/libs/nemesis_dpapi/tests/test_eventing.py b/libs/nemesis_dpapi/tests/test_eventing.py new file mode 100644 index 0000000..5d87723 --- /dev/null +++ b/libs/nemesis_dpapi/tests/test_eventing.py @@ -0,0 +1,129 @@ +"""Tests for DPAPI eventing.""" + +from datetime import UTC, datetime +from uuid import UUID + +from nemesis_dpapi.eventing import ( + NewDomainBackupKeyEvent, + NewDpapiSystemCredentialEvent, + NewEncryptedMasterKeyEvent, + NewPasswordDerivedCredentialEvent, + NewPlaintextMasterKeyEvent, + TypedDpapiEvent, +) +from nemesis_dpapi.keys import DpapiSystemCredential, NtlmHash, Password + + +class TestTypedDpapiEventDeserialization: + """Test TypedDpapiEvent deserialization.""" + + def test_deserialize_from_dict(self): + """Test deserializing TypedDpapiEvent from dictionary.""" + masterkey_guid = UUID("12345678-1234-5678-1234-567812345678") + timestamp = datetime.now(UTC) + + data = { + "type_name": "NewPlaintextMasterKeyEvent", + "evnt": {"masterkey_guid": str(masterkey_guid), "timestamp": timestamp.isoformat()}, + } + + typed_event = TypedDpapiEvent(**data) + + assert typed_event.type_name == "NewPlaintextMasterKeyEvent" + assert isinstance(typed_event.evnt, NewPlaintextMasterKeyEvent) + assert typed_event.evnt.masterkey_guid == masterkey_guid + + def test_deserialize_from_already_instantiated_event(self): + """Test deserializing when event is already an instance (not a dict).""" + masterkey_guid = UUID("12345678-1234-5678-1234-567812345678") + timestamp = datetime.now(UTC) + + event = NewPlaintextMasterKeyEvent(masterkey_guid=masterkey_guid, timestamp=timestamp) + + data = {"type_name": "NewPlaintextMasterKeyEvent", "evnt": event} + + typed_event = TypedDpapiEvent(**data) + + assert typed_event.type_name == "NewPlaintextMasterKeyEvent" + assert isinstance(typed_event.evnt, NewPlaintextMasterKeyEvent) + assert typed_event.evnt.masterkey_guid == masterkey_guid + + def test_deserialize_encrypted_masterkey_event(self): + """Test deserializing NewEncryptedMasterKeyEvent.""" + masterkey_guid = UUID("87654321-4321-8765-4321-876543218765") + + data = { + "type_name": "NewEncryptedMasterKeyEvent", + "evnt": {"masterkey_guid": str(masterkey_guid)}, + } + + typed_event = TypedDpapiEvent(**data) + + assert isinstance(typed_event.evnt, NewEncryptedMasterKeyEvent) + assert typed_event.evnt.masterkey_guid == masterkey_guid + + def test_deserialize_domain_backup_key_event(self): + """Test deserializing NewDomainBackupKeyEvent.""" + backup_key_guid = UUID("11111111-2222-3333-4444-555555555555") + + data = {"type_name": "NewDomainBackupKeyEvent", "evnt": {"backup_key_guid": str(backup_key_guid)}} + + typed_event = TypedDpapiEvent(**data) + + assert isinstance(typed_event.evnt, NewDomainBackupKeyEvent) + assert typed_event.evnt.backup_key_guid == backup_key_guid + + def test_deserialize_dpapi_system_credential_event(self): + """Test deserializing NewDpapiSystemCredentialEvent.""" + cred = DpapiSystemCredential(machine_key=b"0" * 20, user_key=b"1" * 20) + + data = { + "type_name": "NewDpapiSystemCredentialEvent", + "evnt": {"credential": cred.model_dump()}, + } + + typed_event = TypedDpapiEvent(**data) + + assert isinstance(typed_event.evnt, NewDpapiSystemCredentialEvent) + assert typed_event.evnt.credential.machine_key == b"0" * 20 + assert typed_event.evnt.credential.user_key == b"1" * 20 + + def test_deserialize_password_derived_credential_event(self): + """Test deserializing NewPasswordDerivedCredentialEvent.""" + password = Password(value="test123") + user_sid = "S-1-5-21-1234567890-1234567890-1234567890-1001" + + data = { + "type_name": "NewPasswordDerivedCredentialEvent", + "evnt": { + "type": "Password", + "credential": password.model_dump(), + "user_sid": str(user_sid), + }, + } + + typed_event = TypedDpapiEvent(**data) + + assert isinstance(typed_event.evnt, NewPasswordDerivedCredentialEvent) + assert typed_event.evnt.type == "Password" + assert isinstance(typed_event.evnt.credential, Password) + assert typed_event.evnt.credential.value == "test123" + assert typed_event.evnt.user_sid == user_sid + + def test_deserialize_ntlm_hash_credential_event(self): + """Test deserializing NewPasswordDerivedCredentialEvent with NTLM hash.""" + ntlm_hash = NtlmHash(value=b"0" * 16) + + data = { + "type_name": "NewPasswordDerivedCredentialEvent", + "evnt": { + "type": "NtlmHash", + "credential": ntlm_hash.model_dump(), + }, + } + + typed_event = TypedDpapiEvent(**data) + + assert isinstance(typed_event.evnt, NewPasswordDerivedCredentialEvent) + assert typed_event.evnt.type == "NtlmHash" + assert isinstance(typed_event.evnt.credential, NtlmHash) diff --git a/libs/nemesis_dpapi/tests/test_keys.py b/libs/nemesis_dpapi/tests/test_keys.py new file mode 100644 index 0000000..5635db1 --- /dev/null +++ b/libs/nemesis_dpapi/tests/test_keys.py @@ -0,0 +1,941 @@ +"""Tests for DPAPI cryptographic operations.""" + +import base64 +import json +from unittest.mock import PropertyMock, patch +from uuid import UUID + +import pytest +from nemesis_dpapi.core import Blob, MasterKey, MasterKeyFile, MasterKeyType +from nemesis_dpapi.keys import ( + CredKey, + CredKeyHashType, + DomainBackupKey, + DpapiSystemCredential, + MasterKeyEncryptionKey, + NtlmHash, + Password, + Pbkdf2Hash, + Sha1Hash, + _derive_secure_cred_key, +) +from nemesis_dpapi.manager import DpapiManager + +password = "Qwerty12345" +ntlm_hash = "abd9ffb762c86b26ef4ce5c81b0dd37f" +ntlm_bytes = bytes.fromhex(ntlm_hash) +user_sid = "S-1-5-21-3821320868-1508310791-3575676346-1103" +user_sid_bytes = user_sid.encode("utf-16le") + +credkey_ntlm_hash = ntlm_hash +credkey_sha1_hash = "15056cbc481efd37bba0e97e9c28493a40cf8745" +credkey_pbkdf2_hash = "775ec403415b49002386ea8e477346cd" + +masterkey_uuid = UUID("ed93694f-5a6d-46e2-b821-219f2c0ecd4d") +masterkey_bytes = bytes.fromhex( + "36BD60CB9E7E52433169DB00E93ED0A82D3C30C65D948BD8596FB32C267671020B02026B0AE03479DD18374ADBDD7658F45CCE6ED2A45319EFF7A96C411C85F5" +) +masterkey_sha1_bytes = bytes.fromhex("17FD87F91D25A18ABD9BCD66B6D9F3C6BFC16778") +masterkey_entry = "{ed93694f-5a6d-46e2-b821-219f2c0ecd4d}:17FD87F91D25A18ABD9BCD66B6D9F3C6BFC16778" +masterkey_entries = """ +{ed93694f-5a6d-46e2-b821-219f2c0ecd4d}:17FD87F91D25A18ABD9BCD66B6D9F3C6BFC16778 +{12345678-1234-1234-1234-123456789012}:ABCDEF1234567890ABCDEF1234567890ABCDEF12 +""".strip() + +dpapi_system_secret_hex = "01000000dcfd03644f501805c189e15e9367b01415dea75a4e25d96d26879ded571f5d48a6887455d28f66f5" +dpapi_system_secret_bytes = bytes.fromhex(dpapi_system_secret_hex) +dpapi_system_machine_user_key_hex = dpapi_system_secret_hex[8:] # Skip version header +dpapi_system_machine_key_hex = "dcfd03644f501805c189e15e9367b01415dea75a" +dpapi_system_user_key_hex = "4e25d96d26879ded571f5d48a6887455d28f66f5" + +system_masterkey_hex = "020000000000000000000000640064003200360066003800310061002d0034006500640039002d0034003900660064002d0038006200340035002d00340032003700320033006400380061006500300030003600000000000000000006000000b00000000000000090000000000000001400000000000000000000000000000002000000bd2a4e8a1f66c1c29d972ad32534a801401f00000e80000010660000a6fddbe74e2b8975fe896c075bee61c5aee3112b35abc39f96d8229b3c3afe92f9c1db1242edcb84ff61bc3c70f955e73d99b6c1adbc1c8d258a2afd55d553c485eeae7515552ce7805af784b9c02e7cc3dc7dac56fa8fa59f61ebb7f4ad0378ade2f6e456db662ba2ba38441e542c071a2a60aa179835a90eb1f16fa2057808731937b2c4eb30db2b1cee02fff052290200000014c3adfc8d44ee78b8a652bf98e840e8401f00000e80000010660000587695a3307eb92ce3a55de58fb2ee6eee3d8561bd18bea34d44d01b3dba8dea0036cdd5d882c412ce293a3c5316fe6b7e7e2eae7ec11c4d46bbb6654b3f89c77a38fc4d340ea0be7733385a2577fb5b3acdf22fbafc19b9f697f3fa50ba5f1a1d3407c51c0ae14aa8f394f5122def910300000000000000000000000000000000000000" +system_masterkey_bytes = bytes.fromhex(system_masterkey_hex) +system_masterkey_guid = UUID("dd26f81a-4ed9-49fd-8b45-42723d8ae006") +system_masterkey_plaintext_sha1 = "b848ddc68f5250e5977bc52fd9671811ba3bc3b1" + +systemuser_masterkey_hex = "020000000000000000000000660062003100310039003000630031002d0031003200330064002d0034003500660038002d0039003500660034002d00330032006100650065003200380066006500320065006200000000000000000006000000b00000000000000090000000000000001400000000000000000000000000000002000000a4c506cfc4a0e9bcfeefcb8bfcc7f33e401f00000e80000010660000ab63a720b2e4c46bcbe3eb7c7259e7ad746d4e7f1566cdde0716e284a4b4a3f8851895a97db04c512963e11728d19db58873bc8dd8dae54937afbce49f9c723a5dbeba62c3a8b839410fa20a109a9de8857a6dd551052a201d9885365060323cd1d168715de699071e25e9f5c1ec11adf97160ef475d43ff0042c3f93ca47ff7756f335ec733acfc1b522afbeae07ac802000000732740ced42b4ebb05f8273b2f55f317401f00000e800000106600009451eb3cb464cd77ba53c19c792793ce19b7e7e9f1bc6512396b9f3325753741d4076b820e591b4a60418d1b6c6e15b6f30be25026b0ceceb9957e6b46ff5eb173c6642a83dd7bae6a7e5db7332ecbe1c13b0b88cf268ec479a351c0c37c8d425d61f61907b0e9658c832e397dfd67640300000000000000000000000000000000000000" +systemuser_masterkey_bytes = bytes.fromhex(systemuser_masterkey_hex) +systemuser_masterkey_guid = UUID("fb1190c1-123d-45f8-95f4-32aee28fe2eb") +systemuser_masterkey_plaintext_sha1 = "8a6f191d551750fa51324a6b8f3afc7086658888" + + +class TestPassword: + """Test Password model.""" + + def test_valid_password(self): + """Test creating valid password.""" + password = Password(value="secret123") + assert password.value == "secret123" + + def test_empty_password_raises_error(self): + """Test empty password raises validation error.""" + with pytest.raises(ValueError, match="Password value cannot be empty"): + Password(value="") + + def test_none_password_raises_error(self): + """Test None password raises validation error.""" + with pytest.raises(ValueError): + Password(value=None) # type: ignore + + +class TestNtlmHash: + """Test NtlmHash model.""" + + def test_valid_ntlm_hash(self): + """Test creating valid NTLM hash.""" + hash_bytes = b"a" * 16 # 16 bytes + ntlm_hash = NtlmHash(value=hash_bytes) + assert ntlm_hash.value == hash_bytes + + def test_invalid_length_raises_error(self): + """Test invalid length raises validation error.""" + with pytest.raises(ValueError, match="NTLM hash must be exactly 16 bytes"): + NtlmHash(value=b"a" * 15) + + with pytest.raises(ValueError, match="NTLM hash must be exactly 16 bytes"): + NtlmHash(value=b"a" * 17) + + def test_empty_hash_raises_error(self): + """Test empty hash raises validation error.""" + with pytest.raises(ValueError, match="NTLM hash value cannot be empty"): + NtlmHash(value=b"") + + def test_from_hexstring_valid(self): + """Test creating NTLM hash from valid hex string.""" + hex_string = "aabbccddeeff00112233445566778899" + ntlm_hash = NtlmHash.from_hexstring(hex_string) + expected_bytes = bytes.fromhex(hex_string) + assert ntlm_hash.value == expected_bytes + + def test_from_hexstring_invalid(self): + """Test creating NTLM hash from invalid hex string.""" + with pytest.raises(ValueError, match="Invalid hex string"): + NtlmHash.from_hexstring("invalid_hex") + + with pytest.raises(ValueError, match="Invalid hex string"): + NtlmHash.from_hexstring("aabbcc") # Wrong length + + +class TestSha1Hash: + """Test Sha1Hash model.""" + + def test_valid_sha1_hash(self): + """Test creating valid SHA1 hash.""" + hash_bytes = b"a" * 20 # 20 bytes + sha1_hash = Sha1Hash(value=hash_bytes) + assert sha1_hash.value == hash_bytes + + def test_invalid_length_raises_error(self): + """Test invalid length raises validation error.""" + with pytest.raises(ValueError, match="SHA1 hash must be exactly 20 bytes"): + Sha1Hash(value=b"a" * 19) + + with pytest.raises(ValueError, match="SHA1 hash must be exactly 20 bytes"): + Sha1Hash(value=b"a" * 21) + + def test_empty_hash_raises_error(self): + """Test empty hash raises validation error.""" + with pytest.raises(ValueError, match="SHA1 hash value cannot be empty"): + Sha1Hash(value=b"") + + def test_from_hex_valid(self): + """Test creating SHA1 hash from valid hex string.""" + hex_string = "aabbccddeeff00112233445566778899aabbccdd" + sha1_hash = Sha1Hash.from_hex(hex_string) + expected_bytes = bytes.fromhex(hex_string) + assert sha1_hash.value == expected_bytes + + def test_from_hex_invalid(self): + """Test creating SHA1 hash from invalid hex string.""" + with pytest.raises(ValueError, match="Invalid hex string"): + Sha1Hash.from_hex("invalid_hex") + + with pytest.raises(ValueError, match="Invalid hex string"): + Sha1Hash.from_hex("aabbcc") # Wrong length + + +class TestPbkdf2Hash: + """Test Pbkdf2Hash model.""" + + def test_valid_pbkdf2_hash(self): + """Test creating valid PBKDF2 hash.""" + hash_bytes = b"a" * 16 # 16 bytes + pbkdf2_hash = Pbkdf2Hash(value=hash_bytes) + assert pbkdf2_hash.value == hash_bytes + + def test_invalid_length_raises_error(self): + """Test invalid length raises validation error.""" + with pytest.raises(ValueError, match="PBKDF2 hash must be exactly 16 bytes"): + Pbkdf2Hash(value=b"a" * 15) + + with pytest.raises(ValueError, match="PBKDF2 hash must be exactly 16 bytes"): + Pbkdf2Hash(value=b"a" * 17) + + def test_empty_hash_raises_error(self): + """Test empty hash raises validation error.""" + with pytest.raises(ValueError, match="PBKDF2 hash value cannot be empty"): + Pbkdf2Hash(value=b"") + + def test_from_hex_valid(self): + """Test creating PBKDF2 hash from valid hex string.""" + hex_string = "aabbccddeeff00112233445566778899" + pbkdf2_hash = Pbkdf2Hash.from_hex(hex_string) + expected_bytes = bytes.fromhex(hex_string) + assert pbkdf2_hash.value == expected_bytes + + def test_from_hex_invalid(self): + """Test creating PBKDF2 hash from invalid hex string.""" + with pytest.raises(ValueError, match="Invalid hex string"): + Pbkdf2Hash.from_hex("invalid_hex") + + with pytest.raises(ValueError, match="Invalid hex string"): + Pbkdf2Hash.from_hex("aabbcc") # Wrong length + + +class TestCredKeyHashType: + def test_enum_values(self): + assert CredKeyHashType.MD4.value == "md4" + assert CredKeyHashType.NTLM.value == "md4" + assert CredKeyHashType.SHA1.value == "sha1" + assert CredKeyHashType.PBKDF2.value == "pbkdf2" + assert CredKeyHashType.SECURE_CRED_KEY.value == "pbkdf2" + + def test_ntlm_md4_alias(self): + assert CredKeyHashType.NTLM == CredKeyHashType.MD4 + + +class TestCredKey: + def test_init_with_ntlm_hash(self): + """Test initialization with NTLM hash.""" + key_bytes = b"a" * 16 + ntlm_hash = NtlmHash(value=key_bytes) + cred_key = CredKey(key=ntlm_hash) + + assert isinstance(cred_key.key, NtlmHash) + assert cred_key.key.value == key_bytes + assert cred_key.owf == CredKeyHashType.NTLM + + def test_init_with_pbkdf2_hash(self): + """Test initialization with PBKDF2 hash.""" + key_bytes = b"a" * 16 + pbkdf2_hash = Pbkdf2Hash(value=key_bytes) + cred_key = CredKey(key=pbkdf2_hash) + + assert isinstance(cred_key.key, Pbkdf2Hash) + assert cred_key.key.value == key_bytes + assert cred_key.owf == CredKeyHashType.PBKDF2 + + def test_init_with_sha1_hash(self): + """Test initialization with SHA1 hash.""" + key_bytes = b"a" * 20 + sha1_hash = Sha1Hash(value=key_bytes) + cred_key = CredKey(key=sha1_hash) + + assert isinstance(cred_key.key, Sha1Hash) + assert cred_key.key.value == key_bytes + assert cred_key.owf == CredKeyHashType.SHA1 + + def test_init_with_direct_parameters(self): + """Test initialization with direct parameters.""" + ntlm_hash = NtlmHash(value=b"a" * 16) + cred_key = CredKey(key=ntlm_hash) + + assert cred_key.key == ntlm_hash + assert cred_key.owf == CredKeyHashType.NTLM + + def test_from_password_ntlm(self): + """Test creating CredKey from password with NTLM hash.""" + password = "Qwerty12345" + + cred_key = CredKey.from_password(password, CredKeyHashType.NTLM) + + assert isinstance(cred_key.key, NtlmHash) + assert cred_key.owf == CredKeyHashType.NTLM + + expected_hash = "abd9ffb762c86b26ef4ce5c81b0dd37f" + assert cred_key.key.value.hex() == expected_hash + + def test_from_password_sha1(self): + """Test creating CredKey from password with SHA1 hash.""" + password = "Qwerty12345" + cred_key = CredKey.from_password(password, CredKeyHashType.SHA1) + + assert isinstance(cred_key.key, Sha1Hash) + assert cred_key.owf == CredKeyHashType.SHA1 + + expected_hash = "15056cbc481efd37bba0e97e9c28493a40cf8745" + assert cred_key.key.value.hex() == expected_hash + + def test_from_password_pbkdf2(self): + """Test creating CredKey from password with PBKDF2 hash.""" + password = "Qwerty12345" + + with pytest.raises(ValueError, match="user_sid parameter is required"): + CredKey.from_password(password, CredKeyHashType.PBKDF2) + + cred_key = CredKey.from_password(password, CredKeyHashType.PBKDF2, user_sid) + + assert isinstance(cred_key.key, Pbkdf2Hash) + assert cred_key.owf == CredKeyHashType.PBKDF2 + + expected_hash = "775ec403415b49002386ea8e477346cd" + assert cred_key.key.value.hex() == expected_hash + + def test_from_password_md4_explicit(self): + """Test creating CredKey from password with explicit MD4 hash type.""" + password = "Qwerty12345" + + cred_key = CredKey.from_password(password, CredKeyHashType.MD4) + + assert isinstance(cred_key.key, NtlmHash) + assert cred_key.owf == CredKeyHashType.MD4 + + expected_hash = "abd9ffb762c86b26ef4ce5c81b0dd37f" + assert cred_key.key.value.hex() == expected_hash + + def test_from_password_secure_cred_key(self): + """Test creating CredKey from password with SECURE_CRED_KEY alias.""" + password = "Qwerty12345" + + cred_key = CredKey.from_password(password, CredKeyHashType.SECURE_CRED_KEY, user_sid) + + assert isinstance(cred_key.key, Pbkdf2Hash) + assert cred_key.owf == CredKeyHashType.PBKDF2 + + expected_hash = "775ec403415b49002386ea8e477346cd" + assert cred_key.key.value.hex() == expected_hash + + def test_from_password_unsupported_type(self): + """Test creating CredKey from password with unsupported type.""" + password = "TestPassword123" + + with pytest.raises(ValueError, match="Unsupported hash type"): + # Using string instead of enum to test error handling + CredKey.from_password(password, "unsupported") # type: ignore + + def test_from_ntlm_ntlm(self): + """Test creating CredKey from NTLM hash (explicit NTLM type).""" + ntlm_bytes = b"a" * 16 + cred_key = CredKey.from_ntlm(ntlm_bytes, CredKeyHashType.NTLM) + + assert isinstance(cred_key.key, NtlmHash) + assert cred_key.key.value == ntlm_bytes + assert cred_key.owf == CredKeyHashType.NTLM + + def test_from_ntlm_md4(self): + """Test creating CredKey from NTLM hash with explicit MD4 type.""" + ntlm_bytes = b"a" * 16 + cred_key = CredKey.from_ntlm(ntlm_bytes, CredKeyHashType.MD4) + + assert isinstance(cred_key.key, NtlmHash) + assert cred_key.key.value == ntlm_bytes + assert cred_key.owf == CredKeyHashType.MD4 + + def test_from_ntlm_pbkdf2(self): + """Test creating CredKey from NTLM hash with PBKDF2 derivation.""" + ntlm_hash = "abd9ffb762c86b26ef4ce5c81b0dd37f" # Qwerty12345 + ntlm_bytes = bytes.fromhex(ntlm_hash) + + with pytest.raises(ValueError, match="user_sid parameter is required"): + CredKey.from_ntlm(ntlm_bytes, CredKeyHashType.PBKDF2) + + user_sid = "S-1-5-21-3821320868-1508310791-3575676346-1103" + cred_key = CredKey.from_ntlm(ntlm_bytes, CredKeyHashType.PBKDF2, user_sid=user_sid) + + assert isinstance(cred_key.key, Pbkdf2Hash) + assert cred_key.owf == CredKeyHashType.PBKDF2 + + expected_hash = "775ec403415b49002386ea8e477346cd" + assert cred_key.key.value.hex() == expected_hash + + def test_from_ntlm_invalid_derivation(self): + """Test creating CredKey from NTLM hash with invalid derivation.""" + ntlm_bytes = b"a" * 16 + + with pytest.raises(ValueError, match="Cannot derive"): + CredKey.from_ntlm(ntlm_bytes, CredKeyHashType.SHA1) + + def test_from_sha1(self): + """Test creating CredKey from SHA1 hash.""" + sha1_bytes = b"a" * 20 + cred_key = CredKey.from_sha1(sha1_bytes) + + assert isinstance(cred_key.key, Sha1Hash) + assert cred_key.key.value == sha1_bytes + assert cred_key.owf == CredKeyHashType.SHA1 + + def test_from_pbkdf2(self): + """Test creating CredKey from PBKDF2 hash.""" + pbkdf2_bytes = bytes.fromhex(credkey_pbkdf2_hash) + cred_key = CredKey.from_pbkdf2(pbkdf2_bytes) + + assert isinstance(cred_key.key, Pbkdf2Hash) + assert cred_key.key.value == pbkdf2_bytes + assert cred_key.owf == CredKeyHashType.PBKDF2 + + +class TestMasterKeyEncryptionKey: + """Test MasterKeyEncryptionKey model.""" + + def test_from_cred_key_ntlm(self): + """Test creating MasterKeyEncryptionKey from CredKey with NTLM.""" + global ntlm_bytes, user_sid + + ntlm_hash = NtlmHash(value=ntlm_bytes) + cred_key = CredKey(key=ntlm_hash) + + mk_key = MasterKeyEncryptionKey.from_cred_key(cred_key, user_sid) + + assert isinstance(mk_key.key, Sha1Hash) + assert len(mk_key.key.value) == 20 # SHA1 digest length + + expected_sha1 = "44857a618c3d58f37823f016c2ff10a0d7b93ee7" + assert mk_key.key.value.hex() == expected_sha1 + + def test_from_cred_key_sha1(self): + """Test creating MasterKeyEncryptionKey from CredKey with SHA1.""" + global user_sid + + sha1_hash = Sha1Hash(value=bytes.fromhex(credkey_sha1_hash)) + cred_key = CredKey(key=sha1_hash) + + mk_key = MasterKeyEncryptionKey.from_cred_key(cred_key, user_sid) + + assert isinstance(mk_key.key, Sha1Hash) + assert len(mk_key.key.value) == 20 # SHA1 digest length + assert mk_key.key.value.hex() == "0a66b6fa245e40118aab0c9d71774bf540045f9c" + + def test_from_cred_key_pbkdf2(self): + """Test creating MasterKeyEncryptionKey from CredKey with PBKDF2.""" + global user_sid, credkey_pbkdf2_hash + + pbkdf2_hash = Pbkdf2Hash(value=bytes.fromhex(credkey_pbkdf2_hash)) + cred_key = CredKey(key=pbkdf2_hash) + + mk_key = MasterKeyEncryptionKey.from_cred_key(cred_key, user_sid) + + assert isinstance(mk_key.key, Sha1Hash) + assert len(mk_key.key.value) == 20 # SHA1 digest length + assert mk_key.key.value.hex() == "d3205d40d3df002fba1936ce075c0b2805fab06d" + + def test_from_dpapi_system_cred(self): + """Test creating MasterKeyEncryptionKey from DPAPI_SYSTEM credential.""" + dpapi_system_key = b"a" * 20 # 20 bytes for SHA1 + + mk_key = MasterKeyEncryptionKey.from_dpapi_system_cred(dpapi_system_key) + + assert isinstance(mk_key.key, Sha1Hash) + assert mk_key.key.value == dpapi_system_key + + def test_derive_mk_key_with_different_digests(self): + """Test _derive_mk_key with different digest algorithms.""" + pwdhash = b"a" * 16 + user_sid = "S-1-5-21-1234567890-1234567890-1234567890-1001" + + # Test with sha256 + sha256_key = MasterKeyEncryptionKey._derive_mk_key(pwdhash, user_sid, digest="sha256") + assert len(sha256_key) == 32 # SHA256 digest length + + # Test with md4 + md4_key = MasterKeyEncryptionKey._derive_mk_key(pwdhash, user_sid, digest="md4") + assert len(md4_key) == 16 # MD4 digest length + + # Test with sha1 (default) + sha1_key = MasterKeyEncryptionKey._derive_mk_key(pwdhash, user_sid, digest="sha1") + assert len(sha1_key) == 20 # SHA1 digest length + + def test_derive_mk_key_with_unsupported_digest(self): + """Test _derive_mk_key with unsupported digest algorithm.""" + pwdhash = b"a" * 16 + user_sid = "S-1-5-21-1234567890-1234567890-1234567890-1001" + + with pytest.raises(ValueError, match="Unsupported digest algorithm"): + MasterKeyEncryptionKey._derive_mk_key(pwdhash, user_sid, digest="unsupported") + + def test_from_cred_key_type_mismatch_ntlm(self): + """Test type mismatch validation for NTLM/MD4 - wrong hash type.""" + # Create a CredKey with SHA1 hash but mock owf to return NTLM + sha1_hash = Sha1Hash(value=b"a" * 20) + cred_key = CredKey(key=sha1_hash) + + # Mock the owf property to return NTLM while key is actually SHA1 + with patch.object(type(cred_key), "owf", new_callable=PropertyMock) as mock_owf: + mock_owf.return_value = CredKeyHashType.NTLM + with pytest.raises(ValueError, match="Expected NtlmHash for MD4/NTLM key type"): + MasterKeyEncryptionKey.from_cred_key(cred_key, user_sid) + + def test_from_cred_key_type_mismatch_sha1(self): + """Test type mismatch validation for SHA1 - wrong hash type.""" + # Create a CredKey with NTLM hash but mock owf to return SHA1 + ntlm_hash = NtlmHash(value=ntlm_bytes) + cred_key = CredKey(key=ntlm_hash) + + # Mock the owf property to return SHA1 while key is actually NTLM + with patch.object(type(cred_key), "owf", new_callable=PropertyMock) as mock_owf: + mock_owf.return_value = CredKeyHashType.SHA1 + with pytest.raises(ValueError, match="Expected Sha1Hash for SHA1 key type"): + MasterKeyEncryptionKey.from_cred_key(cred_key, user_sid) + + def test_from_cred_key_type_mismatch_pbkdf2(self): + """Test type mismatch validation for PBKDF2 - wrong hash type.""" + # Create a CredKey with NTLM hash but mock owf to return PBKDF2 + ntlm_hash = NtlmHash(value=ntlm_bytes) + cred_key = CredKey(key=ntlm_hash) + + # Mock the owf property to return PBKDF2 while key is actually NTLM + with patch.object(type(cred_key), "owf", new_callable=PropertyMock) as mock_owf: + mock_owf.return_value = CredKeyHashType.PBKDF2 + with pytest.raises(ValueError, match="Expected Pbkdf2Hash for PBKDF2 key type"): + MasterKeyEncryptionKey.from_cred_key(cred_key, user_sid) + + +class TestHelperFunctions: + """Test crypto helper functions.""" + + def test_derive_secure_cred_key(self): + """Test _derive_secure_cred_key function directly.""" + ntlm_hash = bytes.fromhex("abd9ffb762c86b26ef4ce5c81b0dd37f") # Qwerty12345 + user_sid_bytes = "S-1-5-21-3821320868-1508310791-3575676346-1103".encode("utf-16le") + + derived_key = _derive_secure_cred_key(ntlm_hash, user_sid_bytes) + + assert len(derived_key) == 16 # PBKDF2 returns 16 bytes + assert derived_key == bytes.fromhex("775ec403415b49002386ea8e477346cd") + + +class TestDomainBackupKey: + """Test DomainBackupKey dataclass.""" + + def test_create_domain_backup_key(self, get_file_path): + """Test creating DomainBackupKey.""" + # Load valid backup key data from fixtures + backupkey_file = get_file_path("backupkey.json") + with open(backupkey_file) as f: + backupkey_data = json.load(f) + + guid = UUID(backupkey_data["backup_key_guid"]) + key_data = base64.b64decode(backupkey_data["key"]) + domain_controller = backupkey_data["dc"] + + backup_key = DomainBackupKey(guid=guid, key_data=key_data, domain_controller=domain_controller) + + assert backup_key.guid == guid + assert backup_key.key_data == key_data + assert backup_key.domain_controller == domain_controller + + def test_create_domain_backup_key_no_dc(self, get_file_path): + """Test creating DomainBackupKey without domain controller.""" + # Load valid backup key data from fixtures + backupkey_file = get_file_path("backupkey.json") + with open(backupkey_file) as f: + backupkey_data = json.load(f) + + guid = UUID(backupkey_data["backup_key_guid"]) + key_data = base64.b64decode(backupkey_data["key"]) + + backup_key = DomainBackupKey(guid=guid, key_data=key_data) + + assert backup_key.guid == guid + assert backup_key.key_data == key_data + assert backup_key.domain_controller is None + + def test_decrypt_masterkey_file_with_backup_key(self, get_file_path): + """Test decrypting a domain masterkey file using backup key.""" + # Load the backup key from fixtures + backupkey_file = get_file_path("backupkey.json") + with open(backupkey_file) as f: + backupkey_data = json.load(f) + + # Create DomainBackupKey from the fixture data + backup_key = DomainBackupKey( + guid=UUID(backupkey_data["backup_key_guid"]), + key_data=base64.b64decode(backupkey_data["key"]), + domain_controller=backupkey_data["dc"], + ) + + # Load the domain masterkey file + masterkey_file = MasterKeyFile.from_file(get_file_path("masterkey_domain.bin")) + + # Decrypt the masterkey + decrypted_masterkey = masterkey_file.decrypt(backup_key) + + # Verify decryption succeeded + assert decrypted_masterkey is not None + assert decrypted_masterkey.is_decrypted + assert decrypted_masterkey.guid == masterkey_file.masterkey_guid + assert decrypted_masterkey.plaintext_key is not None + assert decrypted_masterkey.plaintext_key_sha1 is not None + assert decrypted_masterkey.backup_key_guid == backup_key.guid + assert len(decrypted_masterkey.plaintext_key_sha1) == 20 # SHA1 is 20 bytes + + masterkey_bytes = "36bd60cb9e7e52433169db00e93ed0a82d3c30c65d948bd8596fb32c267671020b02026b0ae03479dd18374adbdd7658f45cce6ed2a45319eff7a96c411c85f5" + masterkey_sha1_hash = "17fd87f91d25a18abd9bcd66b6d9f3c6bfc16778" + + assert decrypted_masterkey.plaintext_key.hex() == masterkey_bytes + assert decrypted_masterkey.plaintext_key_sha1.hex() == masterkey_sha1_hash + + @pytest.mark.asyncio + async def test_decrypt_masterkey_file_with_backup_key_oldformat(self, get_file_path): + """Test decrypting a domain masterkey file using backup key.""" + # Load the backup key from fixtures + backupkey_file = get_file_path("old_format/dpapi_domain_backupkey.json") + masterkey_file = MasterKeyFile.from_file(get_file_path("old_format/ab998260-e99d-4871-8f4b-d922b2848ce6")) + blob = Blob.from_file(get_file_path("old_format/dpapi_blob.bin")) + + with open(backupkey_file) as f: + backupkey_data = json.load(f) + + # Create DomainBackupKey from the fixture data + backup_key = DomainBackupKey( + guid=UUID(backupkey_data["domain_backupkey_guid"]), + key_data=base64.b64decode(backupkey_data["domain_backupkey_b64"]), + domain_controller=backupkey_data["domain_controller"], + ) + + # Load the domain masterkey file + + # Decrypt the masterkey + decrypted_masterkey = masterkey_file.decrypt(backup_key) + + # Verify decryption succeeded + assert decrypted_masterkey is not None + assert decrypted_masterkey.is_decrypted + assert decrypted_masterkey.guid == masterkey_file.masterkey_guid + assert decrypted_masterkey.plaintext_key is not None + assert decrypted_masterkey.plaintext_key_sha1 is not None + assert decrypted_masterkey.backup_key_guid == backup_key.guid + assert len(decrypted_masterkey.plaintext_key_sha1) == 20 # SHA1 is 20 bytes + + assert ( + masterkey_file is not None + and masterkey_file.master_key is not None + and masterkey_file.domain_backup_key is not None + ) + + manager = DpapiManager(storage_backend="memory", auto_decrypt=True) + await manager.upsert_domain_backup_key(backup_key) + + await manager.upsert_masterkey( + MasterKey( + guid=decrypted_masterkey.guid, + masterkey_type=MasterKeyType.UNKNOWN, + plaintext_key=decrypted_masterkey.plaintext_key, + plaintext_key_sha1=decrypted_masterkey.plaintext_key_sha1, + ) + ) + + assert blob.masterkey_guid == masterkey_file.masterkey_guid + + decrypted = await manager.decrypt_blob(blob) + assert decrypted == b"This is a test." # Adjusted expected value + + def test_decrypt_masterkey_file_no_domain_key(self): + """Test decrypting a masterkey file without domain backup key raises exception.""" + from uuid import uuid4 + + # Create a backup key with fake data (bypassing validation using model_construct) + backup_key = DomainBackupKey.model_construct(guid=uuid4(), key_data=b"fake_key_data") + + # Load the local masterkey file (no domain backup key) + masterkey_file = MasterKeyFile.from_file("tests/fixtures/masterkey_local.bin") + + # Should raise MasterKeyDecryptionError since no domain backup key in file + with pytest.raises(ValueError, match="contains no domain backup key"): + masterkey_file.decrypt(backup_key) + + def test_domain_backup_key_validation(self): + """Test that DomainBackupKey validates key_data format.""" + from uuid import uuid4 + + guid = uuid4() + + # Test with invalid key data (too short) + with pytest.raises(ValueError, match="key_data too short"): + DomainBackupKey(guid=guid, key_data=b"too_short") + + # Test with empty key data + with pytest.raises(ValueError, match="key_data too short"): + DomainBackupKey(guid=guid, key_data=b"") + + # Test with non-bytes key data should be caught by Pydantic + with pytest.raises(ValueError): + DomainBackupKey(guid=guid, key_data="not_bytes") # type: ignore + + def test_decrypt_masterkey_file_unexpected_key_length(self, get_file_path): + """Test decrypting masterkey with unexpected decrypted key length.""" + from unittest.mock import MagicMock, patch + + # Load valid backup key and masterkey file + backupkey_file = get_file_path("backupkey.json") + with open(backupkey_file) as f: + backupkey_data = json.load(f) + + backup_key = DomainBackupKey( + guid=UUID(backupkey_data["backup_key_guid"]), + key_data=base64.b64decode(backupkey_data["key"]), + domain_controller=backupkey_data["dc"], + ) + + masterkey_file = MasterKeyFile.from_file(get_file_path("masterkey_domain.bin")) + + # Mock the cipher.decrypt to return unexpected length + with patch("nemesis_dpapi.core.PKCS1_v1_5") as mock_pkcs: + mock_cipher = MagicMock() + # Return decrypted key with unexpected length (not 104 or 128) + mock_cipher.decrypt.return_value = b"X" * 100 # 100 bytes (unexpected) + mock_pkcs.new.return_value = mock_cipher + + with pytest.raises(Exception, match="Unexpected decrypted key length"): + masterkey_file.decrypt(backup_key) + + def test_parse_backup_key(self): + backup_key_b64 = "HvG1sAAAAAABAAAAAAAAAAAAAACUBAAABwIAAACkAABSU0EyAAgAAAEAAQBBif0kcBLSPpOCv+azdnH4mDEDV5UDHl6AVhDmI8AcZPBmYm0/ftO5xXFmsqrQvt9iZsWQP7nG1nzMjGdRq1F9jFKjIqVTjdzgPwEBXxQln6MCqaHPjx6K8/J6He06Y/mf4MueNDv+kWMEsnyayM3Se3RqYia8dR0PAmzJgntKIqbDG8S5c3WbfF3QYhGldkBPgAPuESB0EPg3TcAvXVP88/6oe9BIV8TzYH6CRFd7PaBdq+0YTRejKWALjPrRoNpEW0Uyosgeu4txQNjf57UbOKU4fJ6VoJYRDqvvw1+O4Lui4R6g+gwEByr2W09rcHg2LCEJDUvQSNvRf/FBoq3Hl3Ub7vWs5no65gerQAA0odxPbPKG1MKaexd9bsYynJMwm6TFn/0ram1AxBZrk2Vd9etnefYr4+6ztGaoY9cf5tUG6nK3TUQdLGs6ohmb4JGsRIO6VpLvYylCD+5hpwaDPiqM2il8nHaMLR+QDrRtu2OHzgzgAX0uKP+jq5CNnNbnwvbkyKf75TzcwXzC28d70wl78ud9GkGYmbUY2b/nvkquGsZbnBEV6hIqpZOyTr6SfCmbx9neH5dPhzOm3boXSRiZI/XXCjCk6xvoyyfLiDeatbnEOgGC/+vd5qYVLjR154CCwbQoHfORsTN5q6XlyfBpaYp+qYzl8x/n09Ev7lGLM8FLgLqvOIwwpmzlKCFxFU8JOxOubp4x6/Fgy8nx7m5wtD2uam4WKiKlX26IxCQNAzfThXsjuTi2kaE6vvIk+TM9OkD/ZdzIEIZ62/GVdy1Ns3GMQbk1fBq2+idtQiAweg4zubsoGr2kk0DbbSKrOf9nmxMK/jNP+SKlPrKxExtYlkZbJc//P0IuQ90C4uTZELAADBWWtZrDYozmA3sMnIUQRv6CZMpRU70FKkKqnGtjXtQgX3R6OhIqj5tyCkxu4sFKg4nLzzT4GrMLIqHjtSQ7TuCV4C0yUlJ8PGMKMXBTD0k2IoYbuzKkPfoc6TFaAKf6F0qgH7ZfJgHOD9bWhTP8U8UG3Bo3cHIZXWbg25VE0YQpFxxoNRIHTfLbz5W7opcY5I9ljfQ61+5qkZNcMGl7AGJBU5EJzBQnoJv1DiN0HuoFZHDFoKiq/KV1YkTLDf3yTqCNPWhuwOXOYwDjzM9QyC6tH76jv7fegIu5v2RurhXqLp4IxlnVEYL3AWGexfArKxhmuymUsggEz/y9pOGZdKrmbjixg1vLR3nPAUYnYCmMXc6jAa8HijoTQH0h9d7swEbvPMCH+P1eOUdvW9QxY6GZL3329jUQoIr2zYrUDh0X92q9ZsGkGaNW1P6iHYrZBIwtebNegjcSMboLgeeYxHCPjycfl3shLEu3vV7Wi6VPa6k77ezYYxeelqc6PMjcy2nhHslEoISf3KNH3lpk0/fc0xrTDpZcPpYXr5sI0KZh5IyAwriiqvY9ksSz6tNNS41h0xnWAjtSBITDJnbHJNK3SvCH5gM3/zVrI0RvlLrQaiYMMGd5W2WPSoq0YMKp39ByP+mKMcAn2ic=" + backup_key_bytes = base64.b64decode(backup_key_b64, validate=True) + + from Cryptodome.Cipher import PKCS1_v1_5 + from impacket.dpapi import PRIVATE_KEY_BLOB, PVK_FILE_HDR, privatekeyblob_to_pkcs1 + + # Extract the private key from the backup key data + key = PRIVATE_KEY_BLOB(backup_key_bytes[len(PVK_FILE_HDR()) :]) + private = privatekeyblob_to_pkcs1(key) + PKCS1_v1_5.new(private) + + +class TestDpapiSystemSecret: + """Tests for DpapiSystemSecret class.""" + + def test_from_bytes_valid_40_bytes(self): + """Test creating DpapiSystemSecret from valid 40-byte data.""" + # Create test data: 20 bytes user key + 20 bytes machine key + user_key_data = b"user_key_12345678901" # 20 bytes + machine_key_data = b"mach_key_12345678901" # 20 bytes + dpapi_system_data = machine_key_data + user_key_data # 40 bytes total, machine key first + + secret = DpapiSystemCredential.from_bytes(dpapi_system_data) + + assert secret.user_key == user_key_data + assert secret.machine_key == machine_key_data + + def test_from_bytes_with_hex_string(self): + """Test creating DpapiSystemSecret from hex string.""" + # Use the actual test data hex strings + hex_string = dpapi_system_machine_user_key_hex # 40 bytes as hex (80 chars) + + secret = DpapiSystemCredential.from_bytes(hex_string) + + assert secret.user_key == bytes.fromhex(dpapi_system_user_key_hex) + assert secret.machine_key == bytes.fromhex(dpapi_system_machine_key_hex) + + def test_from_bytes_with_invalid_hex_string(self): + """Test from_bytes with invalid hex string raises error.""" + invalid_hex = "not_valid_hex_string" + + with pytest.raises(ValueError, match="Invalid hex string"): + DpapiSystemCredential.from_bytes(invalid_hex) + + def test_from_bytes_with_hex_string_wrong_length(self): + """Test from_bytes with hex string of wrong length.""" + # 60 hex chars = 30 bytes (not 40) + wrong_length_hex = "a" * 60 + + with pytest.raises(ValueError, match="DPAPI_SYSTEM must be exactly 40 bytes, got 30"): + DpapiSystemCredential.from_bytes(wrong_length_hex) + + def test_direct_creation_with_bytes(self): + """Test creating DpapiSystemSecret directly with bytes.""" + # Create test data: 20 bytes user key + 20 bytes machine key + user_key_data = b"user_key_12345678901" # 20 bytes + machine_key_data = b"mach_key_12345678901" # 20 bytes + + # Test by creating instance directly with bytes + secret = DpapiSystemCredential( + user_key=user_key_data, + machine_key=machine_key_data, + ) + + assert secret.user_key == user_key_data + assert secret.machine_key == machine_key_data + + def test_from_bytes_invalid_length_short(self): + """Test from_bytes with data too short raises ValueError.""" + short_data = b"too_short" # Only 9 bytes + + with pytest.raises(ValueError, match="DPAPI_SYSTEM must be exactly 40 bytes, got 9"): + DpapiSystemCredential.from_bytes(short_data) + + def test_from_bytes_invalid_length_long(self): + """Test from_bytes with data too long raises ValueError.""" + long_data = b"a" * 50 # 50 bytes + + with pytest.raises(ValueError, match="DPAPI_SYSTEM must be exactly 40 bytes, got 50"): + DpapiSystemCredential.from_bytes(long_data) + + def test_from_lsa_secret_valid_structure(self): + """Test creating DpapiSystemSecret from valid LSA secret structure.""" + + secret = DpapiSystemCredential.from_lsa_secret(dpapi_system_secret_hex) + + assert secret.user_key == bytes.fromhex(dpapi_system_user_key_hex) + assert secret.machine_key == bytes.fromhex(dpapi_system_machine_key_hex) + + def test_from_lsa_secret_and_direct_creation(self): + """Test creating DpapiSystemSecret from LSA secret and direct instantiation.""" + import struct + + # Create valid LSA secret structure + version = 1 + machine_key = b"mach_key_12345678901" # 20 bytes + user_key = b"user_key_12345678901" # 20 bytes + + lsa_secret_data = struct.pack(" value (allowed) + field3=None, # NULL -> NULL (allowed) + ) + + conflicts = check_write_once_conflicts(existing, new, ["field1", "field2", "field3"]) + + assert conflicts == [] + + def test_empty_bytes_distinct_from_none(self): + """Should treat empty bytes as distinct from None.""" + existing = self.MockRecord(field1=b"") + new = self.MockRecord(field1=None) + + conflicts = check_write_once_conflicts(existing, new, ["field1"]) + + assert conflicts == ["field1"] + + def test_empty_bytes_equality(self): + """Should treat empty bytes as equal to empty bytes.""" + existing = self.MockRecord(field1=b"") + new = self.MockRecord(field1=b"") + + conflicts = check_write_once_conflicts(existing, new, ["field1"]) + + assert conflicts == [] + + def test_string_fields(self): + """Should work with string fields too.""" + existing = self.MockRecord(name="Alice", email=None) + new = self.MockRecord(name="Bob", email="test@example.com") + + conflicts = check_write_once_conflicts(existing, new, ["name", "email"]) + + assert conflicts == ["name"] + + def test_uuid_fields(self): + """Should work with UUID fields.""" + from uuid import UUID + + guid1 = UUID("12345678-1234-5678-1234-567812345678") + guid2 = UUID("87654321-4321-8765-4321-876543218765") + + existing = self.MockRecord(backup_key_guid=guid1) + new = self.MockRecord(backup_key_guid=guid2) + + conflicts = check_write_once_conflicts(existing, new, ["backup_key_guid"]) + + assert conflicts == ["backup_key_guid"] diff --git a/libs/nemesis_dpapi/tests/test_write_once.py b/libs/nemesis_dpapi/tests/test_write_once.py new file mode 100644 index 0000000..a5c2d63 --- /dev/null +++ b/libs/nemesis_dpapi/tests/test_write_once.py @@ -0,0 +1,485 @@ +"""Tests for write-once semantics in upsert operations.""" + +from uuid import uuid4 + +import pytest +from nemesis_dpapi.core import MasterKey, MasterKeyType +from nemesis_dpapi.exceptions import WriteOnceViolationError +from nemesis_dpapi.keys import DomainBackupKey +from nemesis_dpapi.manager import DpapiManager + + +class TestMasterKeyWriteOnce: + """Test write-once semantics for masterkey upserts.""" + + @pytest.mark.asyncio + async def test_insert_new_masterkey(self): + """Should successfully insert a new masterkey.""" + async with DpapiManager(storage_backend="memory") as manager: + guid = uuid4() + masterkey = MasterKey( + guid=guid, + masterkey_type=MasterKeyType.USER, + encrypted_key_usercred=b"encrypted_data", + plaintext_key=b"plaintext_key_data", + ) + + await manager.upsert_masterkey(masterkey) + + # Verify it was inserted + result = await manager.get_masterkeys(guid=guid) + assert len(result) == 1 + assert result[0].guid == guid + + @pytest.mark.asyncio + async def test_idempotent_update_with_same_values(self): + """Should allow updating with identical values (idempotent).""" + async with DpapiManager(storage_backend="memory") as manager: + guid = uuid4() + masterkey = MasterKey( + guid=guid, + masterkey_type=MasterKeyType.USER, + encrypted_key_usercred=b"encrypted_data", + ) + + # First insert + await manager.upsert_masterkey(masterkey) + + # Second insert with same values should succeed + await manager.upsert_masterkey(masterkey) + + # Verify + result = await manager.get_masterkeys(guid=guid) + assert len(result) == 1 + + @pytest.mark.asyncio + async def test_reject_changing_encrypted_key_usercred(self): + """Should reject attempt to change encrypted_key_usercred.""" + async with DpapiManager(storage_backend="memory") as manager: + guid = uuid4() + + # First insert + masterkey1 = MasterKey( + guid=guid, + masterkey_type=MasterKeyType.USER, + encrypted_key_usercred=b"original_data", + ) + await manager.upsert_masterkey(masterkey1) + + # Try to change encrypted_key_usercred + masterkey2 = MasterKey( + guid=guid, + masterkey_type=MasterKeyType.USER, + encrypted_key_usercred=b"different_data", + ) + + with pytest.raises(WriteOnceViolationError) as exc_info: + await manager.upsert_masterkey(masterkey2) + + # Verify error details + assert exc_info.value.entity_type == "masterkey" + assert exc_info.value.entity_id == str(guid) + assert "encrypted_key_usercred" in exc_info.value.fields + + @pytest.mark.asyncio + async def test_reject_changing_plaintext_key(self): + """Should reject attempt to change plaintext_key.""" + async with DpapiManager(storage_backend="memory") as manager: + guid = uuid4() + + # First insert with plaintext + masterkey1 = MasterKey( + guid=guid, + masterkey_type=MasterKeyType.USER, + plaintext_key=b"original_plaintext", + ) + await manager.upsert_masterkey(masterkey1) + + # Try to change plaintext_key + masterkey2 = MasterKey( + guid=guid, + masterkey_type=MasterKeyType.USER, + plaintext_key=b"different_plaintext", + ) + + with pytest.raises(WriteOnceViolationError) as exc_info: + await manager.upsert_masterkey(masterkey2) + + assert "plaintext_key" in exc_info.value.fields + + @pytest.mark.asyncio + async def test_reject_changing_masterkey_type(self): + """Should reject attempt to change masterkey_type (strict enforcement).""" + async with DpapiManager(storage_backend="memory") as manager: + guid = uuid4() + + # First insert as UNKNOWN + masterkey1 = MasterKey( + guid=guid, + masterkey_type=MasterKeyType.UNKNOWN, + ) + await manager.upsert_masterkey(masterkey1) + + # Try to change to USER (even though it's a refinement, we're strict) + masterkey2 = MasterKey( + guid=guid, + masterkey_type=MasterKeyType.USER, + ) + + with pytest.raises(WriteOnceViolationError) as exc_info: + await manager.upsert_masterkey(masterkey2) + + assert "masterkey_type" in exc_info.value.fields + + @pytest.mark.asyncio + async def test_allow_filling_null_fields(self): + """Should allow writing to previously NULL fields.""" + async with DpapiManager(storage_backend="memory") as manager: + guid = uuid4() + + # First insert with only encrypted data + masterkey1 = MasterKey( + guid=guid, + masterkey_type=MasterKeyType.USER, + encrypted_key_usercred=b"encrypted_data", + ) + await manager.upsert_masterkey(masterkey1) + + # Add plaintext_key (previously NULL) + masterkey2 = MasterKey( + guid=guid, + masterkey_type=MasterKeyType.USER, + encrypted_key_usercred=b"encrypted_data", # Same + plaintext_key=b"now_decrypted", # New + ) + await manager.upsert_masterkey(masterkey2) + + # Verify both fields are set + result = await manager.get_masterkeys(guid=guid) + assert result[0].encrypted_key_usercred == b"encrypted_data" + assert result[0].plaintext_key == b"now_decrypted" + + @pytest.mark.asyncio + async def test_reject_clearing_non_null_field(self): + """Should reject attempt to set non-NULL field to NULL.""" + async with DpapiManager(storage_backend="memory") as manager: + guid = uuid4() + + # First insert with plaintext + masterkey1 = MasterKey( + guid=guid, + masterkey_type=MasterKeyType.USER, + plaintext_key=b"plaintext_data", + ) + await manager.upsert_masterkey(masterkey1) + + # Try to clear plaintext_key + masterkey2 = MasterKey( + guid=guid, + masterkey_type=MasterKeyType.USER, + plaintext_key=None, # Trying to clear + ) + + with pytest.raises(WriteOnceViolationError) as exc_info: + await manager.upsert_masterkey(masterkey2) + + assert "plaintext_key" in exc_info.value.fields + + @pytest.mark.asyncio + async def test_sha1_auto_calculation(self): + """Should auto-calculate SHA1 when plaintext_key is provided.""" + async with DpapiManager(storage_backend="memory") as manager: + from Crypto.Hash import SHA1 + + guid = uuid4() + plaintext = b"my_plaintext_key" + expected_sha1 = SHA1.new(plaintext).digest() + + masterkey = MasterKey( + guid=guid, + masterkey_type=MasterKeyType.USER, + plaintext_key=plaintext, + # Note: plaintext_key_sha1 not provided + ) + await manager.upsert_masterkey(masterkey) + + # Verify SHA1 was calculated + result = await manager.get_masterkeys(guid=guid) + assert result[0].plaintext_key_sha1 == expected_sha1 + + @pytest.mark.asyncio + async def test_sha1_verification_accepts_correct_hash(self): + """Should accept when provided SHA1 matches plaintext_key.""" + async with DpapiManager(storage_backend="memory") as manager: + from Crypto.Hash import SHA1 + + guid = uuid4() + plaintext = b"my_plaintext_key" + correct_sha1 = SHA1.new(plaintext).digest() + + masterkey = MasterKey( + guid=guid, + masterkey_type=MasterKeyType.USER, + plaintext_key=plaintext, + plaintext_key_sha1=correct_sha1, + ) + + await manager.upsert_masterkey(masterkey) + + # Verify + result = await manager.get_masterkeys(guid=guid) + assert result[0].plaintext_key_sha1 == correct_sha1 + + @pytest.mark.asyncio + async def test_sha1_only_update(self): + """Should allow updating only SHA1 without plaintext_key.""" + async with DpapiManager(storage_backend="memory") as manager: + guid = uuid4() + sha1_value = b"1" * 20 + + masterkey = MasterKey( + guid=guid, + masterkey_type=MasterKeyType.USER, + plaintext_key_sha1=sha1_value, # Only SHA1 + ) + + await manager.upsert_masterkey(masterkey) + + # Verify + result = await manager.get_masterkeys(guid=guid) + assert result[0].plaintext_key_sha1 == sha1_value + assert result[0].plaintext_key is None + + @pytest.mark.asyncio + async def test_multiple_field_conflicts(self): + """Should detect multiple field conflicts.""" + async with DpapiManager(storage_backend="memory") as manager: + guid = uuid4() + + # First insert + masterkey1 = MasterKey( + guid=guid, + masterkey_type=MasterKeyType.USER, + encrypted_key_usercred=b"encrypted1", + plaintext_key=b"plaintext1", + ) + await manager.upsert_masterkey(masterkey1) + + # Try to change both + masterkey2 = MasterKey( + guid=guid, + masterkey_type=MasterKeyType.USER, + encrypted_key_usercred=b"encrypted2", # Changed + plaintext_key=b"plaintext2", # Changed + ) + + with pytest.raises(WriteOnceViolationError) as exc_info: + await manager.upsert_masterkey(masterkey2) + + # Should report both conflicts + assert "encrypted_key_usercred" in exc_info.value.fields + assert "plaintext_key" in exc_info.value.fields + + +class TestDomainBackupKeyWriteOnce: + """Test write-once semantics for domain backup key upserts.""" + + def _create_valid_backup_key_data(self) -> bytes: + """Create valid PVK file header + private key data for testing.""" + # This is a minimal valid PVK structure for testing + # In real scenarios, this would be actual RSA private key data + import struct + + PVK_MAGIC = 0xB0B5F11E + PVK_VERSION = 0 + KEY_SPEC = 1 + ENCRYPT_TYPE = 0 + ENCRYPT_DATA_SIZE = 0 + PVK_SIZE = 20 # Minimal size + + header = struct.pack( + "<6I", + PVK_MAGIC, + PVK_VERSION, + KEY_SPEC, + ENCRYPT_TYPE, + ENCRYPT_DATA_SIZE, + PVK_SIZE, + ) + + # Add minimal private key data (would be actual PRIVATE_KEY_BLOB in real usage) + private_key = b"\x00" * PVK_SIZE + + return header + private_key + + @pytest.mark.asyncio + async def test_insert_new_backup_key(self): + """Should successfully insert a new domain backup key.""" + async with DpapiManager(storage_backend="memory") as manager: + guid = uuid4() + key_data = self._create_valid_backup_key_data() + + backup_key = DomainBackupKey( + guid=guid, + key_data=key_data, + domain_controller="DC01.contoso.com", + ) + + await manager.upsert_domain_backup_key(backup_key) + + # Verify + result = await manager.get_backup_keys(guid=guid) + assert len(result) == 1 + assert result[0].guid == guid + + @pytest.mark.asyncio + async def test_idempotent_update_backup_key(self): + """Should allow idempotent updates.""" + async with DpapiManager(storage_backend="memory") as manager: + guid = uuid4() + key_data = self._create_valid_backup_key_data() + + backup_key = DomainBackupKey( + guid=guid, + key_data=key_data, + ) + + await manager.upsert_domain_backup_key(backup_key) + await manager.upsert_domain_backup_key(backup_key) # Should succeed + + result = await manager.get_backup_keys(guid=guid) + assert len(result) == 1 + + @pytest.mark.asyncio + async def test_reject_changing_key_data(self): + """Should reject attempt to change key_data.""" + async with DpapiManager(storage_backend="memory") as manager: + guid = uuid4() + key_data1 = self._create_valid_backup_key_data() + key_data2 = self._create_valid_backup_key_data() + b"\xff" # Different + + # First insert + backup_key1 = DomainBackupKey(guid=guid, key_data=key_data1) + await manager.upsert_domain_backup_key(backup_key1) + + # Try to change + backup_key2 = DomainBackupKey(guid=guid, key_data=key_data2) + + with pytest.raises(WriteOnceViolationError) as exc_info: + await manager.upsert_domain_backup_key(backup_key2) + + assert exc_info.value.entity_type == "backup_key" + assert "key_data" in exc_info.value.fields + + @pytest.mark.asyncio + async def test_reject_changing_domain_controller(self): + """Should reject attempt to change domain_controller.""" + async with DpapiManager(storage_backend="memory") as manager: + guid = uuid4() + key_data = self._create_valid_backup_key_data() + + # First insert + backup_key1 = DomainBackupKey( + guid=guid, + key_data=key_data, + domain_controller="DC01.contoso.com", + ) + await manager.upsert_domain_backup_key(backup_key1) + + # Try to change domain_controller + backup_key2 = DomainBackupKey( + guid=guid, + key_data=key_data, + domain_controller="DC02.contoso.com", # Different + ) + + with pytest.raises(WriteOnceViolationError) as exc_info: + await manager.upsert_domain_backup_key(backup_key2) + + assert "domain_controller" in exc_info.value.fields + + @pytest.mark.asyncio + async def test_allow_filling_null_domain_controller(self): + """Should allow setting domain_controller when previously NULL.""" + async with DpapiManager(storage_backend="memory") as manager: + guid = uuid4() + key_data = self._create_valid_backup_key_data() + + # First insert without domain_controller + backup_key1 = DomainBackupKey(guid=guid, key_data=key_data) + await manager.upsert_domain_backup_key(backup_key1) + + # Add domain_controller + backup_key2 = DomainBackupKey( + guid=guid, + key_data=key_data, + domain_controller="DC01.contoso.com", + ) + await manager.upsert_domain_backup_key(backup_key2) + + # Verify + result = await manager.get_backup_keys(guid=guid) + assert result[0].domain_controller == "DC01.contoso.com" + + @pytest.mark.asyncio + async def test_reject_empty_string_domain_controller(self): + """Should reject empty string for domain_controller.""" + async with DpapiManager(storage_backend="memory") as manager: + guid = uuid4() + key_data = self._create_valid_backup_key_data() + + backup_key = DomainBackupKey( + guid=guid, + key_data=key_data, + domain_controller="", # Empty string + ) + + with pytest.raises(ValueError, match="cannot be empty string"): + await manager.upsert_domain_backup_key(backup_key) + + @pytest.mark.asyncio + async def test_case_sensitive_domain_controller(self): + """Should treat domain_controller as case-sensitive.""" + async with DpapiManager(storage_backend="memory") as manager: + guid = uuid4() + key_data = self._create_valid_backup_key_data() + + # First insert with uppercase + backup_key1 = DomainBackupKey( + guid=guid, + key_data=key_data, + domain_controller="DC01.CONTOSO.COM", + ) + await manager.upsert_domain_backup_key(backup_key1) + + # Try with lowercase (different value due to case sensitivity) + backup_key2 = DomainBackupKey( + guid=guid, + key_data=key_data, + domain_controller="dc01.contoso.com", + ) + + with pytest.raises(WriteOnceViolationError): + await manager.upsert_domain_backup_key(backup_key2) + + +class TestSystemCredentialWriteOnce: + """Test that system credentials already have write-once semantics.""" + + @pytest.mark.asyncio + async def test_system_credentials_already_write_once(self): + """System credentials should already use DO NOTHING (no changes needed).""" + from nemesis_dpapi.keys import DpapiSystemCredential + + async with DpapiManager(storage_backend="memory", auto_decrypt=False) as manager: + # First insert + cred1 = DpapiSystemCredential(user_key=b"0" * 20, machine_key=b"1" * 20) + await manager.upsert_system_credential(cred1) + + # Second insert with same keys (should succeed, idempotent) + cred2 = DpapiSystemCredential(user_key=b"0" * 20, machine_key=b"1" * 20) + await manager.upsert_system_credential(cred2) + + # Verify + result = await manager.get_system_credentials() + assert len(result) == 1 diff --git a/mkdocs.yml b/mkdocs.yml index b20cbd5..0fc18a2 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -18,9 +18,12 @@ nav: - Usage: - usage_guide.md - cli.md + - containers.md + - agents.md - troubleshooting.md - performance.md - yara.md + - chromium.md - Services: - dapr.md @@ -33,6 +36,7 @@ nav: - Adding Nosey Parker Rules: noseyparker_rules.md - Operational Data Reference: odr.md - Docker Compose Documentation: docker_compose.md + - Nemesis API: api.md theme: name: material diff --git a/projects/InspectAssembly/Dockerfile b/projects/InspectAssembly/Dockerfile deleted file mode 100644 index 9c8b286..0000000 --- a/projects/InspectAssembly/Dockerfile +++ /dev/null @@ -1,25 +0,0 @@ -# Use the official .NET SDK image as the base image for building -FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build -WORKDIR /src - -# Copy the solution file and project files -COPY ["InspectAssembly.sln", "./"] -COPY ["InspectAssembly/InspectAssembly.csproj", "InspectAssembly/"] - -# Restore NuGet packages -RUN dotnet restore - -# Copy the remaining source code -COPY . . - -# Build the application -RUN dotnet build "InspectAssembly/InspectAssembly.csproj" -c Release -o /app/build - -# Publish the application -RUN dotnet publish "InspectAssembly/InspectAssembly.csproj" -c Release -o /app/publish - -# Create the runtime image -FROM mcr.microsoft.com/dotnet/runtime:6.0 -WORKDIR /app -COPY --from=build /app/publish . -ENTRYPOINT ["dotnet", "InspectAssembly.dll"] \ No newline at end of file diff --git a/projects/InspectAssembly/InspectAssembly.sln b/projects/InspectAssembly/InspectAssembly.sln deleted file mode 100755 index bb7a313..0000000 --- a/projects/InspectAssembly/InspectAssembly.sln +++ /dev/null @@ -1,25 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.3.32804.467 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "InspectAssembly", "InspectAssembly\InspectAssembly.csproj", "{89A5830C-EE94-4917-A978-30572339A1C7}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {89A5830C-EE94-4917-A978-30572339A1C7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {89A5830C-EE94-4917-A978-30572339A1C7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {89A5830C-EE94-4917-A978-30572339A1C7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {89A5830C-EE94-4917-A978-30572339A1C7}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {A7AFB945-4341-4030-8505-E330F939BE35} - EndGlobalSection -EndGlobal diff --git a/projects/InspectAssembly/InspectAssembly/InspectAssembly.csproj b/projects/InspectAssembly/InspectAssembly/InspectAssembly.csproj deleted file mode 100755 index 9aacdd5..0000000 --- a/projects/InspectAssembly/InspectAssembly/InspectAssembly.csproj +++ /dev/null @@ -1,15 +0,0 @@ - - - - Exe - net6.0 - enable - enable - - - - - - - - diff --git a/projects/InspectAssembly/InspectAssembly/Program.cs b/projects/InspectAssembly/InspectAssembly/Program.cs deleted file mode 100755 index 99eda52..0000000 --- a/projects/InspectAssembly/InspectAssembly/Program.cs +++ /dev/null @@ -1,431 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Net; -using System.Reflection; -using System.Security.Cryptography; -using Mono.Cecil; -using Newtonsoft.Json; - -namespace InspectAssembly -{ - class Program - { - private const string BF_DESERIALIZE = "System.Runtime.Serialization.Formatters.Binary.BinaryFormatter::Deserialize"; - private const string DC_JSON_READ_OBJ = "System.Runtime.Serialization.Json.DataContractJsonSerializer::ReadObject"; - private const string DC_XML_READ_OBJ = "System.Runtime.Serialization.Xml.DataContractSerializer::ReadObject"; - private const string JS_SERIALIZER_DESERIALIZE = "System.Web.Script.Serialization.JavaScriptSerializer::Deserialize"; - private const string LOS_FORMATTER_DESERIALIZE = "System.Web.UI.LosFormatter::Deserialize"; - private const string NET_DATA_CONTRACT_READ_OBJ = "System.Runtime.Serialization.NetDataContractSerializer::ReadObject"; - private const string NET_DATA_CONTRACT_DESERIALIZE = "System.Runtime.Serialization.NetDataContractSerializer::Deserialize"; - private const string OBJ_STATE_FORMATTER_DESERIALIZE = "System.Web.UI.ObjectStateFormatter::Deserialize"; - private const string SOAP_FORMATTER_DESERIALIZE = "System.Runtime.Serialization.Formatters.Soap.SoapFormatter::Deserialize"; - private const string XML_SERIALIZER_DESERIALIZE = "System.Xml.Serialization.XmlSerializer::Deserialize"; - private const string REGISTER_CHANNEL = "System.Runtime.Remoting.Channels.ChannelServices::RegisterChannel"; - private const string WCF_SERVER_STRING = "System.ServiceModel.ServiceHost::AddServiceEndpoint"; - private const string WCF_CLIENT_STRING = "System.ServiceModel.ChannelFactory::CreateChannel"; - private const string JSCRIPT_EVALUATION = "Microsoft.JScript.Eval::JScriptEvaluate"; - private const string POWERSHELL_EVALUATION = "System.Management.Automation.Runspaces.Pipeline::Invoke"; - private const string PROCESS_START = "System.Diagnostics.Process::Start"; - - private static string[] wcfServerGadgetNames = { WCF_SERVER_STRING }; - - - static void Main(string[] args) - { - //JsonConvert.SerializeObject() - if (args.Length != 1) - { - Console.WriteLine("{\"error\": \"no file path provided\"}"); - return; - } - else if (!File.Exists(args[0])) - { - Console.WriteLine($"{{\"error\": \"file path {args[0]} does not exist\"}}"); - return; - } - - var assemblyPath = args[0]; - - try - { - // Make sure that the target is actually an assembly before we get started - AssemblyName assemblyName = AssemblyName.GetAssemblyName(assemblyPath); - } - catch - { - Console.WriteLine($"{{\"error\": \"file path {args[0]} is not an assembly\"}}"); - return; - } - - AssemblyAnalysis result = AnalyzeAssembly(assemblyPath); - Console.WriteLine(JsonConvert.SerializeObject(result)); - } - - public static Dictionary FormatGadgets(Dictionary tmp) - { - Dictionary gadgets = new Dictionary(); - - foreach (var key in tmp.Keys) - { - string[] gadgetParts = key.Replace("::", "|").Split('|'); - string gadget; - if (gadgetParts.Length != 2) - gadget = key; - else - { - string[] typeParts = gadgetParts[0].Split('.'); - gadget = String.Format("{0}::{1}()", typeParts[typeParts.Length - 1], gadgetParts[1]); - } - - MethodInfo[] result = tmp[key].Distinct().Select((o) => - { - return o; - }).ToArray(); - - gadgets[gadget] = result; - } - return gadgets; - } - - internal struct AssemblyAnalysis - { - string AssemblyName; - public string[] RemotingChannels; - public bool IsWCFServer; - public bool IsWCFClient; - public Dictionary SerializationGadgetCalls; - public Dictionary WcfServerCalls; - public Dictionary ClientCalls; - public Dictionary RemotingCalls; - public Dictionary ExecutionCalls; - - public AssemblyAnalysis(string assemblyName, GadgetItem[] items) - { - AssemblyName = assemblyName; - IsWCFClient = false; - IsWCFServer = false; - Dictionary> temp = new Dictionary>(); - Dictionary> tempClient = new Dictionary>(); - Dictionary> tempServer = new Dictionary>(); - Dictionary> tempRemoting = new Dictionary>(); - Dictionary> tempExecution = new Dictionary>(); - List dnRemotingChannels = new List(); - - foreach (var gadget in items) - { - if (gadget.IsWCFClient && !tempClient.ContainsKey(gadget.GadgetName)) - tempClient[gadget.GadgetName] = new List(); - else if (gadget.IsWCFServer && !tempServer.ContainsKey(gadget.GadgetName)) - tempServer[gadget.GadgetName] = new List(); - if (gadget.IsDotNetRemoting && !tempClient.ContainsKey(gadget.GadgetName)) - tempRemoting[gadget.GadgetName] = new List(); - if (gadget.IsExecution && !tempClient.ContainsKey(gadget.GadgetName)) - tempExecution[gadget.GadgetName] = new List(); - else if (!temp.ContainsKey(gadget.GadgetName)) - temp[gadget.GadgetName] = new List(); - if (gadget.IsWCFClient) - { - tempClient[gadget.GadgetName].Add(new MethodInfo() - { - MethodName = gadget.MethodAppearance, - FilterLevel = gadget.FilterLevel - }); - } - else if (gadget.IsWCFServer) - { - tempServer[gadget.GadgetName].Add(new MethodInfo() - { - MethodName = gadget.MethodAppearance, - FilterLevel = gadget.FilterLevel - }); - } - else if (gadget.IsDotNetRemoting) - { - tempRemoting[gadget.GadgetName].Add(new MethodInfo() - { - MethodName = gadget.MethodAppearance, - FilterLevel = gadget.FilterLevel - }); - } - else if (gadget.IsExecution) - { - tempExecution[gadget.GadgetName].Add(new MethodInfo() - { - MethodName = gadget.MethodAppearance, - FilterLevel = gadget.FilterLevel - }); - } - else - { - temp[gadget.GadgetName].Add(new MethodInfo() - { - MethodName = gadget.MethodAppearance, - FilterLevel = gadget.FilterLevel - }); - } - if (gadget.IsDotNetRemoting) - dnRemotingChannels.Add(gadget.RemotingChannel); - } - RemotingChannels = dnRemotingChannels.ToArray(); - SerializationGadgetCalls = new Dictionary(); - ClientCalls = new Dictionary(); - WcfServerCalls = new Dictionary(); - RemotingCalls = new Dictionary(); - ExecutionCalls = new Dictionary(); - foreach (var key in temp.Keys) - { - if (!string.IsNullOrEmpty(key)) - SerializationGadgetCalls[key] = temp[key].ToArray(); - } - foreach (var key in tempClient.Keys) - { - if (!string.IsNullOrEmpty(key)) - ClientCalls[key] = tempClient[key].ToArray(); - } - foreach (var key in tempServer.Keys) - { - if (!string.IsNullOrEmpty(key)) - WcfServerCalls[key] = tempServer[key].ToArray(); - } - foreach (var key in tempRemoting.Keys) - { - if (!string.IsNullOrEmpty(key)) - RemotingCalls[key] = tempRemoting[key].ToArray(); - } - foreach (var key in tempExecution.Keys) - { - if (!string.IsNullOrEmpty(key)) - ExecutionCalls[key] = tempExecution[key].ToArray(); - } - } - - public override string ToString() - { - string fmtStr = ""; - var tmp = SerializationGadgetCalls; - if (RemotingChannels.Length > 0) - { - fmtStr += string.Format(" .NET Remoting Channels:\n"); - foreach (var chan in RemotingChannels) - fmtStr += string.Format(" {0}\n", chan); - } - if (RemotingCalls.Keys.Count > 0) - { - fmtStr += " .NET Remoting:\n"; - fmtStr += FormatGadgets(RemotingCalls); - //Console.WriteLine(FormatGadgets(RemotingCalls)); - - fmtStr += " Remoting Channels:\n"; - if (RemotingChannels.Length > 0) - { - foreach (var chan in RemotingChannels) - fmtStr += string.Format(" {0}\n", chan); - } - } - if (ClientCalls.Keys.Count > 0) - { - fmtStr += " WCFClient Gadgets:\n"; - fmtStr += FormatGadgets(ClientCalls); - } - if (WcfServerCalls.Keys.Count > 0) - { - fmtStr += " WCFServer Gadgets:\n"; - fmtStr += FormatGadgets(WcfServerCalls); - } - if (SerializationGadgetCalls.Keys.Count > 0) - { - fmtStr += " Serialization Gadgets:\n"; - fmtStr += FormatGadgets(SerializationGadgetCalls); - } - if (fmtStr != "") - fmtStr = String.Format("Assembly Name: {0}\n", AssemblyName) + fmtStr; - return fmtStr; - } - } - - public struct MethodInfo - { - public string MethodName; - public string FilterLevel; - - public override string ToString() - { - return !string.IsNullOrEmpty(FilterLevel) ? string.Format("{0} (Filter Level: {1})", MethodName, FilterLevel) : MethodName; - } - } - - internal struct GadgetItem - { - internal bool IsDotNetRemoting; - internal string RemotingChannel; - internal bool IsWCFServer; - internal bool IsWCFClient; - internal bool IsExecution; - internal string GadgetName; - internal string FilterLevel; - internal string MethodAppearance; - - public override string ToString() - { - //Console.WriteLine("[+] Assembly registers a .NET Remoting channel ({0}) in {1}.{2}", dnrChannel[5], method.t.Name, method.m.Name); - string[] gadgetParts = GadgetName.Replace("::", "|").Split('|'); - string gadget; - if (gadgetParts.Length != 2) - gadget = GadgetName; - else - { - string[] typeParts = gadgetParts[0].Split('.'); - gadget = String.Format("{0}::{1}()", typeParts[typeParts.Length - 1], gadgetParts[1]); - } - string fmtMessage = String.Format(@" -IsDotNetRemoting : {0} - RemotingChannel : {1} -IsWCFServer : {2} -IsWCFClient : {3} -IsExecution : {4} -GadgetName : {5} -MethodAppearance : {6}", IsDotNetRemoting, RemotingChannel, IsWCFServer, IsWCFClient, IsExecution, gadget, MethodAppearance); - if (!string.IsNullOrEmpty(FilterLevel)) - fmtMessage += string.Format("\n\tFilterLevel : {0}", FilterLevel); - return fmtMessage; - } - } - - static AssemblyAnalysis AnalyzeAssembly(string assemblyName) - { - // Just in case we run into .NET Remoting - string[] dnrChannel = { }; - string typeFilterLevel = "ldc.i4.2"; // Default opcode if not set manually - string filterLevel = "Low"; - List listGadgets = new List(); - - // Parse the target assembly and get its types - AssemblyDefinition assembly = AssemblyDefinition.ReadAssembly(assemblyName); - IEnumerable allTypes = assembly.MainModule.GetTypes(); - - // Pull out all the type with methods that we want to look at - var validTypes = allTypes.SelectMany(t => t.Methods.Select(m => new { t, m })) - .Where(x => x.m.HasBody); - - foreach (var method in validTypes) - { - // Disassemble the assembly and check for potentially vulnerable functions - foreach (var instruction in method.m.Body.Instructions) - { - string gadgetName = ""; - bool isRemoting = false; - string remotingChannel = ""; - bool isWCFServer = false; - bool isWCFClient = false; - bool isExecution = false; - - // Deserialization checks - if (instruction.OpCode.ToString() == "callvirt") - { - switch (instruction.Operand.ToString()) - { - case string x when x.Contains(BF_DESERIALIZE): - gadgetName = BF_DESERIALIZE; - break; - case string x when x.Contains(DC_JSON_READ_OBJ): - gadgetName = DC_JSON_READ_OBJ; - break; - case string x when x.Contains(DC_XML_READ_OBJ): - gadgetName = DC_XML_READ_OBJ; - break; - case string x when x.Contains(JS_SERIALIZER_DESERIALIZE): - gadgetName = JS_SERIALIZER_DESERIALIZE; - break; - case string x when x.Contains(LOS_FORMATTER_DESERIALIZE): - gadgetName = LOS_FORMATTER_DESERIALIZE; - break; - case string x when x.Contains(NET_DATA_CONTRACT_READ_OBJ): - gadgetName = NET_DATA_CONTRACT_READ_OBJ; - break; - case string x when x.Contains(NET_DATA_CONTRACT_DESERIALIZE): - gadgetName = NET_DATA_CONTRACT_DESERIALIZE; - break; - case string x when x.Contains(OBJ_STATE_FORMATTER_DESERIALIZE): - gadgetName = OBJ_STATE_FORMATTER_DESERIALIZE; - break; - case string x when x.Contains(SOAP_FORMATTER_DESERIALIZE): - gadgetName = SOAP_FORMATTER_DESERIALIZE; - break; - case string x when x.Contains(XML_SERIALIZER_DESERIALIZE): - gadgetName = XML_SERIALIZER_DESERIALIZE; - break; - case string x when x.Contains(POWERSHELL_EVALUATION): - gadgetName = POWERSHELL_EVALUATION; - isExecution = true; - break; - case string x when x.Contains(WCF_SERVER_STRING): - gadgetName = WCF_SERVER_STRING; - isWCFServer = true; - break; - case string x when x.Contains("System.ServiceModel.ChannelFactory") && x.Contains("CreateChannel"): // System.ServiceModel.ChannelFactory`1::CreateChannel() - gadgetName = WCF_CLIENT_STRING; - isWCFClient = true; - break; - // Collect the TypeFilterLevel if it is explicitly set - case string x when x.Contains("set_FilterLevel(System.Runtime.Serialization.Formatters.TypeFilterLevel)"): - if (typeFilterLevel.EndsWith("3")) - { - filterLevel = "Full"; - } - break; - } - - } - else if (instruction.OpCode.ToString().StartsWith("ldc.i4")) - { - typeFilterLevel = instruction.OpCode.ToString(); - } - else if (instruction.OpCode.ToString() == "newobj" && instruction.Operand.ToString().Contains("System.Runtime.Remoting.Channels.")) - { - // .NET Remoting Checks - dnrChannel = instruction.Operand.ToString().Split('.'); - } - else if (instruction.OpCode.ToString() == "call") - { - switch (instruction.Operand.ToString()) - { - case string x when x.Contains(JSCRIPT_EVALUATION): - gadgetName = JSCRIPT_EVALUATION; - isExecution = true; - break; - case string x when x.Contains(PROCESS_START): - gadgetName = PROCESS_START; - isExecution = true; - break; - case string x when x.Contains(REGISTER_CHANNEL): - isRemoting = true; - gadgetName = REGISTER_CHANNEL; - remotingChannel = dnrChannel[5]; - break; - } - } - - if (!string.IsNullOrEmpty(gadgetName) || isWCFClient || isWCFServer || isRemoting) - { - listGadgets.Add(new GadgetItem() - { - GadgetName = gadgetName, - IsDotNetRemoting = isRemoting, - RemotingChannel = remotingChannel, - IsWCFClient = isWCFClient, - IsWCFServer = isWCFServer, - IsExecution = isExecution, - MethodAppearance = String.Format("{0}.{1}", method.t.Name, method.m.Name), - FilterLevel = gadgetName.Contains(BF_DESERIALIZE) ? filterLevel : null - }); - } - } - } - - return new AssemblyAnalysis(assemblyName, listGadgets.ToArray()); - } - } -} \ No newline at end of file diff --git a/projects/InspectAssembly/README.md b/projects/InspectAssembly/README.md deleted file mode 100644 index 26be9ec..0000000 --- a/projects/InspectAssembly/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# InspectAssemblyDotnet6 - -InspectAssembly built with structured output and compatible with .NET 6.0/Linux. This project is compiled and used in .NET enrichment services. - -Original [InspectAssembly code](https://github.com/matterpreter/OffensiveCSharp/tree/master/InspectAssembly) is by [@matterpreter](https://github.com/matterpreter/OffensiveCSharp/tree/master/InspectAssembly) under a BSD 3-Clause license. - -To run: `# dotnet InspectAssembly.dll program.exe` \ No newline at end of file diff --git a/projects/triage/.vscode/launch.json b/projects/agents/.vscode/launch.json similarity index 100% rename from projects/triage/.vscode/launch.json rename to projects/agents/.vscode/launch.json diff --git a/projects/dotnet_api/.vscode/settings.json b/projects/agents/.vscode/settings.json similarity index 88% rename from projects/dotnet_api/.vscode/settings.json rename to projects/agents/.vscode/settings.json index 34bd581..8818d80 100644 --- a/projects/dotnet_api/.vscode/settings.json +++ b/projects/agents/.vscode/settings.json @@ -8,13 +8,12 @@ "[python]": { "editor.formatOnSave": true, "editor.codeActionsOnSave": { - "source.fixAll": "explicit", - "source.organizeImports": "explicit" + "source.fixAll.ruff": "explicit", + "source.organizeImports.ruff": "explicit" }, - "editor.defaultFormatter": "charliermarsh.ruff" + "editor.defaultFormatter": "charliermarsh.ruff", }, "autoDocstring.docstringFormat": "google", - "editor.formatOnSave": true, "files.exclude": { "**/.DS_Store": true, "**/.git": true, @@ -62,4 +61,5 @@ "python.testing.unittestEnabled": false, "python.testing.pytestEnabled": true, "python.analysis.typeCheckingMode": "basic", + "python.defaultInterpreterPath": "${workspaceFolder}/.venv/bin/python" } \ No newline at end of file diff --git a/projects/triage/Dockerfile b/projects/agents/Dockerfile similarity index 58% rename from projects/triage/Dockerfile rename to projects/agents/Dockerfile index 9834e55..fc9c2a7 100644 --- a/projects/triage/Dockerfile +++ b/projects/agents/Dockerfile @@ -3,15 +3,33 @@ ARG PYTHON_BASE_DEV_IMAGE=nemesis-python-base-dev ARG PYTHON_BASE_PROD_IMAGE=nemesis-python-base-prod FROM ${PYTHON_BASE_DEV_IMAGE} AS base +RUN apt-get update && \ + apt-get install -y libpq5 \ + gcc libc6-dev curl wget libicu-dev && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* + + +# Install .NET Runtime +ENV DOTNET_ROOT=/usr/local/dotnet \ + PATH=/usr/local/dotnet:$PATH + +RUN curl -sSL https://dot.net/v1/dotnet-install.sh | bash /dev/stdin \ + --version latest \ + --runtime aspnetcore \ + --install-dir /usr/local/dotnet \ + && chmod +x /usr/local/dotnet/dotnet + + # If dependencies change, re-copy all the dependencies # In the future we can be more efficient with this an only copy the lib folders # that this project uses -COPY ./projects/triage/poetry.lock ./projects/triage/pyproject.toml /src/projects/triage/ +COPY ./projects/agents/poetry.lock ./projects/agents/pyproject.toml /src/projects/agents/ COPY ./libs /src/libs -COPY ./projects/triage /src/projects/triage/ +COPY ./projects/agents /src/projects/agents/ -WORKDIR /src/projects/triage +WORKDIR /src/projects/agents ######################## # Development @@ -19,7 +37,7 @@ WORKDIR /src/projects/triage FROM base AS dev COPY --from=base /src /src -WORKDIR /src/projects/triage +WORKDIR /src/projects/agents RUN poetry install # Immediate output (no buffering) @@ -34,7 +52,7 @@ ENV UVICORN_PORT=8000 ENV UVICORN_RELOAD_DIR="/src/" ENTRYPOINT ["/bin/sh", "-c", " \ - poetry run uvicorn triage.main:app \ + poetry run uvicorn agents.main:app \ --host ${UVICORN_HOST} \ --port ${UVICORN_PORT} \ --reload \ @@ -47,11 +65,23 @@ ENTRYPOINT ["/bin/sh", "-c", " \ FROM base AS bundle COPY --from=base /src /src -WORKDIR /src/projects/triage +WORKDIR /src/projects/agents RUN poetry bundle venv --python=/usr/bin/python3 --only=main /venv # FROM nemesis-python-base-prod AS prod FROM ${PYTHON_BASE_PROD_IMAGE} AS prod + +# Install runtime dependencies for psycopg and .NET +RUN apt-get update && \ + apt-get install -y libpq5 libicu72 && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* + +# Copy .NET runtime from base stage +ENV DOTNET_ROOT=/usr/local/dotnet \ + PATH=/usr/local/dotnet:$PATH +COPY --from=base /usr/local/dotnet /usr/local/dotnet + COPY --from=bundle /venv /venv @@ -66,7 +96,7 @@ ENV UVICORN_HOST=0.0.0.0 \ # USER nemesis ENTRYPOINT ["/bin/sh", "-c", "\ - /venv/bin/uvicorn \"triage.main:app\" \ + /venv/bin/uvicorn \"agents.main:app\" \ --host ${UVICORN_HOST} \ --port ${UVICORN_PORT} \ --workers ${UVICORN_WORKERS} \ diff --git a/projects/agents/README.md b/projects/agents/README.md new file mode 100644 index 0000000..ec76cad --- /dev/null +++ b/projects/agents/README.md @@ -0,0 +1,9 @@ +# Agents Service + +TODO + +## Purpose + +This service contains a number of agents used by Nemesis (if configured) to achieve a number of tasks, including triaging alerts. + +This service automatically triages security findings generated by the platform, classifying them as true positives, false positives, or requiring manual review. It helps security analysts prioritize their investigation efforts by reducing false positives and highlighting genuine threats. diff --git a/projects/agents/agents/agent_manager.py b/projects/agents/agents/agent_manager.py new file mode 100644 index 0000000..0660bef --- /dev/null +++ b/projects/agents/agents/agent_manager.py @@ -0,0 +1,155 @@ +"""Agent manager for dynamic loading and registration of agents.""" + +import importlib +import inspect +from pathlib import Path + +from agents.base_agent import BaseAgent +from common.db import get_postgres_connection_str +from common.logger import get_logger + +logger = get_logger(__name__) + + +class AgentManager: + """Manages dynamic loading and registration of agents.""" + + def __init__(self): + self.agents: dict[str, type[BaseAgent]] = {} + self.agent_instances: dict[str, BaseAgent] = {} + self.tasks_dir = Path(__file__).parent / "tasks" + + def discover_agents(self) -> dict[str, type[BaseAgent]]: + """Dynamically discover and load all agent classes from the tasks directory.""" + discovered_agents = {} + + for py_file in self.tasks_dir.glob("*.py"): + if py_file.name.startswith("__"): + continue + + module_name = py_file.stem + try: + # Import the module + module = importlib.import_module(f"agents.tasks.{module_name}") + + # Find classes that inherit from BaseAgent + for name, obj in inspect.getmembers(module, inspect.isclass): + if obj != BaseAgent and issubclass(obj, BaseAgent) and obj.__module__ == module.__name__: + agent_key = module_name + discovered_agents[agent_key] = obj + logger.debug( + "Discovered agent class", + module=module_name, + class_name=name, + agent_key=agent_key, + ) + break + + except Exception as e: + logger.warning( + "Failed to load agent module", + module=module_name, + error=str(e), + ) + + return discovered_agents + + def load_agents(self) -> None: + """Load all discovered agents and initialize their prompts.""" + self.agents = self.discover_agents() + logger.info("Loaded agents", count=len(self.agents), agents=list(self.agents.keys())) + + # Initialize prompts for agents that have them + self.initialize_agent_prompts() + + def get_agent_instance(self, agent_key: str) -> BaseAgent: + """Get or create an agent instance.""" + if agent_key not in self.agent_instances: + if agent_key not in self.agents: + raise ValueError(f"Agent '{agent_key}' not found") + + agent_class = self.agents[agent_key] + self.agent_instances[agent_key] = agent_class() + logger.debug("Created agent instance", agent_key=agent_key) + + return self.agent_instances[agent_key] + + def get_agent_metadata(self) -> list[dict]: + """Get metadata for all loaded agents.""" + metadata = [] + + for agent_key, agent_class in self.agents.items(): + try: + # Create temporary instance to get metadata + instance = agent_class() + metadata.append( + { + "name": getattr(instance, "name", agent_key), + "description": getattr(instance, "description", f"Agent: {agent_key}"), + "agent_type": getattr(instance, "agent_type", "unknown"), + "has_prompt": getattr(instance, "has_prompt", False), + "enabled": True, + } + ) + except Exception as e: + logger.warning( + "Failed to get metadata for agent", + agent_key=agent_key, + error=str(e), + ) + # Add basic metadata even if instance creation fails + metadata.append( + { + "name": agent_key, + "description": f"Agent: {agent_key}", + "agent_type": "unknown", + "has_prompt": False, + "enabled": True, + } + ) + + return metadata + + def get_wrapper_function(self, agent_key: str): + """Get the wrapper function for an agent to maintain compatibility.""" + + def wrapper_function(ctx, activity_input: dict) -> dict: + agent = self.get_agent_instance(agent_key) + return agent.execute(ctx, activity_input) + + wrapper_function.__name__ = f"{agent_key}_wrapper" + return wrapper_function + + def register_activities(self, workflow_runtime): + """Register all agent activities with the workflow runtime.""" + for agent_key in self.agents.keys(): + wrapper_func = self.get_wrapper_function(agent_key) + workflow_runtime.activity(wrapper_func) + logger.debug("Registered activity", agent_key=agent_key, function_name=wrapper_func.__name__) + + def initialize_agent_prompts(self): + """Initialize agent prompts in the database for agents that have prompts.""" + from agents.prompt_manager import PromptManager + + prompt_manager = PromptManager(get_postgres_connection_str()) + + for agent_key in self.agents.keys(): + try: + agent = self.get_agent_instance(agent_key) + if hasattr(agent, "has_prompt") and agent.has_prompt: + if hasattr(agent, "system_prompt"): + success = prompt_manager.save_prompt(agent.name, agent.system_prompt, agent.description) + if success: + logger.debug("Initialized prompt for agent", agent_key=agent_key) + else: + logger.warning("Failed to save prompt for agent", agent_key=agent_key) + except Exception as e: + logger.warning( + "Failed to initialize prompt for agent", + agent_key=agent_key, + error=str(e), + ) + + +# Global instance +agent_manager = AgentManager() diff --git a/projects/agents/agents/base_agent.py b/projects/agents/agents/base_agent.py new file mode 100644 index 0000000..b6c7ff5 --- /dev/null +++ b/projects/agents/agents/base_agent.py @@ -0,0 +1,81 @@ +"""Base class for all agents.""" + +from abc import ABC, abstractmethod + +from common.logger import get_logger +from dapr.ext.workflow.workflow_activity_context import WorkflowActivityContext + +logger = get_logger(__name__) + + +class BaseAgent(ABC): + """Base class for all agents in the system.""" + + def __init__(self): + """Initialize the agent.""" + # These should be set by subclasses + self.name: str = "" + self.description: str = "" + self.agent_type: str = "unknown" # "llm_based" or "rule_based" + self.has_prompt: bool = False + self.enabled: bool = True + + @abstractmethod + def execute(self, ctx: WorkflowActivityContext, activity_input: dict) -> dict: + """ + Execute the agent's main logic. + + Args: + ctx: Workflow activity context + activity_input: Input data for the agent + + Returns: + Dict containing the agent's result + """ + pass + + def get_prompt(self) -> str | None: + """ + Get the agent's prompt (for LLM-based agents). + + Returns: + The prompt string if this agent uses prompts, None otherwise + """ + return None + + def initialize_prompt(self): + """ + Initialize the agent's prompt in database if needed. + This is called during service startup for agents that have prompts. + """ + if self.has_prompt: + # Try to get/initialize prompt + prompt = self.get_prompt() + if prompt: + logger.debug(f"Prompt initialized for agent {self.name}") + + def get_metadata(self) -> dict: + """ + Get agent metadata for discovery. + + Returns: + Dict containing agent metadata + """ + return { + "name": self.name, + "description": self.description, + "has_prompt": self.has_prompt, + "enabled": self.enabled, + "type": self.agent_type, + } + + @classmethod + def get_agent_class_name(cls) -> str: + """Get the class name for registration purposes.""" + return cls.__name__ + + def __str__(self): + return f"{self.__class__.__name__}(name='{self.name}', type='{self.agent_type}')" + + def __repr__(self): + return self.__str__() diff --git a/projects/agents/agents/helpers.py b/projects/agents/agents/helpers.py new file mode 100644 index 0000000..2e3deac --- /dev/null +++ b/projects/agents/agents/helpers.py @@ -0,0 +1,171 @@ +"""Rate limit handling for HTTP clients with Retry-After support.""" + +import openai +from agents.litellm_startup import litellm_startup +from common.logger import get_logger +from gql import gql +from httpx import AsyncClient, HTTPStatusError +from pydantic_ai.retries import AsyncTenacityTransport, wait_retry_after +from tenacity import AsyncRetrying, retry_if_exception_type, stop_after_attempt, wait_exponential + +logger = get_logger(__name__) + + +def create_rate_limit_client(): + """Create a client that respects Retry-After headers from rate limiting responses.""" + + def validator(response): + """Safely validate response.""" + try: + # Check if response has a status_code attribute + if hasattr(response, "status_code"): + if response.status_code >= 400: + response.raise_for_status() + else: + print(f"WARNING: Response object doesn't have status_code: {type(response)}") + except Exception as e: + print(f"Validator exception: {type(e).__name__}: {e}") + # Only re-raise HTTP errors, not other exceptions + if isinstance(e, HTTPStatusError): + raise + + transport = AsyncTenacityTransport( + controller=AsyncRetrying( + retry=retry_if_exception_type(HTTPStatusError), + wait=wait_retry_after( + fallback_strategy=wait_exponential(multiplier=1, max=60), + max_wait=300, # Don't wait more than 5 minutes + ), + stop=stop_after_attempt(10), + reraise=True, + ), + validate_response=validator, + ) + + return AsyncClient(transport=transport) + + +async def get_litellm_token(): + """Sets up the LiteLLM token.""" + + litellm_token = None + + try: + try: + litellm_token = await litellm_startup() + except RuntimeError as e: + # Handle LiteLLM not being available gracefully + if "LiteLLM API not available" in str(e): + logger.warning("LiteLLM service is not available - continuing with JWT-only") + else: + logger.warning(f"LiteLLM initialization failed: {e}") + return None + except Exception as e: + logger.warning(f"Unexpected error initializing LiteLLM: {e}") + return None + + # Check available models if we have a token + models = [] + if litellm_token: + try: + client = openai.OpenAI(base_url="http://litellm:4000/", api_key=litellm_token) + models = [model.id for model in client.models.list().data] + except Exception as e: + logger.error(f"Error initializing OpenAI client for https://litellm:4000: {e}") + return None + + if models: + return litellm_token + else: + logger.warning("No models available: LLM finding triage disabled", available_models=models) + return None + else: + logger.warning("No LiteLLM token available - only JWT rule-based triage will be available") + return None + + except Exception as e: + logger.error(e, message="Error initializing LiteLLM connection") + return None + + +async def fetch_finding_details(session, finding_id): + """Fetch full details for a finding after receiving its ID from subscription""" + FINDING_DETAILS_QUERY = gql(""" + query FindingDetails($finding_id: bigint!) { + findings_by_pk(finding_id: $finding_id) { + finding_name + category + severity + object_id + origin_type + origin_name + data + raw_data + files_enriched { + path + object_id + } + } + } + """) + + try: + result = await session.execute(FINDING_DETAILS_QUERY, variable_values={"finding_id": finding_id}) + + finding_details = result.get("findings_by_pk") + if not finding_details: + logger.warning(f"No details found for finding ID {finding_id}") + return None + + return finding_details + except Exception as e: + logger.exception(e, message=f"Error fetching details for finding ID {finding_id}") + return None + + +def check_triage_consensus(session, object_id, threshold=3): + """Check if there's a consensus for triage decisions on a file. + + Returns: + dict: {'has_consensus': bool, 'decision': str, 'count': int} if consensus exists + None: if no consensus + """ + TRIAGE_CONSENSUS_QUERY = gql(""" + query TriageConsensus($object_id: uuid!) { + findings(where: {object_id: {_eq: $object_id}}) { + finding_id + finding_triage_histories(where: {automated: {_eq: true}}) { + value + } + } + } + """) + + try: + result = session.execute(TRIAGE_CONSENSUS_QUERY, variable_values={"object_id": object_id}) + + findings = result.get("findings", []) + if not findings: + return None + + decision_counts = {"true_positive": 0, "false_positive": 0} + + for finding in findings: + triage_histories = finding.get("finding_triage_histories", []) + if triage_histories: + # Get the most recent triage decision for this finding + latest_value = triage_histories[0].get("value") + if latest_value in decision_counts: + decision_counts[latest_value] += 1 + + # Check if we have consensus + for decision, count in decision_counts.items(): + if count >= threshold: + logger.info(f"Found triage consensus for object {object_id}: {decision} ({count} findings)") + return {"has_consensus": True, "decision": decision, "count": count} + + return None + + except Exception as e: + logger.exception(e, message=f"Error checking triage consensus for object {object_id}") + return None diff --git a/projects/agents/agents/lib/ICSharpCode.Decompiler.dll b/projects/agents/agents/lib/ICSharpCode.Decompiler.dll new file mode 100644 index 0000000..1ef63a9 Binary files /dev/null and b/projects/agents/agents/lib/ICSharpCode.Decompiler.dll differ diff --git a/projects/agents/agents/lib/ICSharpCode.ILSpyX.dll b/projects/agents/agents/lib/ICSharpCode.ILSpyX.dll new file mode 100644 index 0000000..2c7c50e Binary files /dev/null and b/projects/agents/agents/lib/ICSharpCode.ILSpyX.dll differ diff --git a/projects/agents/agents/lib/Mono.Cecil.dll b/projects/agents/agents/lib/Mono.Cecil.dll new file mode 100644 index 0000000..553498b Binary files /dev/null and b/projects/agents/agents/lib/Mono.Cecil.dll differ diff --git a/projects/agents/agents/litellm_startup.py b/projects/agents/agents/litellm_startup.py new file mode 100644 index 0000000..ed2c1d8 --- /dev/null +++ b/projects/agents/agents/litellm_startup.py @@ -0,0 +1,227 @@ +#!/usr/bin/env python3 + +import asyncio +import os + +import aiohttp +import structlog +from dapr.clients import DaprClient + +# Set up logging +logger = structlog.get_logger(module=__name__) + +# Configuration +LLM_EMAIL = os.getenv("LLM_EMAIL", f"bedrock-chat-service@{os.getenv('EMAIL_DOMAIN', 'local')}") +if not os.getenv("LLM_EMAIL"): + LLM_EMAIL = "nemesis@local" + +MAX_BUDGET = float(os.getenv("MAX_BUDGET", "100.0")) +BUDGET_DURATION = os.getenv("BUDGET_DURATION", "30d") +LITELLM_API_URL = os.getenv("LITELLM_API_URL", "http://litellm:4000") +LITELLM_ADMIN_KEY = os.getenv("LITELLM_ADMIN_KEY") +DAPR_STATE_STORE = os.getenv("DAPR_STATE_STORE", "statestore") +MAX_RETRIES = 2 +RETRY_DELAY = 5 + +# Token key for Dapr state store +TOKEN_KEY = "litellm_token" + + +async def wait_for_litellm() -> bool: + """Wait for LiteLLM API to be available""" + logger.debug("Checking LiteLLM API availability...") + + async with aiohttp.ClientSession() as session: + for attempt in range(1, MAX_RETRIES + 1): + try: + async with session.get( + f"{LITELLM_API_URL}/health", + headers={"Authorization": f"Bearer {LITELLM_ADMIN_KEY}"}, + timeout=aiohttp.ClientTimeout(total=5), + ) as response: + if response.status == 200: + logger.info("LiteLLM API is ready") + return True + except (TimeoutError, aiohttp.ClientError): + pass + + if attempt == MAX_RETRIES: + # Don't use logger.error which might print stack traces + logger.info(f"LiteLLM API not available after {MAX_RETRIES} attempts") + return False + + # Only log every 5th attempt to reduce noise + if attempt % 5 == 0 or attempt == 1: + logger.debug(f"Attempt {attempt}/{MAX_RETRIES}: LiteLLM not ready, continuing...") + await asyncio.sleep(RETRY_DELAY) + + return False + + +async def get_token_from_dapr() -> str | None: + """Retrieve token from Dapr state store""" + try: + logger.info(f"Checking Dapr state store for existing token (key: {TOKEN_KEY})...") + + with DaprClient() as client: + result = client.get_state(DAPR_STATE_STORE, TOKEN_KEY) + + if result.data: + token = result.data.decode("utf-8") + if token: + logger.info("Found existing token in Dapr state store") + return token + + logger.info("No existing token found in Dapr state store") + except Exception as e: + logger.error(f"Unexpected error retrieving token from Dapr: {e}") + + return None + + +async def save_token_to_dapr(token: str) -> bool: + """Save token to Dapr state store""" + try: + logger.info(f"Saving token to Dapr state store (key: {TOKEN_KEY})...") + + with DaprClient() as client: + client.save_state(DAPR_STATE_STORE, TOKEN_KEY, token) + logger.info("Successfully saved token to Dapr state store") + return True + + except Exception as e: + logger.error(f"Unexpected error saving token to Dapr: {e}") + + return False + + +async def validate_token(token: str) -> bool: + """Validate that the token works with LiteLLM""" + try: + logger.info("Validating token...") + + async with aiohttp.ClientSession() as session: + async with session.get( + f"{LITELLM_API_URL}/models", + headers={"Authorization": f"Bearer {token}"}, + timeout=aiohttp.ClientTimeout(total=10), + ) as response: + if response.status == 200: + logger.info("Token validation successful") + return True + else: + logger.error(f"Token validation failed: {response.status}") + response_text = await response.text() + logger.error(f"Response: {response_text}") + + except (TimeoutError, aiohttp.ClientError) as e: + logger.error(f"Token validation failed: {e}") + + return False + + +async def create_new_token() -> str | None: + """Create a new user/token with budget limit""" + logger.info("Attempting to create new chat service user with budget limit...") + + async with aiohttp.ClientSession() as session: + # Try to create new user with budget limit first + try: + create_payload = { + "user_id": LLM_EMAIL, + "user_email": LLM_EMAIL, + "max_budget": MAX_BUDGET, + "budget_duration": BUDGET_DURATION, + } + logger.info(f"create_payload: {create_payload}") + + async with session.post( + f"{LITELLM_API_URL}/user/new", + json=create_payload, + headers={"Authorization": f"Bearer {LITELLM_ADMIN_KEY}", "Content-Type": "application/json"}, + timeout=aiohttp.ClientTimeout(total=10), + ) as response: + if response.status == 200: + response_data = await response.json() + token = response_data.get("key") + if token: + logger.info(f"Successfully created new chat service user with budget limit of ${MAX_BUDGET}") + return token + + # If user creation failed, try to generate key for existing user + logger.info("User creation failed (likely already exists), generating new token for existing user...") + + key_payload = {"user_id": LLM_EMAIL} + async with session.post( + f"{LITELLM_API_URL}/key/generate", + json=key_payload, + headers={"Authorization": f"Bearer {LITELLM_ADMIN_KEY}", "Content-Type": "application/json"}, + timeout=aiohttp.ClientTimeout(total=10), + ) as response: + if response.status == 200: + response_data = await response.json() + token = response_data.get("key") + if token: + logger.info("Generated new API key for existing user") + return token + + logger.error(f"Failed to generate API key: {response.status}") + response_text = await response.text() + logger.error(f"Response: {response_text}") + + except (TimeoutError, aiohttp.ClientError) as e: + logger.error(f"Failed to create token: {e}") + + return None + + +async def litellm_startup() -> str: + """ + LiteLLM startup function that provisions a budget-limited token. + Returns the token string or raises an exception if provisioning fails. + """ + logger.debug("Starting LiteLLM token provisioning...") + + # Validate required environment variables + if not LITELLM_ADMIN_KEY: + raise ValueError("LITELLM_ADMIN_KEY environment variable is required") + + # Wait for LiteLLM to be ready + if not await wait_for_litellm(): + raise RuntimeError("LiteLLM API not available") + + # Try to get existing token from Dapr state store + existing_token = await get_token_from_dapr() + + if existing_token: + # Validate the existing token + if await validate_token(existing_token): + logger.info("Using existing token from Dapr state store") + logger.info(f"User: {LLM_EMAIL}") + logger.info(f"Budget: ${MAX_BUDGET}") + logger.info(f"Token: {existing_token}") + return existing_token + else: + logger.warning("Existing token is invalid, creating new one...") + + # Create new token + new_token = await create_new_token() + + if not new_token: + raise RuntimeError("Failed to provision budget-limited token") + + # Validate the new token + if not await validate_token(new_token): + raise RuntimeError("New token validation failed") + + # Save token to Dapr state store + if not await save_token_to_dapr(new_token): + logger.warning("Failed to save token to Dapr state store") + # Continue anyway - token still works + + logger.info("Budget-limited token provisioned successfully!") + logger.info(f"User: {LLM_EMAIL}") + logger.info(f"Budget: ${MAX_BUDGET}") + logger.info(f"Token: {new_token}") + + return new_token diff --git a/projects/agents/agents/logger.py b/projects/agents/agents/logger.py new file mode 100644 index 0000000..d25b823 --- /dev/null +++ b/projects/agents/agents/logger.py @@ -0,0 +1,252 @@ +import logging +import os +from importlib.metadata import version + +import colorlog +import structlog +from opentelemetry import trace +from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter +from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter as HTTPOTLPSpanExporter +from opentelemetry.sdk.resources import Resource +from opentelemetry.sdk.trace import TracerProvider +from opentelemetry.sdk.trace.export import BatchSpanProcessor, SimpleSpanProcessor +from opentelemetry.semconv._incubating.attributes import service_attributes + +WORKFLOW_RUNTIME_LOG_LEVEL = os.getenv("WORKFLOW_RUNTIME_LOG_LEVEL", "WARNING") +WORKFLOW_CLIENT_LOG_LEVEL = os.getenv("WORKFLOW_CLIENT_LOG_LEVEL", "WARNING") + + +# Create a processor to add worker ID +def add_worker_id(logger, method_name, event_dict): + try: + import multiprocessing + + event_dict["worker_id"] = multiprocessing.current_process().name + except (ImportError, AttributeError): + event_dict["worker_id"] = "unknown" + return event_dict + + +def configure_logging(): + log_level = os.getenv("LOG_LEVEL", "INFO").upper() + + # Validate the log level + numeric_level = getattr(logging, log_level, None) + if not isinstance(numeric_level, int): + raise ValueError(f"Invalid log level: {log_level}") + + # Set up colorlog handler + handler = colorlog.StreamHandler() + + # Create a ProcessorFormatter for structlog that includes color formatting + formatter = structlog.stdlib.ProcessorFormatter( + processor=structlog.dev.ConsoleRenderer(colors=True), + foreign_pre_chain=[ + structlog.stdlib.add_log_level, + structlog.stdlib.add_logger_name, + ], + ) + + # Set the formatter once + handler.setFormatter(formatter) + + # Configure root logger + root_logger = logging.getLogger() + # root_logger.setLevel(logging.DEBUG) + root_logger.setLevel(numeric_level) + + # Clear any existing handlers to prevent double logging + root_logger.handlers = [] + root_logger.addHandler(handler) + + # Configure specific loggers + logging.getLogger("urllib3.connectionpool").setLevel(logging.WARN) + logging.getLogger("asyncio").setLevel(logging.WARN) + logging.getLogger("opentelemetry.sdk.trace").setLevel(logging.ERROR) + logging.getLogger("httpx").setLevel(logging.WARN) + logging.getLogger("httpcore").setLevel(logging.WARN) + logging.getLogger("httpcore.connection").setLevel(logging.WARN) + logging.getLogger("httpcore.http11").setLevel(logging.WARN) + logging.getLogger("openai").setLevel(logging.WARN) + logging.getLogger("openai._base_client").setLevel(logging.WARN) + logging.getLogger("anthropic").setLevel(logging.WARN) + logging.getLogger("websockets").setLevel(logging.WARN) + logging.getLogger("websockets.client").setLevel(logging.WARN) + logging.getLogger("gql").setLevel(logging.WARN) + logging.getLogger("gql.transport").setLevel(logging.WARN) + logging.getLogger("gql.transport.websockets").setLevel(logging.WARN) + logging.getLogger("gql.dsl").setLevel(logging.WARN) + + # Configure structlog to use the same handler + structlog.configure( + processors=[ + structlog.stdlib.filter_by_level, + # add_worker_id, + structlog.stdlib.add_log_level, + structlog.processors.format_exc_info, + structlog.stdlib.ProcessorFormatter.wrap_for_formatter, + ], + logger_factory=structlog.stdlib.LoggerFactory(), + wrapper_class=structlog.stdlib.BoundLogger, + cache_logger_on_first_use=False, + ) + + return handler, formatter + + +def get_instance_id(): + hostname = os.getenv("HOSTNAME", "unknown-host") # Docker: container ID, K8s: pod name + pid = os.getpid() # Uvicorn/Gunicorn worker PID + return f"{hostname}-{pid}" + + +def get_tracer(tracer_name: str, otel_exporter_enabled: bool = True): + """ + Initialize and return an OpenTelemetry tracer with the specified name. + + This function creates a TracerProvider with service metadata and configures + trace export based on the NEMESIS_MONITORING environment variable. When monitoring + is enabled, spans are exported to an OTLP endpoint (e.g., Jaeger). Otherwise, + tracing is still active but spans are not exported. + + Args: + tracer_name: The name to identify this tracer instance. This typically + corresponds to the module or service name. + otel_exporter_enabled: Currently unused parameter. Export behavior is + controlled by the NEMESIS_MONITORING env var instead. + + Returns: + A configured OpenTelemetry Tracer instance that can be used to create spans. + + Environment Variables: + NEMESIS_MONITORING: Set to "enabled" to export traces to OTLP endpoint + OTEL_EXPORTER_OTLP_TRACES_ENDPOINT_INSECURE: Set to "true" for insecure + connections (default: "true") + HOSTNAME: Used to construct the service instance ID + """ + + resource = Resource.create( + { + service_attributes.SERVICE_NAME: "agents", + service_attributes.SERVICE_NAMESPACE: "nemesis", + service_attributes.SERVICE_VERSION: version("agents"), + service_attributes.SERVICE_INSTANCE_ID: get_instance_id(), + } + ) + + # Only setup OTLP exporter if monitoring is enabled + if os.getenv("NEMESIS_MONITORING", "").lower() == "enabled": + otlp_exporter = OTLPSpanExporter( + insecure=os.getenv("OTEL_EXPORTER_OTLP_TRACES_ENDPOINT_INSECURE", "true").lower() == "true", + ) + + trace_provider = TracerProvider(resource=resource) + span_processor = BatchSpanProcessor(otlp_exporter) + trace_provider.add_span_processor(span_processor) + trace.set_tracer_provider(trace_provider) + else: + trace_provider = TracerProvider(resource=resource) + trace.set_tracer_provider(trace_provider) + + return trace_provider.get_tracer(tracer_name) + + +# Global storage for agent metadata +_agent_metadata = {} + + +def set_agent_metadata(agent_name: str, **kwargs): + """Set metadata that will be added to the next agent span""" + global _agent_metadata + _agent_metadata = {"agent_name": agent_name, **kwargs} + + +def setup_phoenix_llm_tracing(): + """ + Setup Phoenix tracing ONLY for LLM calls - adds Phoenix exporter to existing tracer. + """ + if os.getenv("PHOENIX_ENABLED", "false").lower() == "true": + logger = structlog.get_logger(__name__) + logger.info("Phoenix enabled, setting up LLM tracing") + + try: + # Import Phoenix/OpenInference components + import json + + from openinference.instrumentation.pydantic_ai import OpenInferenceSpanProcessor, is_openinference_span + from openinference.semconv.trace import SpanAttributes + from opentelemetry.sdk.trace import Span + + # Custom processor that enhances Pydantic AI spans with our metadata + class CustomPydanticAIProcessor(OpenInferenceSpanProcessor): + def on_start(self, span: Span, parent_context=None): + """Modify span when it starts""" + super().on_start(span, parent_context) + + # Check if this is a Pydantic AI span + if hasattr(span, "name") and "agent" in span.name.lower(): + global _agent_metadata + if _agent_metadata: + # Update span name if agent_name provided + if "agent_name" in _agent_metadata: + span.update_name(_agent_metadata["agent_name"]) + # Also set the AGENT_NAME attribute using OpenInference convention + span.set_attribute(SpanAttributes.AGENT_NAME, _agent_metadata["agent_name"]) + + # Set session ID if file_path is provided (use file path as session) + if "file_path" in _agent_metadata: + span.set_attribute(SpanAttributes.SESSION_ID, _agent_metadata["file_path"]) + + # Add other metadata using METADATA attribute (OpenInference convention) + metadata = { + k: v for k, v in _agent_metadata.items() if k not in ["agent_name"] and v is not None + } + if metadata: + span.set_attribute(SpanAttributes.METADATA, json.dumps(metadata)) + + # Add tags if provided + if "tags" in _agent_metadata and isinstance(_agent_metadata["tags"], list): + span.set_attribute(SpanAttributes.TAG_TAGS, _agent_metadata["tags"]) + + # Get the existing tracer provider (already set up for Jaeger) + existing_provider = trace.get_tracer_provider() + + if not isinstance(existing_provider, TracerProvider): + logger.warning("No existing TracerProvider found, Phoenix tracing not enabled") + return False + + # Phoenix exporter using HTTP OTLP + phoenix_endpoint = os.getenv("PHOENIX_ENDPOINT", "http://phoenix:6006/v1/traces") + phoenix_exporter = HTTPOTLPSpanExporter(endpoint=phoenix_endpoint) + + # Add our custom processor to enhance spans + existing_provider.add_span_processor(CustomPydanticAIProcessor(span_filter=is_openinference_span)) + + # Create a filtering processor that ONLY sends Pydantic AI spans to Phoenix + class PhoenixLLMOnlyProcessor(SimpleSpanProcessor): + """Only send Pydantic AI LLM spans to Phoenix, not healthchecks""" + + def on_end(self, span): + # Only export if this is a Pydantic AI span (has OpenInference attributes) + if is_openinference_span(span): + super().on_end(span) + + # Add Phoenix exporter with strict LLM-only filtering + existing_provider.add_span_processor(PhoenixLLMOnlyProcessor(phoenix_exporter)) + + logger.info("Phoenix LLM tracing with custom processor added", endpoint=phoenix_endpoint) + + logger.info("Phoenix LLM tracing successfully initialized") + return True + + except ImportError as e: + logger.error(f"Failed to import Phoenix instrumentation: {e}") + logger.error("Make sure openinference-instrumentation-pydantic-ai is installed") + return False + except Exception as e: + logger.exception("Failed to setup Phoenix tracing", error=str(e)) + return False + + else: + logger.debug("Phoenix tracing disabled (PHOENIX_ENABLED not set to 'true')") + return False diff --git a/projects/agents/agents/main.py b/projects/agents/agents/main.py new file mode 100644 index 0000000..6d922ec --- /dev/null +++ b/projects/agents/agents/main.py @@ -0,0 +1,765 @@ +# projects/agents/agents/main.py +import asyncio +import json +import os +from contextlib import asynccontextmanager +from datetime import datetime + +import asyncpg +import structlog +from agents.agent_manager import agent_manager +from agents.helpers import check_triage_consensus, fetch_finding_details, get_litellm_token +from agents.model_manager import ModelManager +from agents.phoenix_cost_sync import sync_pricing_to_phoenix +from agents.prompt_manager import PromptManager +from agents.schemas import NoseyParkerData, TriageCategory, TriageRequest, TriageResult +from agents.tasks.credential_analyzer import analyze_credentials +from agents.tasks.dotnet_analyzer import analyze_dotnet_assembly +from agents.tasks.summarizer import summarize_text +from agents.tasks.translate import translate_text +from common.db import get_postgres_connection_str +from dapr.clients import DaprClient + +# from dapr.ext.fastapi import DaprApp # needed if we're doing pub/sub +from dapr.ext.workflow import DaprWorkflowClient, DaprWorkflowContext, WorkflowRuntime +from dapr.ext.workflow.logger.options import LoggerOptions +from dapr.ext.workflow.workflow_activity_context import WorkflowActivityContext +from fastapi import FastAPI +from gql import Client, gql +from gql.transport.requests import RequestsHTTPTransport +from gql.transport.websockets import WebsocketsTransport + +from .logger import WORKFLOW_CLIENT_LOG_LEVEL, WORKFLOW_RUNTIME_LOG_LEVEL, configure_logging + +configure_logging() +logger = structlog.get_logger(module=__name__) + +db_pool = None +workflow_client: DaprWorkflowClient = None + +litellm_model = "default" + +workflow_runtime = WorkflowRuntime(logger_options=LoggerOptions(log_level=WORKFLOW_RUNTIME_LOG_LEVEL)) + +with DaprClient() as client: + secret = client.get_secret(store_name="nemesis-secret-store", key="HASURA_ADMIN_SECRET") + hasura_admin_secret = secret.secret["HASURA_ADMIN_SECRET"] + logger.info("[agents] HASURA_ADMIN_SECRET retrieved") + + +# Configuration +dapr_port = os.getenv("DAPR_HTTP_PORT", 3500) +gotenberg_url = f"http://localhost:{dapr_port}/v1.0/invoke/gotenberg/method/forms/libreoffice/convert" + + +@asynccontextmanager +async def lifespan(app: FastAPI): + """Lifespan manager for FastAPI - handles startup and shutdown events""" + global workflow_runtime, workflow_client, litellm_model + try: + # setup the LiteLLM connection/token (if available) + litellm_token = await get_litellm_token() + logger.debug(f"LiteLLM token: {litellm_token}") + + # Initialize OpenTelemetry tracer provider first (needed for Phoenix) + from agents.logger import get_tracer + + tracer = get_tracer("agents") + logger.debug("OpenTelemetry tracer initialized") + + # Initialize ModelManager if we have a model + # this is so we can pull the custom pricing and sync it back to Phoenix for tracking + if litellm_token: + ModelManager.initialize(litellm_token, litellm_model) + logger.info("ModelManager initialized", litellm_model=litellm_model, litellm_token=litellm_token) + + # Sync model pricing to Phoenix if we can reach LiteLLM and have Phoenix DB configured + phoenix_db_url = os.getenv("PHOENIX_SQL_DATABASE_URL") + if phoenix_db_url: + try: + pricing_synced = await sync_pricing_to_phoenix(litellm_model) + if pricing_synced: + logger.info(f"Successfully synced pricing for model '{litellm_model}' to Phoenix") + else: + logger.debug( + f"Could not sync pricing for model '{litellm_model}' to Phoenix (LiteLLM may be unavailable)" + ) + except Exception as e: + logger.debug(f"Could not sync pricing to Phoenix: {e}") + + # Initialize PromptManager + PromptManager.initialize() + logger.info("PromptManager initialized") + + # Load and register agents + agent_manager.load_agents() + agent_manager.register_activities(workflow_runtime) + logger.info("Agent manager initialized and activities registered") + + # start the workflow runtime + workflow_runtime.start() + logger.debug("Started Dapr runtime") + + # Initialize workflow client + workflow_client = DaprWorkflowClient( + logger_options=LoggerOptions(log_level=WORKFLOW_CLIENT_LOG_LEVEL), + ) + logger.debug("Started Dapr workflow client") + + # Now that everything is initialized, save agent prompts to database + agent_manager.initialize_agent_prompts() + logger.info("Agent prompts initialized in database") + + # Start findings subscription + asyncio.create_task(handle_findings_subscription()) + logger.debug("Started findings subscription handler") + + except Exception as e: + logger.exception(e, message="Error initializing service") + raise + + yield + + # Cleanup on shutdown + try: + logger.info("Starting service shutdown cleanup") + + # PromptManager cleanup no longer needed with sync operations + + # Shutdown workflow runtime + if workflow_runtime: + workflow_runtime.shutdown() + logger.info("Workflow runtime shutdown completed") + + except Exception as e: + logger.warning("Error during service shutdown", error=str(e)) + + +app = FastAPI(lifespan=lifespan) + +# Instrument FastAPI with OpenTelemetry if monitoring is enabled +# dapr_app = DaprApp(app) # for pub/sub, if used + + +# Workflow Activities + + +@workflow_runtime.activity +def check_consensus_activity(ctx: WorkflowActivityContext, activity_input: dict): + """Check if there's a triage consensus for the file associated with this finding.""" + try: + object_id = activity_input["object_id"] + threshold = activity_input.get("threshold", 3) + + transport = RequestsHTTPTransport( + url="http://hasura:8080/v1/graphql", + headers={"x-hasura-admin-secret": hasura_admin_secret}, + ) + + with Client( + transport=transport, + fetch_schema_from_transport=True, + ) as session: + consensus = check_triage_consensus(session, object_id, threshold) + return consensus + + except Exception as e: + logger.exception(e, message=f"Error checking consensus for object {object_id}") + return None + + +@workflow_runtime.activity +def insert_triage_result(ctx: WorkflowActivityContext, activity_input: dict): + """Insert triage result into Hasura via GraphQL mutation.""" + try: + triage_result = activity_input["triage_result"] + finding_id = activity_input["finding_id"] + + INSERT_TRIAGE = gql(""" + mutation InsertTriage($finding_id: bigint!, $username: String!, $value: String!, $explanation: String!, $confidence: Float!, $true_positive_context: String, $automated: Boolean!) { + insert_findings_triage_history_one(object: { + finding_id: $finding_id, + username: $username, + value: $value, + explanation: $explanation, + confidence: $confidence, + true_positive_context: $true_positive_context, + automated: $automated + }) { + id + } + } + """) + + transport = RequestsHTTPTransport( + url="http://hasura:8080/v1/graphql", + headers={"x-hasura-admin-secret": hasura_admin_secret}, + ) + + with Client( + transport=transport, + fetch_schema_from_transport=True, + ) as session: + variable_values = { + "finding_id": finding_id, + "username": "automatation_agent", + "value": triage_result["decision"], + "explanation": triage_result["explanation"], + "confidence": triage_result["confidence"], + "automated": True, + } + + # Add true_positive_context if it exists + if "true_positive_context" in triage_result: + variable_values["true_positive_context"] = triage_result["true_positive_context"] + else: + variable_values["true_positive_context"] = None + + session.execute(INSERT_TRIAGE, variable_values=variable_values) + + logger.debug( + f"Inserted triage result for finding {finding_id}", + decision=triage_result["decision"], + explanation=triage_result["explanation"], + confidence=triage_result["confidence"], + ) + + except Exception as e: + logger.exception(e, message=f"Error inserting triage result for finding {finding_id}") + raise + + +def extract_summary_from_triage_request(triage_request: TriageRequest) -> str | None: + """ + Extract the summary from a TriageRequest's data field. + + Args: + triage_request: The triage request containing finding data + + Returns: + The summary string if found, None otherwise + """ + if not triage_request.data or len(triage_request.data) == 0: + return None + + first_data = triage_request.data[0] + try: + if isinstance(first_data, str): + first_data = json.loads(first_data) + if isinstance(first_data, dict) and "metadata" in first_data: + return first_data["metadata"].get("summary") + except json.JSONDecodeError: + logger.error("Failed to parse finding data as JSON") + + return None + + +def handle_jwt_triage(ctx: DaprWorkflowContext, triage_request: TriageRequest, summary: str): + """ + Handle JWT finding triage using rule-based validation. + + Checks if the summary contains JWT-specific markers and processes the finding + with rule-based validation instead of LLM triage. + + Args: + ctx: Dapr workflow context + finding_id: The finding ID being triaged + summary: The finding summary text + file_path: Path to the file containing the JWT + + Returns: + True if this is a JWT finding that was processed, False otherwise + + Yields: + Workflow activity calls for JWT validation and result insertion + """ + # Check if it's a JWT finding + if triage_request.origin_name != "noseyparker": + return False + + if not triage_request.raw_data: + if not ctx.is_replaying: + logger.warning(f"No raw_data available for noseyparker finding {triage_request.finding_id}") + return False + + try: + noseyparker_data = NoseyParkerData(**triage_request.raw_data) + except Exception as e: + if not ctx.is_replaying: + logger.error(f"Failed to parse noseyparker data for finding {triage_request.finding_id}: {e}") + return False + + # Check if this is specifically a JWT finding + if noseyparker_data.match.rule_name != "JSON Web Token (base64url-encoded)": + return False + + if not ctx.is_replaying: + logger.info(f"Processing JWT finding {triage_request.finding_id} with rule-based triage") + + jwt_wrapper = agent_manager.get_wrapper_function("jwt") + jwt_result = yield ctx.call_activity( + jwt_wrapper, + input={ + "summary": summary, + "file_path": triage_request.file_path, + }, + ) + + result = TriageResult( + finding_id=triage_request.finding_id, + decision=jwt_result["decision"], + explanation=jwt_result.get("explanation", "JWT validation completed"), + confidence=1.0, + true_positive_context=None, + success=True, + ) + + yield ctx.call_activity( + insert_triage_result, + input={ + "finding_id": triage_request.finding_id, + "triage_result": result.model_dump(), + }, + ) + + return True + + +@workflow_runtime.workflow +def finding_triage_workflow(ctx: DaprWorkflowContext, workflow_input: dict): + """Main workflow for triaging findings.""" + try: + # Parse workflow_input as TriageRequest + triage_request = TriageRequest(**workflow_input) + finding_id = triage_request.finding_id + object_id = triage_request.object_id + + if not ctx.is_replaying: + logger.info(f"Starting triage workflow for finding {finding_id}") + + # Step 1: Extract out the finding summary + summary = extract_summary_from_triage_request(triage_request) + + if not summary: + logger.warning(f"No summary found for finding {finding_id}") + result = TriageResult( + finding_id=finding_id, + decision=TriageCategory.NOT_TRIAGED, + explanation="No summary available", + confidence=0.0, + true_positive_context=None, + success=False, + ) + + yield ctx.call_activity( + insert_triage_result, + input={ + "finding_id": finding_id, + "triage_result": result.model_dump(), + }, + ) + return + + file_path = triage_request.file_path + + # Step 2: Check if it's a JWT finding and handle non-LLM triage + jwt_handled = yield from handle_jwt_triage(ctx, triage_request, summary) + if jwt_handled: + return + + # Step 3: Check for finding consensus: + # If we hit this number of the same triage values for the same file, all future findings get that value + consensus_threshold = int(os.getenv("TRIAGE_CONSENSUS_THRESHOLD", 3)) + consensus = yield ctx.call_activity( + check_consensus_activity, + input={ + "object_id": object_id, + "threshold": consensus_threshold, + }, + ) + + if consensus and consensus.get("has_consensus"): + if not ctx.is_replaying: + logger.info( + f"Using consensus triage for finding {finding_id}: {consensus['decision']} based on {consensus['count']} findings" + ) + + result = TriageResult( + finding_id=finding_id, + decision=consensus["decision"], + explanation=f"Determined by existing {consensus['decision'].replace('_', ' ')} consensus for this file ({consensus['count']} findings)", + confidence=1.0, + true_positive_context=None, + success=True, + ) + + yield ctx.call_activity( + insert_triage_result, + input={ + "finding_id": finding_id, + "triage_result": result.model_dump(), + }, + ) + return + + # Step 4: Use LLM validation agent if ModelManager has a model available + if ModelManager.is_available(): + if not ctx.is_replaying: + logger.info(f"Processing finding {finding_id} with AI validation") + + validate_wrapper = agent_manager.get_wrapper_function("validate") + validation_result = yield ctx.call_activity( + validate_wrapper, + input={ + "file_path": file_path, + "finding_id": finding_id, + "object_id": object_id, + "summary": summary, + }, + ) + if not ctx.is_replaying: + logger.debug(f"validation_result: {validation_result}") + + true_positive_context = "" + if validation_result["decision"].lower() == "true_positive": + true_positive_context = validation_result["true_positive_context"] + + result = TriageResult( + finding_id=finding_id, + decision=validation_result["decision"], + explanation=validation_result["explanation"], + confidence=validation_result["confidence"], + true_positive_context=true_positive_context, + success=True, + ) + + yield ctx.call_activity( + insert_triage_result, input={"finding_id": finding_id, "triage_result": result.model_dump()} + ) + return + + else: + if not ctx.is_replaying: + logger.warning("No LLM available for finding triage", finding_id=finding_id) + + return + + except Exception as e: + logger.exception(e, message=f"Error in finding triage workflow for finding {finding_id}") + + +async def handle_findings_subscription(): + """Sets up and handles subscription to findings table in Hasura""" + + SUBSCRIPTION = gql(""" + subscription NewFindingIds { + findings( + where: { + triage_id: {_is_null: true}, + category: {_nin: ["extracted_hash", "yara_match", "extracted_data"]}, + finding_triage_histories_aggregate: {count: {predicate: {_eq: 0}}} + }, + order_by: {created_at: desc} + ) { + finding_id + } + } + """) + + while True: + try: + transport = WebsocketsTransport( + url="ws://hasura:8080/v1/graphql", + headers={"x-hasura-admin-secret": hasura_admin_secret}, + connect_args={"max_size": 20 * 1024 * 1024}, + ) + + async with Client( + transport=transport, + fetch_schema_from_transport=True, + ) as session: + async for result in session.subscribe(SUBSCRIPTION): + if result is None: + continue + + findings_list = result.get("findings", []) + + for finding in findings_list: + finding_id = finding["finding_id"] + logger.info(f"Processing finding ID: {finding_id}") + + finding_details = await fetch_finding_details(session, finding_id) + + if not finding_details: + continue + + file_path = finding_details["files_enriched"]["path"] + object_id = finding_details["files_enriched"]["object_id"] + + try: + # Convert data objects to JSON strings if they're dicts + data_strings = [] + if finding_details.get("data"): + for item in finding_details["data"]: + if isinstance(item, dict): + data_strings.append(json.dumps(item)) + else: + data_strings.append(str(item)) + + triage_request = TriageRequest( + finding_id=finding_id, + finding_name=finding_details["finding_name"], + category=finding_details["category"], + severity=finding_details["severity"], + object_id=object_id, + origin_type=finding_details["origin_type"], + origin_name=finding_details["origin_name"], + data=data_strings, + raw_data=finding_details["raw_data"], + file_path=file_path, + ) + + instance_id = f"agents-triage-{finding_id}" + workflow_client.schedule_new_workflow( + workflow=finding_triage_workflow, + instance_id=instance_id, + input=triage_request.model_dump(), + ) + + try: + state = await asyncio.wait_for( + asyncio.to_thread( + workflow_client.wait_for_workflow_completion, + instance_id=instance_id, + timeout_in_seconds=3 * 60, # internal timeout in workflow client + ), + timeout=3 * 60 + 10, # small outer buffer, in seconds + ) + + if not state: + logger.error("Workflow not found!", instance_id=instance_id) + elif state.runtime_status.name == "COMPLETED": + logger.debug("Workflow completed", finding_id=finding_id, instance_id=instance_id) + else: + logger.warning(f"Workflow failed! Status: {state.runtime_status.name}", instance_id=instance_id) + + except TimeoutError: + logger.error("Workflow client timed out internally", instance_id=instance_id) + except Exception as e: + logger.exception(e, message=f"Error waiting for workflow completion for {finding_id}") + + except Exception as e: + logger.exception(e, message=f"Error running workflow for finding {finding_id}") + + del finding_details + + except Exception as e: + logger.exception(e, message="Error in findings subscription, reconnecting in 5 seconds...") + await asyncio.sleep(5) + + +@app.get("/agents/metadata") +async def get_agents_metadata(): + """Get metadata for all available agents.""" + try: + # Run in thread pool to avoid blocking event loop + agents = await asyncio.to_thread(agent_manager.get_agent_metadata) + return {"agents": agents, "total_count": len(agents), "timestamp": datetime.now().isoformat()} + except Exception as e: + logger.exception(e, message="Error getting agent metadata") + return {"agents": [], "total_count": 0, "error": str(e), "timestamp": datetime.now().isoformat()} + + +@app.get("/agents/spend-data") +async def get_llm_spend_data(): + """Get LiteLLM spend and token usage data.""" + try: + postgres_connection_url = get_postgres_connection_str() + # Modify connection URL to connect to litellm database instead of enrichment + litellm_connection_string = postgres_connection_url.replace("/enrichment", "/litellm") + + # Connect to the litellm database and fetch spend data + conn = await asyncpg.connect(litellm_connection_string) + try: + query = """ + SELECT + COALESCE(SUM(spend), 0) as total_spend, + COALESCE(SUM(total_tokens), 0) as total_tokens, + COALESCE(SUM(prompt_tokens), 0) as total_prompt_tokens, + COALESCE(SUM(completion_tokens), 0) as total_completion_tokens, + COUNT(*) as total_requests + FROM "LiteLLM_SpendLogs" + """ + result = await conn.fetchrow(query) + import pprint + + pprint.pprint(result) + + return { + "total_spend": float(result["total_spend"]), + "total_tokens": int(result["total_tokens"]), + "total_prompt_tokens": int(result["total_prompt_tokens"]), + "total_completion_tokens": int(result["total_completion_tokens"]), + "total_requests": int(result["total_requests"]), + "timestamp": datetime.now().isoformat(), + } + finally: + await conn.close() + + except Exception as e: + logger.exception(e, message="Error getting LLM spend data") + return { + "total_spend": 0.0, + "total_tokens": 0, + "total_prompt_tokens": 0, + "total_completion_tokens": 0, + "total_requests": 0, + "error": str(e), + "timestamp": datetime.now().isoformat(), + } + + +async def _run_summarization_task(object_id: str): + """Background task for text summarization.""" + try: + mock_ctx = type("MockContext", (), {})() + result = await asyncio.to_thread(summarize_text, mock_ctx, {"object_id": object_id}) + logger.info("Text summarization completed", object_id=object_id, success=result.get("success")) + except Exception as e: + logger.exception(e, message="Error in background text summarization", object_id=object_id) + + +@app.post("/agents/text_summarizer") +async def run_text_summarizer(request: dict): + """Run text summarization on a file (async, non-blocking).""" + try: + object_id = request.get("object_id") + if not object_id: + return {"success": False, "error": "object_id is required"} + + # Start background task + asyncio.create_task(_run_summarization_task(object_id)) + + return {"success": True, "message": "Text summarization started in background"} + + except Exception as e: + logger.exception(e, message="Error starting text summarizer") + return {"success": False, "error": str(e)} + + +@app.post("/agents/llm_credential_analysis") +async def run_credential_analysis(request: dict): + """Run credential analysis on a file (async, non-blocking).""" + try: + object_id = request.get("object_id") + if not object_id: + return {"success": False, "error": "object_id is required"} + + # Create a mock workflow context for compatibility + mock_ctx = type("MockContext", (), {})() + + # Run in background task - don't wait for completion + asyncio.create_task( + asyncio.to_thread(analyze_credentials, mock_ctx, {"object_id": object_id}) + ) + + return {"success": True, "message": "Credential analysis started in background"} + + except Exception as e: + logger.exception(e, message="Error starting credential analysis") + return {"success": False, "error": str(e)} + + +@app.post("/agents/dotnet_analysis") +async def run_dotnet_analysis(request: dict): + """Run .NET assembly analysis on a file (async, non-blocking).""" + try: + object_id = request.get("object_id") + if not object_id: + return {"success": False, "error": "object_id is required"} + + # Create a mock workflow context for compatibility + mock_ctx = type("MockContext", (), {})() + + # Run in background task - don't wait for completion + asyncio.create_task( + asyncio.to_thread(analyze_dotnet_assembly, mock_ctx, {"object_id": object_id}) + ) + + return {"success": True, "message": ".NET analysis started in background"} + + except Exception as e: + logger.exception(e, message="Error starting .NET analysis") + return {"success": False, "error": str(e)} + + +@app.post("/agents/translate") +async def run_translation(request: dict): + """Run text translation on a file (async, non-blocking).""" + try: + object_id = request.get("object_id") + if not object_id: + return {"success": False, "error": "object_id is required"} + + target_language = request.get("target_language", "English") + + # Create a mock workflow context for compatibility + mock_ctx = type("MockContext", (), {})() + + # Run in background task - don't wait for completion + asyncio.create_task( + asyncio.to_thread(translate_text, mock_ctx, {"object_id": object_id, "target_language": target_language}) + ) + + return {"success": True, "message": f"Translation to {target_language} started in background"} + + except Exception as e: + logger.exception(e, message="Error starting translation") + return {"success": False, "error": str(e)} + + +@app.post("/agents/report_generator") +def run_report_generator(request: dict): + """Generate LLM-based risk assessment report.""" + try: + report_data = request.get("report_data") + if not report_data: + return {"success": False, "error": "report_data is required"} + + report_type = request.get("report_type", "source") + source_name = request.get("source_name", "Unknown") + max_tokens = request.get("max_tokens", 150000) + + # Create a mock workflow context for compatibility + mock_ctx = type("MockContext", (), {})() + + from agents.tasks.reporting_agent import generate_report + + result = generate_report( + mock_ctx, + { + "report_data": report_data, + "report_type": report_type, + "source_name": source_name, + "max_tokens": max_tokens, + }, + ) + return result + + except Exception as e: + logger.exception(e, message="Error running report generator") + return {"success": False, "error": str(e)} + + +@app.api_route("/healthz", methods=["GET", "HEAD"]) +async def health_check(): + """Health check endpoint.""" + try: + if not workflow_runtime or not workflow_client: + return {"status": "unhealthy", "error": "Workflow runtime not initialized"} + + return {"status": "healthy"} + + except Exception as e: + logger.exception(e, message="Health check failed") + return {"status": "unhealthy", "error": str(e)} diff --git a/projects/agents/agents/model_manager.py b/projects/agents/agents/model_manager.py new file mode 100644 index 0000000..bc11aa7 --- /dev/null +++ b/projects/agents/agents/model_manager.py @@ -0,0 +1,80 @@ +"""Centralized model management for all agent activities.""" + +import structlog +from agents.helpers import create_rate_limit_client +from agents.logger import setup_phoenix_llm_tracing +from pydantic_ai.models.openai import OpenAIModel +from pydantic_ai.providers.openai import OpenAIProvider + +logger = structlog.get_logger(__name__) + + +class ModelManager: + """Singleton manager for LLM model instances used across all activities.""" + + _model: OpenAIModel | None = None + _token: str | None = None + _model_name: str | None = None + _base_url: str = "http://litellm:4000/" + _instrumentation_enabled: bool = False + + @classmethod + def initialize(cls, token: str, model_name: str = "default") -> None: + """ + Initialize the model manager with LiteLLM credentials. + Called once during application startup in lifespan(). + + Args: + token: LiteLLM API token + model_name: Name of the model to use (default: "default") + """ + cls._token = token + cls._model_name = model_name + cls._model = None # Reset model to force recreation with new config + + # Setup Phoenix tracing for LLM calls if enabled + cls._instrumentation_enabled = setup_phoenix_llm_tracing() + + logger.info( + f"ModelManager initialized with model: {model_name}", + phoenix_enabled=cls._instrumentation_enabled + ) + + @classmethod + def get_model(cls) -> OpenAIModel | None: + """ + Get the shared model instance, creating it if necessary. + + Returns: + OpenAIModel instance or None if not initialized + """ + if not cls._token: + logger.warning("ModelManager not initialized - no token available") + return None + + if not cls._model: + try: + cls._model = OpenAIModel( + model_name=cls._model_name, + provider=OpenAIProvider( + base_url=cls._base_url, + api_key=cls._token, + http_client=create_rate_limit_client() + ) + ) + logger.info(f"Created model instance: {cls._model_name}") + except Exception as e: + logger.error(f"Failed to create model: {e}") + return None + + return cls._model + + @classmethod + def is_available(cls) -> bool: + """Check if a model is available for use.""" + return cls._token is not None + + @classmethod + def is_instrumentation_enabled(cls) -> bool: + """Check if Phoenix LLM instrumentation is enabled.""" + return cls._instrumentation_enabled diff --git a/projects/agents/agents/phoenix_cost_sync.py b/projects/agents/agents/phoenix_cost_sync.py new file mode 100644 index 0000000..b352f69 --- /dev/null +++ b/projects/agents/agents/phoenix_cost_sync.py @@ -0,0 +1,272 @@ +"""Sync LiteLLM model pricing to Phoenix for cost tracking.""" +import os + +import asyncpg +import httpx +import structlog +from dapr.clients import DaprClient + +logger = structlog.get_logger(__name__) + + +async def fetch_litellm_model_info(admin_key: str) -> dict | None: + """ + Fetch model information including pricing from LiteLLM. + + Args: + admin_key: LiteLLM admin API key + + Returns: + Dictionary containing model info or None if request fails + """ + try: + async with httpx.AsyncClient() as client: + response = await client.get( + "http://litellm:4000/model/info", + headers={"Authorization": f"Bearer {admin_key}"}, + timeout=10.0 + ) + response.raise_for_status() + return response.json() + except Exception as e: + logger.error(f"Failed to fetch LiteLLM model info: {e}") + return None + + +def extract_model_pricing(model_info: dict, model_name: str = "default") -> dict | None: + """ + Extract pricing information for a specific model from LiteLLM response. + + Args: + model_info: Full response from LiteLLM /model/info endpoint + model_name: Name of the model to extract pricing for + + Returns: + Dictionary with pricing info or None if model not found + """ + for model in model_info.get("data", []): + if model.get("model_name") == model_name: + info = model.get("model_info", {}) + + # Both LiteLLM and Phoenix use cost per token (not per million) + # For example: $3/million tokens = 0.000003 per token + input_cost_per_token = info.get("input_cost_per_token", 0) + output_cost_per_token = info.get("output_cost_per_token", 0) + + # Special token costs if available + cache_creation_cost = info.get("cache_creation_input_token_cost", 0) + cache_read_cost = info.get("cache_read_input_token_cost", 0) + + return { + "model_name": model_name, + "provider": "litellm", + # Cost per token (same format for both LiteLLM and Phoenix) + "input_cost_per_token": input_cost_per_token, + "output_cost_per_token": output_cost_per_token, + # Additional token type costs + "cache_creation_cost_per_token": cache_creation_cost, + "cache_read_cost_per_token": cache_read_cost, + # Original model backend info + "backend_model": model.get("litellm_params", {}).get("model", "unknown"), + "backend_provider": info.get("litellm_provider", "unknown"), + } + + logger.warning(f"Model '{model_name}' not found in LiteLLM response") + return None + + +async def insert_phoenix_model_pricing(database_url: str, model_name: str, pricing: dict) -> bool: + """ + Insert model and pricing data directly into Phoenix PostgreSQL database. + + Args: + database_url: PostgreSQL connection string + model_name: Name of the model + pricing: Dictionary containing pricing information + + Returns: + True if successful, False otherwise + """ + conn = None + try: + # Connect to Phoenix database + conn = await asyncpg.connect(database_url) + + # Start a transaction + async with conn.transaction(): + # First check if model already exists + existing_model = await conn.fetchrow( + """ + SELECT id FROM generative_models + WHERE name = $1 AND deleted_at IS NULL + """, + model_name + ) + + if existing_model: + model_id = existing_model['id'] + logger.info(f"Model '{model_name}' already exists with ID {model_id}, updating pricing") + + # Delete existing token prices for this model + await conn.execute( + "DELETE FROM token_prices WHERE model_id = $1", + model_id + ) + else: + # Insert new model into generative_models table + model_id = await conn.fetchval( + """ + INSERT INTO generative_models + (name, name_pattern, provider, is_built_in, created_at, updated_at) + VALUES ($1, $2, $3, $4, NOW(), NOW()) + RETURNING id + """, + model_name, # name + model_name, # name_pattern (same as name for exact match) + "litellm", # provider (required field, cannot be NULL) + False # is_built_in + ) + logger.info(f"Created new model '{model_name}' with ID {model_id}") + + # Insert token prices for input tokens + await conn.execute( + """ + INSERT INTO token_prices + (model_id, token_type, is_prompt, base_rate, customization) + VALUES ($1, $2, $3, $4, $5) + """, + model_id, + "input", # token_type + True, # is_prompt + pricing["input_cost_per_token"], # base_rate (cost per token) + None # customization (no customization) + ) + + # Insert token prices for output tokens + await conn.execute( + """ + INSERT INTO token_prices + (model_id, token_type, is_prompt, base_rate, customization) + VALUES ($1, $2, $3, $4, $5) + """, + model_id, + "output", # token_type + False, # is_prompt + pricing["output_cost_per_token"], # base_rate (cost per token) + None # customization (no customization) + ) + + logger.info( + f"Inserted token prices for model {model_id}: " + f"input=${pricing['input_cost_per_token']}/token, " + f"output=${pricing['output_cost_per_token']}/token" + ) + + return True + + except Exception as e: + logger.exception(e, message="Failed to insert model pricing into Phoenix database") + return False + finally: + if conn: + await conn.close() + + +async def sync_pricing_to_phoenix(model_name: str = "default") -> bool: + """ + Sync model pricing from LiteLLM to Phoenix database. + + This function: + 1. Fetches the LITELLM_ADMIN_KEY from environment or Dapr secret store + 2. Gets model pricing from LiteLLM + 3. Inserts/updates the model and pricing in Phoenix PostgreSQL database + + Args: + model_name: Name of the model to sync pricing for + + Returns: + True if sync was successful, False otherwise + """ + try: + # Get Phoenix database URL from environment + phoenix_db_url = os.getenv("PHOENIX_SQL_DATABASE_URL") + if not phoenix_db_url: + logger.error("PHOENIX_SQL_DATABASE_URL not set in environment") + return False + + # Get LiteLLM admin key - try environment first, then Dapr + admin_key = os.getenv("LITELLM_ADMIN_KEY") + if not admin_key: + try: + with DaprClient() as client: + secret = client.get_secret( + store_name="nemesis-secret-store", + key="LITELLM_ADMIN_KEY" + ) + admin_key = secret.secret.get("LITELLM_ADMIN_KEY") + except Exception as e: + logger.warning(f"Could not get LITELLM_ADMIN_KEY from Dapr: {e}") + + if not admin_key: + logger.error("LITELLM_ADMIN_KEY not found in environment or secret store") + return False + + # Fetch model info from LiteLLM + model_info = await fetch_litellm_model_info(admin_key) + if not model_info: + return False + + # Extract pricing for our model + pricing = extract_model_pricing(model_info, model_name) + if not pricing: + return False + + logger.info( + "Extracted model pricing from LiteLLM", + model_name=model_name, + input_cost_per_token=pricing["input_cost_per_token"], + output_cost_per_token=pricing["output_cost_per_token"], + backend_model=pricing["backend_model"], + ) + + # Insert pricing into Phoenix database + success = await insert_phoenix_model_pricing(phoenix_db_url, model_name, pricing) + + if success: + # Also store in environment for reference + os.environ["PHOENIX_MODEL_NAME"] = model_name + os.environ["PHOENIX_MODEL_PROVIDER"] = "litellm" + os.environ["PHOENIX_INPUT_COST_PER_TOKEN"] = str(pricing["input_cost_per_token"]) + os.environ["PHOENIX_OUTPUT_COST_PER_TOKEN"] = str(pricing["output_cost_per_token"]) + + logger.info( + "Model pricing successfully synced to Phoenix database", + model_name=model_name, + input_cost_per_token=pricing["input_cost_per_token"], + output_cost_per_token=pricing["output_cost_per_token"], + ) + + return success + + except Exception as e: + logger.exception(e, message="Failed to sync pricing to Phoenix") + return False + + +def get_synced_pricing() -> dict | None: + """ + Get the synced pricing information from environment variables. + + Returns: + Dictionary with pricing info or None if not set + """ + model_name = os.getenv("PHOENIX_MODEL_NAME") + if not model_name: + return None + + return { + "model_name": model_name, + "provider": os.getenv("PHOENIX_MODEL_PROVIDER", "litellm"), + "input_cost_per_token": float(os.getenv("PHOENIX_INPUT_COST_PER_TOKEN", "0")), + "output_cost_per_token": float(os.getenv("PHOENIX_OUTPUT_COST_PER_TOKEN", "0")), + } diff --git a/projects/agents/agents/prompt_manager.py b/projects/agents/agents/prompt_manager.py new file mode 100644 index 0000000..41247c8 --- /dev/null +++ b/projects/agents/agents/prompt_manager.py @@ -0,0 +1,119 @@ +"""Manager for agent prompts stored in the database.""" + +from typing import Any + +import psycopg +import structlog +from common.db import get_postgres_connection_str + +logger = structlog.get_logger(__name__) + + +class PromptManager: + """Manager for loading and saving agent prompts to/from the database.""" + + _instance = None + _initialized = False + + def __new__(cls, postgres_connection_string: str): + if cls._instance is None: + cls._instance = super().__new__(cls) + cls._postgres_connection_string = postgres_connection_string + return cls._instance + + @classmethod + def initialize(cls): + """Initialize the PromptManager with PostgreSQL connection.""" + if cls._initialized: + return + + cls._postgres_connection_string = get_postgres_connection_str() + cls._initialized = True + + @classmethod + def is_available(cls) -> bool: + """Check if PromptManager is available for use.""" + return cls._initialized and cls._postgres_connection_string is not None + + def _get_connection(self): + """Get a database connection.""" + if not self.is_available(): + raise RuntimeError("PromptManager not initialized or PostgreSQL connection string unavailable") + return psycopg.connect(self._postgres_connection_string) + + def get_prompt(self, agent_name: str) -> dict[str, Any] | None: + """Get agent prompt from database. + + Args: + agent_name: Name of the agent (e.g., "validate") + + Returns: + Dict with 'prompt', 'description', and 'enabled' keys, or None if not found + """ + if not self.is_available(): + logger.debug("PromptManager not available, cannot get prompt", agent_name=agent_name) + return None + + query = """ + SELECT name, description, prompt, enabled + FROM agent_prompts + WHERE name = %s AND enabled = true + """ + + try: + with self._get_connection() as conn: + with conn.cursor() as cur: + cur.execute(query, (agent_name,)) + row = cur.fetchone() + + if row: + logger.debug("Retrieved prompt from database", agent_name=agent_name) + return { + "prompt": row[2], + "description": row[1], + "enabled": row[3], + } + else: + logger.debug("No enabled prompt found in database", agent_name=agent_name) + return None + + except Exception as e: + logger.warning("Failed to get prompt from database", agent_name=agent_name, error=str(e)) + return None + + def save_prompt(self, agent_name: str, prompt: str, description: str | None = None) -> bool: + """Save agent prompt to database. + + Args: + agent_name: Name of the agent (e.g., "validate") + prompt: The prompt text + description: Optional description of what the agent does + + Returns: + True if saved successfully, False otherwise + """ + if not self.is_available(): + logger.debug("PromptManager not available, cannot save prompt", agent_name=agent_name) + return False + + query = """ + INSERT INTO agent_prompts (name, prompt, description, enabled) + VALUES (%s, %s, %s, true) + ON CONFLICT (name) + DO UPDATE SET + prompt = EXCLUDED.prompt, + description = EXCLUDED.description, + updated_at = CURRENT_TIMESTAMP + """ + + try: + with self._get_connection() as conn: + with conn.cursor() as cur: + cur.execute(query, (agent_name, prompt, description)) + + logger.info("Saved prompt to database", agent_name=agent_name) + return True + + except Exception as e: + logger.error("Failed to save prompt to database", agent_name=agent_name, error=str(e)) + return False diff --git a/projects/agents/agents/schemas.py b/projects/agents/agents/schemas.py new file mode 100644 index 0000000..2efcc52 --- /dev/null +++ b/projects/agents/agents/schemas.py @@ -0,0 +1,167 @@ +from enum import Enum +from typing import Any, Literal, Union + +from pydantic import BaseModel, Field, field_validator + + +class TriageCategory(str, Enum): + """Triage decision categories""" + + TRUE_POSITIVE = "true_positive" + FALSE_POSITIVE = "false_positive" + NEEDS_REVIEW = "needs_review" + NOT_TRIAGED = "not_triaged" + ERROR = "error" + + +class TriageDecision(BaseModel): + """Schema for LLM triage decision response""" + + decision: str = Field(..., description="Triage decision: true_positive, false_positive, or needs_review") + explanation: str = Field(..., description="One sentence explaining the reasoning for this decision") + + +class TriageRequest(BaseModel): + """Schema for triage workflow input""" + + finding_id: int = Field(..., description="Unique finding identifier") + finding_name: str = Field(..., description="Name/type of the finding") + category: str | None = Field(None, description="Finding category") + severity: Union[int, str] | None = Field(None, description="Finding severity level (0-10 or string)") + object_id: str = Field(..., description="Object storage ID") + origin_type: str | None = Field(None, description="Type of the finding's origin") + origin_name: str | None = Field(None, description="Name of the finding's origin") + data: list[str] = Field(..., description="Finding data payload") + raw_data: dict[str, Any] | None = Field(None, description="Raw finding data") + file_path: str = Field(..., description="Path of the file associated with finding") + + @field_validator("severity") + @classmethod + def validate_severity(cls, v): + if v is None: + return v + + try: + severity_int = int(v) + except (ValueError, TypeError) as e: + raise ValueError(f"Severity must be convertible to integer, got: {v}") from e + + if not 0 <= severity_int <= 10: + raise ValueError(f"Severity must be between 0 and 10, got: {severity_int}") + + return severity_int + + +class NoseyParkerLocation(BaseModel): + """Schema for location information in Nosey Parker findings""" + + line: int = Field(..., description="Line number where the match was found") + column: int = Field(..., description="Column number where the match was found") + + +class NoseyParkerMatch(BaseModel): + """Schema for a Nosey Parker match""" + + snippet: str = Field(..., description="Portion of text around where the match was found") + location: NoseyParkerLocation = Field(..., description="Location of the match in the file") + file_path: str | None = Field(None, description="Path to the file containing the match") + rule_name: str = Field(..., description="Name of the detection rule that triggered") + rule_type: str = Field(..., description="Type/category of the detection rule") + git_commit: str | None = Field(None, description="Git commit hash where this was found") + matched_content: str = Field(..., description="The actual content that matched the rule") + + +class NoseyParkerData(BaseModel): + """Schema for Nosey Parker finding data""" + + match: NoseyParkerMatch = Field(..., description="Match information from Nosey Parker") + + +class JWTAnalysis(BaseModel): + """Schema for JWT-specific analysis""" + + is_expired: bool = Field(..., description="Whether the JWT is expired") + has_expiry_conflict: bool = Field(False, description="Whether there's conflicting expiry info") + is_sample_data: bool = Field(False, description="Whether this appears to be sample/test data") + decision: TriageCategory = Field(..., description="Triage decision for JWT") + explanation: str = Field(..., description="Explanation for the triage decision") + + +class ValidateRequest(BaseModel): + """Request for validation agent""" + + file_path: str = Field(..., description="Path of the file being analyzed") + summary: str = Field(..., description="Security finding summary to validate") + + +class ValidateResponse(BaseModel): + """Response from validation agent with strict decision values.""" + + decision: Literal["true_positive", "false_positive", "needs_review"] = Field( + ..., description="Triage decision - MUST be exactly one of: true_positive, false_positive, needs_review" + ) + explanation: str = Field(..., description="Accurate but concise 1 sentence explanation for the decision") + confidence: float = Field(default=1.0, ge=0.0, le=1.0, description="Confidence score 0-1.0") + true_positive_context: str | None = Field( + None, description="Context/risk for true_positive decisions. Only required for true_positive findings." + ) + + +class CredentialWithContext(BaseModel): + """A credential with its surrounding textual context.""" + + credential: str = Field(..., description="The extracted credential/password") + context: str = Field(..., description="Surrounding textual context for the credential") + + +class CredentialAnalysisResponse(BaseModel): + """Response from credential analysis agent.""" + + credentials: list[CredentialWithContext] = Field( + default_factory=list, description="List of extracted credentials with their surrounding context" + ) + + +class SummaryResponse(BaseModel): + """Response from text summarization agent.""" + + summary: str = Field(..., description="Generated summary of the text content") + + +class TranslationResponse(BaseModel): + """Response from text translation agent.""" + + translated_text: str = Field(..., description="Translated text content in the target language") + + +class DotNetAnalysisResponse(BaseModel): + """Response from .NET analysis agent.""" + + analysis: str = Field(..., description="Detailed analysis of the .NET assembly") + + +class TriageResult(BaseModel): + """Schema for the result returned by finding_triage_workflow()""" + + finding_id: int = Field(..., description="ID of the finding that was triaged") + decision: str = Field(..., description="Triage decision made") + explanation: str = Field(..., description="Explanation for the triage decision") + confidence: float | None = Field(None, ge=0.0, le=1.0, description="Confidence score 0-1.0 (optional)") + true_positive_context: str | None = Field(None, description="Context/risk for true_positive decisions") + success: bool = Field(..., description="Whether the triage process completed successfully") + + +class ReportSynthesisResponse(BaseModel): + """Response from reporting agent for risk assessment synthesis.""" + + risk_level: Literal["high", "medium", "low"] = Field( + ..., description="Overall risk level assessment - MUST be exactly one of: high, medium, low" + ) + executive_summary: str = Field(..., description="Executive summary of the risk assessment (2-3 paragraphs)") + critical_findings: list[str] = Field( + default_factory=list, description="List of the most critical findings requiring immediate attention" + ) + credential_exposure: str = Field(..., description="Analysis of credential exposure risk") + sensitive_data_exposure: str = Field(..., description="Analysis of sensitive data exposure") + attack_surface: str = Field(..., description="Analysis of attack surface based on file types and applications") + full_report_markdown: str = Field(..., description="Full markdown-formatted report combining all sections") diff --git a/projects/agents/agents/tasks/THIRD_PARTY_LICENSES.txt b/projects/agents/agents/tasks/THIRD_PARTY_LICENSES.txt new file mode 100644 index 0000000..f70d68a --- /dev/null +++ b/projects/agents/agents/tasks/THIRD_PARTY_LICENSES.txt @@ -0,0 +1,30 @@ +----------------------------------------------------------------------------- + +- Cecil (dotnet_reversing/lib/Mono.Cecil.dll) + https://github.com/jbevain/cecil + Copyright (c) 2008 - 2015 Jb Evain + Copyright (c) 2008 - 2011 Novell, Inc. + +- iLSpy (dotnet_reversing/lib/ICSharpCode*.dll) + https://github.com/icsharpcode/ILSpy + Copyright (c) 2011-2025 AlphaSierraPapa for the ILSpy team + +--- + +MIT license + +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. \ No newline at end of file diff --git a/projects/agents/agents/tasks/credential_analyzer.py b/projects/agents/agents/tasks/credential_analyzer.py new file mode 100644 index 0000000..a95902d --- /dev/null +++ b/projects/agents/agents/tasks/credential_analyzer.py @@ -0,0 +1,241 @@ +"""Credential analysis agent using Pydantic AI.""" + +import json +import tempfile + +import psycopg +import structlog +from agents.base_agent import BaseAgent +from agents.model_manager import ModelManager +from agents.prompt_manager import PromptManager +from agents.schemas import CredentialAnalysisResponse, CredentialWithContext +from common.db import get_postgres_connection_str +from common.models import FileObject, FindingCategory, FindingOrigin +from common.state_helpers import get_file_enriched +from common.storage import StorageMinio +from dapr.ext.workflow.workflow_activity_context import WorkflowActivityContext +from pydantic_ai import Agent +from pydantic_ai.settings import ModelSettings + +logger = structlog.get_logger(__name__) + + +def credentials_to_markdown(credentials: list[CredentialWithContext]) -> str: + """Format extracted credentials as markdown report.""" + if not credentials: + return "## LLM Credential Analysis\n\nNo credentials found in this document." + + markdown = "## LLM Credential Analysis\n\n" + markdown += "### Detected Credentials\n\n" + + for i, cred in enumerate(credentials, 1): + markdown += f"#### Credential {i}\n\n" + markdown += f"**Credential:** `{cred.credential}`\n\n" + markdown += f"**Context:**\n```\n{cred.context}\n```\n\n" + + return markdown + + +class CredentialAnalyzer(BaseAgent): + """Agent for extracting credentials from text using LLM.""" + + def __init__(self): + super().__init__() + self.prompt_manager = PromptManager(get_postgres_connection_str()) + self.name = "Credential Analyzer" + self.description = "Extracts credentials and passwords from text content using LLM analysis" + self.agent_type = "llm_based" + self.has_prompt = True + self.llm_temperature = 0.2 + self.system_prompt = """You are a cybersecurity expert extremely proficient at identifying credentials and passwords. + +Analyze the following document and extract any credentials or passwords you find. For each credential found, provide: +1. The credential itself (password, API key, token, etc.) +2. The surrounding textual context (2-3 lines before and after the credential to show where it was found) + +Return your findings as a structured list. If no credentials are found, return an empty list.""" + self.storage = StorageMinio() + self.postgres_connection_url = get_postgres_connection_str() + + def _get_text_content(self, object_id: str) -> str: + """Get text content from file or extracted_text transform.""" + try: + file_enriched = get_file_enriched(object_id) + + if file_enriched.is_plaintext: + try: + file_bytes = self.storage.download_bytes(object_id) + return file_bytes.decode("utf-8", errors="replace") + except Exception as e: + logger.warning(f"Failed to decode plaintext file content: {e}") + + # Look for extracted_text transform + with psycopg.connect(self.postgres_connection_url) as conn: + with conn.cursor() as cur: + cur.execute( + """ + SELECT transform_object_id + FROM transforms + WHERE object_id = %s AND type = 'extracted_text' + LIMIT 1 + """, + (object_id,), + ) + result = cur.fetchone() + transform_object_id = result[0] if result else None + if transform_object_id: + transform_object_id = str(transform_object_id) + try: + transform_bytes = self.storage.download_bytes(transform_object_id) + return transform_bytes.decode("utf-8", errors="replace") + except Exception as e: + logger.error(f"Failed to get extracted text transform content: {e}") + + return "" + + except Exception as e: + logger.error(f"Error getting text content: {e}") + return "" + + def get_prompt(self) -> str: + """Get the credential analysis prompt from database or use default.""" + try: + # Try to get prompt from database + prompt_data = self.prompt_manager.get_prompt(self.name) + + if prompt_data: + return prompt_data["prompt"] + else: + # No prompt in database, try to save default + logger.info("No prompt found in database, initializing with default", agent_name=self.name) + success = self.prompt_manager.save_prompt(self.name, self.system_prompt, self.description) + if success: + logger.info("Default prompt saved to database", agent_name=self.name) + else: + # This is expected during startup when event loop is running + logger.debug( + "Could not save default prompt to database (likely during startup)", agent_name=self.name + ) + + return self.system_prompt + + except Exception as e: + logger.warning("Error managing prompt, using default", agent_name=self.name, error=str(e)) + return self.system_prompt + + def execute(self, ctx: WorkflowActivityContext, activity_input: dict) -> dict: + """Analyze credentials in the given file.""" + object_id = activity_input.get("object_id", "") + + logger.debug("credential_analysis activity started", object_id=object_id) + + model = ModelManager.get_model() + + if not model: + logger.warning("No model available from ModelManager") + return {"success": False, "error": "AI model not available for credential analysis"} + + try: + # Get text content + text_content = self._get_text_content(object_id) + if not text_content: + return {"success": False, "error": "No text content found to analyze"} + + # Get the current prompt from database or default + current_prompt = self.get_prompt() + + agent = Agent( + model=model, + system_prompt=current_prompt, + output_type=CredentialAnalysisResponse, + instrument=ModelManager.is_instrumentation_enabled(), + retries=3, + model_settings=ModelSettings(temperature=self.llm_temperature), + ) + + prompt = f"""Document: + +{text_content}""" + + result = agent.run_sync(prompt) + logger.debug( + "Credential LLM analysis completed", + object_id=object_id, + total_tokens=result.usage().total_tokens, + request_tokens=result.usage().request_tokens, + response_tokens=result.usage().response_tokens, + ) + + credentials = result.output.credentials + + # Create markdown report + markdown_report = credentials_to_markdown(credentials) + + # Store the analysis as a file + with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8") as tmp_credentials: + tmp_credentials.write(markdown_report) + tmp_credentials.flush() + credentials_id = self.storage.upload_file(tmp_credentials.name) + + # Add transform to database + with psycopg.connect(self.postgres_connection_url) as conn: + with conn.cursor() as cur: + metadata = { + "file_name": "extracted_credentials.md", + "display_type_in_dashboard": "markdown", + "display_title": "LLM-Extracted Credentials", + "default_display": True, + } + + cur.execute( + """ + INSERT INTO transforms (object_id, type, transform_object_id, metadata) + VALUES (%s, %s, %s, %s) + """, + (object_id, "llm_extracted_credentials", credentials_id, json.dumps(metadata)), + ) + + # Create finding if credentials were found + if credentials: + display_data = FileObject( + type="finding_summary", + metadata={"summary": markdown_report}, + ) + + cur.execute( + """ + INSERT INTO findings ( + category, finding_name, origin_type, origin_name, + object_id, severity, raw_data, data + ) + VALUES (%s, %s, %s, %s, %s, %s, %s, %s) + """, + ( + FindingCategory.CREDENTIAL.value, + "llm_extracted_credentials", + FindingOrigin.ENRICHMENT_MODULE.value, + "credential_analyzer", + object_id, + 8, + json.dumps({"credentials": [cred.model_dump() for cred in credentials]}), + json.dumps([display_data.model_dump()]), + ), + ) + conn.commit() + + logger.debug("Credential analysis completed", object_id=object_id) + return { + "success": True, + "credentials_found": bool(credentials), + "transform_id": credentials_id, + } + + except Exception as e: + logger.error("Credential analysis failed", object_id=object_id, error=str(e)) + return {"success": False, "error": str(e)} + + +def analyze_credentials(ctx: WorkflowActivityContext, activity_input: dict) -> dict: + """Wrapper function to maintain compatibility with existing workflow calls.""" + agent = CredentialAnalyzer() + return agent.execute(ctx, activity_input) diff --git a/projects/agents/agents/tasks/dotnet_analyzer.py b/projects/agents/agents/tasks/dotnet_analyzer.py new file mode 100644 index 0000000..415c8c6 --- /dev/null +++ b/projects/agents/agents/tasks/dotnet_analyzer.py @@ -0,0 +1,572 @@ +""" +Adapted directly from https://github.com/dreadnode/example-agents/tree/6dbbfe85b335618ca5f4ca2bc5f439052b84d0b1/dotnet_reversing +Author: @dreadnode +License: None +""" + +import json +import os +import sys +import tempfile +import typing as t +from dataclasses import dataclass +from pathlib import Path + +import psycopg +import structlog +from agents.base_agent import BaseAgent +from agents.model_manager import ModelManager +from agents.prompt_manager import PromptManager +from agents.schemas import DotNetAnalysisResponse +from common.db import get_postgres_connection_str +from common.state_helpers import get_file_enriched +from common.storage import StorageMinio +from dapr.ext.workflow.workflow_activity_context import WorkflowActivityContext +from pydantic_ai import Agent, RunContext +from pydantic_ai.exceptions import UsageLimitExceeded +from pydantic_ai.settings import ModelSettings +from pydantic_ai.usage import UsageLimits +from pythonnet import load # type: ignore [import-untyped] + +logger = structlog.get_logger(__name__) + +# Load .NET runtime +load("coreclr") + +import clr # type: ignore [import-untyped] # noqa: E402 + +# Add references to .NET libraries +lib_dir = Path(__file__).parent.parent / "lib" +sys.path.append(str(lib_dir)) + +clr.AddReference("ICSharpCode.Decompiler") +clr.AddReference("Mono.Cecil") + +from ICSharpCode.Decompiler import ( # type: ignore [import-not-found] # noqa: E402 + DecompilerSettings, +) +from ICSharpCode.Decompiler.CSharp import ( # type: ignore [import-not-found] # noqa: E402 + CSharpDecompiler, +) +from ICSharpCode.Decompiler.Metadata import ( # type: ignore [import-not-found] # noqa: E402 + MetadataTokenHelpers, +) +from Mono.Cecil import AssemblyDefinition # type: ignore [import-not-found] # noqa: E402 + +# Helper functions (adapted from dotnet_reversing/reversing.py) + + +def _shorten_dotnet_name(name: str) -> str: + return name.split(" ")[-1].split("(")[0] + + +def _get_decompiler(path: Path | str) -> CSharpDecompiler: + settings = DecompilerSettings() + settings.ThrowOnAssemblyResolveErrors = False + return CSharpDecompiler(str(path), settings) + + +def _decompile_token(path: Path | str, token: int) -> str: + entity_handle = MetadataTokenHelpers.TryAsEntityHandle(token.ToUInt32()) # type: ignore [attr-defined] + return _get_decompiler(path).DecompileAsString(entity_handle) # type: ignore [no-any-return] + + +def _find_references(assembly: AssemblyDefinition, search: str) -> list[str]: + flexible_search_strings = [ + search.lower(), + search.lower().replace(".", "::"), + search.lower().replace("::", "."), + ] + + using_methods: set[str] = set() + for module in assembly.Modules: + methods = [] + for module_type in module.Types: + for method in module_type.Methods: + methods.append(method) + + for method in methods: + if not method.HasBody: + continue + + for instruction in method.Body.Instructions: + intruction_str = str(instruction.Operand).lower() + + for _search in flexible_search_strings: + if _search in intruction_str: + using_methods.add(method.FullName) + + return list(using_methods) + + +DEFAULT_EXCLUDE = [ + "mscorlib.dll", +] + + +@dataclass +class DotnetReversing: + """Adapted from @dreadnode's dotnet_reversing/reversing.py""" + + file_path: Path + + @classmethod + def from_file(cls, path: Path | str) -> "DotnetReversing": + file_path = Path(path) + if not file_path.exists(): + raise ValueError(f"File path does not exist: {file_path}") + return cls(file_path=file_path) + + def decompile_module(self) -> str: + """Decompile the entire module and return the decompiled code as a string.""" + logger.info(f"decompile_module({self.file_path})") + return _get_decompiler(self.file_path).DecompileWholeModuleAsString() # type: ignore [no-any-return] + + def decompile_type(self, type_name: str) -> str: + """Decompile a specific type and return the decompiled code as a string.""" + logger.info(f"decompile_type({self.file_path}, {type_name})") + try: + # Use metadata token approach directly since the API signature is different + assembly = AssemblyDefinition.ReadAssembly(str(self.file_path)) + for module in assembly.Modules: + for module_type in module.Types: + if module_type.FullName == type_name: + return _decompile_token(self.file_path, module_type.MetadataToken) + return f"Type '{type_name}' not found in assembly" + except Exception as e: + logger.error(f"Error decompiling type '{type_name}': {e}") + return f"Error decompiling type '{type_name}': {str(e)}" + + def list_namespaces(self) -> list[str]: + """List all namespaces in the assembly.""" + logger.info(f"list_namespaces({self.file_path})") + assembly = AssemblyDefinition.ReadAssembly(str(self.file_path)) + + namespaces = set() + for module in assembly.Modules: + for module_type in module.Types: + if "." in module_type.FullName: + # Get namespace part (everything before the last dot) + namespace = ".".join(module_type.FullName.split(".")[:-1]) + namespaces.add(namespace) + else: + # Handle types without namespace (add as root) + namespaces.add("") + + return sorted(namespaces) + + def list_types_in_namespace(self, namespace: str) -> list[str]: + """List all types in the specified namespace.""" + logger.info(f"list_types_in_namespace({self.file_path}, {namespace})") + assembly = AssemblyDefinition.ReadAssembly(str(self.file_path)) + + types = [] + for module in assembly.Modules: + for module_type in module.Types: + if namespace == "": + # Handle types without namespace + if "." not in module_type.FullName or ( + module_type.FullName.count(".") == 1 and module_type.FullName.endswith("Module") + ): + types.append(module_type.FullName) + elif module_type.FullName.startswith(f"{namespace}."): + # Check if the type belongs directly to this namespace (not a sub-namespace) + remainder = module_type.FullName[len(namespace) + 1 :] + if "." not in remainder: + types.append(module_type.FullName) + + return types + + def list_types(self) -> list[str]: + """List all types in the assembly and return their full names.""" + logger.info(f"list_types({self.file_path})") + assembly = AssemblyDefinition.ReadAssembly(str(self.file_path)) + return [module_type.FullName for module in assembly.Modules for module_type in module.Types] + + def list_methods(self) -> list[str]: + """List all methods in the assembly and return their full names.""" + logger.info(f"list_methods({self.file_path})") + assembly = AssemblyDefinition.ReadAssembly(str(self.file_path)) + methods: list[str] = [] + for module in assembly.Modules: + for module_type in module.Types: + methods.extend([method.FullName for method in module_type.Methods]) + return methods + + def search_for_references(self, search: str) -> list[str]: + """Locate all methods inside the assembly that reference the search string.""" + logger.info(f"search_for_references({self.file_path}, {search})") + assembly = AssemblyDefinition.ReadAssembly(str(self.file_path)) + return _find_references(assembly, search) + + def decompile_methods(self, method_names: list[str]) -> dict[str, str]: + """Decompile specific methods and return a dictionary with method names as keys and decompiled code as values.""" + logger.info(f"decompile_methods({self.file_path}, {method_names})") + flexible_method_names = [_shorten_dotnet_name(name).lower() for name in method_names] + assembly = AssemblyDefinition.ReadAssembly(str(self.file_path)) + methods: dict[str, str] = {} + for module in assembly.Modules: + for module_type in module.Types: + for method in module_type.Methods: + method_name = _shorten_dotnet_name(method.FullName).lower() + if method_name in flexible_method_names: + methods[method.FullName] = _decompile_token(self.file_path, method.MetadataToken) + return methods + + def list_methods_in_type(self, type_name: str) -> list[str]: + """List all methods in the specified type.""" + logger.info(f"list_methods_in_type({self.file_path}, {type_name})") + assembly = AssemblyDefinition.ReadAssembly(str(self.file_path)) + + methods = [] + for module in assembly.Modules: + for module_type in module.Types: + if module_type.FullName == type_name: + methods.extend([method.Name for method in module_type.Methods]) + break + + return methods + + def search_by_name(self, search: str) -> dict[str, list[str]]: + """Search for types and methods in the assembly that match the search string.""" + logger.info(f"search_by_name({self.file_path}, {search})") + + results: dict[str, list[str]] = { + "types": [], + "methods": [], + } + + assembly = AssemblyDefinition.ReadAssembly(str(self.file_path)) + search_lower = search.lower() + + # Type search + for module in assembly.Modules: + for module_type in module.Types: + if search_lower in module_type.FullName.lower(): + results["types"].append(module_type.FullName) + + # Method search + for module in assembly.Modules: + for module_type in module.Types: + for method in module_type.Methods: + if search_lower in method.FullName.lower(): + results["methods"].append(method.FullName) + + return results + + def get_call_flows_to_method(self, method_name: str, max_depth: int = 10) -> list[list[str]]: + """Find all unique call flows to the target method and return nested list of method names representing call paths.""" + logger.info(f"get_call_flows_to_method({self.file_path}, {method_name})") + + def _extract_unique_call_paths( + tree: dict[str, t.Any], current_path: list[str] | None = None + ) -> list[list[str]]: + if current_path is None: + current_path = [] + + if not tree: # Leaf node + return [current_path] if current_path else [] + + paths = [] + for method, subtree in tree.items(): + new_path = [method, *current_path] + paths.extend(_extract_unique_call_paths(subtree, new_path)) + + return paths + + assembly = AssemblyDefinition.ReadAssembly(str(self.file_path)) + short_target_name = _shorten_dotnet_name(method_name) + + def build_tree(method_name: str, current_depth: int = 0, visited: set[str] | None = None) -> dict[str, t.Any]: + visited = visited or set() + if method_name in visited or current_depth > max_depth: + return {} + + visited.add(method_name) + tree = {} + + for caller in _find_references(assembly, method_name): + if caller not in visited: + tree[caller] = build_tree( + _shorten_dotnet_name(caller), + current_depth + 1, + visited.copy(), + ) + + return tree + + call_tree = build_tree(short_target_name) + return _extract_unique_call_paths(call_tree) + + +class DotNetAnalyzer(BaseAgent): + """Agent for analyzing .NET assemblies using LLM.""" + + def __init__(self): + super().__init__() + self.prompt_manager = PromptManager(get_postgres_connection_str()) + self.name = ".NET Assembly Analyzer" + self.description = "Adapted @dreadnode agent that analyzes .NET assemblies" + self.agent_type = "llm_based" + self.has_prompt = True + self.llm_temperature = 0.3 + # Usage limits from environment variables with defaults + self.request_limit = int(os.getenv("DOTNET_ANALYSIS_RUN_REQUEST_LIMIT", 25)) + self.total_tokens_limit = int(os.getenv("DOTNET_ANALYSIS_RUN_TOKENS_LIMIT", 1_000_000)) + # self.system_prompt = """You are a .NET reverse engineering expert with access to decompilation and analysis tools. + + # Analyze the following .NET assembly and resolve the task below using all the tools available to you. + # Provide a report for all interesting findings you discover while performing the task. + + # Focus your analysis on: + # 1. **Architecture & Design Patterns**: Identify architectural patterns, design patterns used + # 2. **Security Analysis**: Highlight potential security concerns, suspicious methods, crypto usage + # 3. **Functionality Assessment**: Describe what the assembly does, its main purposes + # 4. **Key Components**: Identify the most important classes, methods, and namespaces + # 5. **External Dependencies**: Note any interesting external API calls or P/Invoke usage + # 6. **Obfuscation/Protection**: Identify any signs of obfuscation, packing, or anti-analysis + + # Provide your analysis in markdown format with clear sections and practical insights for a security analyst.""" + + self.system_prompt = """You are a .NET security vulnerability analyst with access to decompilation and analysis tools. + +Use the available tools systematically to analyze the assembly: +1. Start with high-level overview (namespaces, types) +2. Focus on security-relevant code patterns +3. Decompile suspicious methods for detailed analysis + +Provide analysis in markdown format with actionable security findings.""" + self.storage = StorageMinio() + self.dotnet_analyzer = None # Will be set during execution + self.postgres_connection_url = get_postgres_connection_str() + + def decompile_module(self, ctx: RunContext) -> str: + """Decompile the entire module and return the decompiled code as a string.""" + if not self.dotnet_analyzer: + return "Error: .NET analyzer not initialized" + return self.dotnet_analyzer.decompile_module() + + def decompile_type(self, ctx: RunContext, type_name: t.Annotated[str, "The specific type to decompile"]) -> str: + """Decompile a specific type and return the decompiled code as a string.""" + if not self.dotnet_analyzer: + return "Error: .NET analyzer not initialized" + return self.dotnet_analyzer.decompile_type(type_name) + + def decompile_methods( + self, ctx: RunContext, method_names: t.Annotated[list[str], "List of methods to decompile"] + ) -> dict[str, str]: + """Decompile specific methods and return a dictionary with method names as keys and decompiled code as values.""" + if not self.dotnet_analyzer: + return {"error": ".NET analyzer not initialized"} + return self.dotnet_analyzer.decompile_methods(method_names) + + def list_namespaces(self, ctx: RunContext) -> list[str]: + """List all namespaces in the assembly.""" + if not self.dotnet_analyzer: + logger.error("TOOL ERROR: .NET analyzer not initialized") + return ["Error: .NET analyzer not initialized"] + result = self.dotnet_analyzer.list_namespaces() + return result + + def list_types_in_namespace( + self, ctx: RunContext, namespace: t.Annotated[str, "The namespace to list types from"] + ) -> list[str]: + """List all types in the specified namespace.""" + if not self.dotnet_analyzer: + return ["Error: .NET analyzer not initialized"] + return self.dotnet_analyzer.list_types_in_namespace(namespace) + + def list_methods_in_type(self, ctx: RunContext, type_name: t.Annotated[str, "The full type name"]) -> list[str]: + """List all methods in the specified type.""" + if not self.dotnet_analyzer: + return ["Error: .NET analyzer not initialized"] + return self.dotnet_analyzer.list_methods_in_type(type_name) + + def list_types(self, ctx: RunContext) -> list[str]: + """List all types in the assembly and return their full names.""" + if not self.dotnet_analyzer: + logger.error("TOOL ERROR: .NET analyzer not initialized") + return ["Error: .NET analyzer not initialized"] + result = self.dotnet_analyzer.list_types() + return result + + def list_methods(self, ctx: RunContext) -> list[str]: + """List all methods in the assembly and return their full names.""" + if not self.dotnet_analyzer: + return ["Error: .NET analyzer not initialized"] + return self.dotnet_analyzer.list_methods() + + def search_for_references( + self, ctx: RunContext, search: t.Annotated[str, "A flexible search string used to check called function names"] + ) -> list[str]: + """Locate all methods inside the assembly that reference the search string. This can be used to locate uses of a specific function or method anywhere in the assembly.""" + if not self.dotnet_analyzer: + return ["Error: .NET analyzer not initialized"] + return self.dotnet_analyzer.search_for_references(search) + + def get_call_flows_to_method( + self, ctx: RunContext, method_name: t.Annotated[str, "Target method name"], max_depth: int = 10 + ) -> list[list[str]]: + """Find all unique call flows to the target method and return a nested list of method names representing the call paths.""" + if not self.dotnet_analyzer: + return [["Error: .NET analyzer not initialized"]] + return self.dotnet_analyzer.get_call_flows_to_method(method_name, max_depth) + + def _is_dotnet_file(self, object_id: str) -> bool: + """Check if the file is a .NET assembly.""" + try: + file_enriched = get_file_enriched(object_id) + return "mono/.net assembly" in file_enriched.magic_type.lower() + except Exception as e: + logger.error(f"Error checking if file is .NET assembly: {e}") + return False + + def get_prompt(self) -> str: + """Get the .NET analysis prompt from database or use default.""" + try: + # Try to get prompt from database + prompt_data = self.prompt_manager.get_prompt(self.name) + + if prompt_data: + return prompt_data["prompt"] + else: + # No prompt in database, try to save default + logger.info("No prompt found in database, initializing with default", agent_name=self.name) + success = self.prompt_manager.save_prompt(self.name, self.system_prompt, self.description) + if success: + logger.info("Default prompt saved to database", agent_name=self.name) + else: + # This is expected during startup when event loop is running + logger.debug( + "Could not save default prompt to database (likely during startup)", agent_name=self.name + ) + + return self.system_prompt + + except Exception as e: + logger.warning("Error managing prompt, using default", agent_name=self.name, error=str(e)) + return self.system_prompt + + def execute(self, ctx: WorkflowActivityContext, activity_input: dict) -> dict: + """Analyze .NET assembly content in the given file using interactive tools.""" + object_id = activity_input.get("object_id", "") + + logger.debug(".NET analysis activity started", object_id=object_id) + + # Check if this is a .NET file + if not self._is_dotnet_file(object_id): + return {"success": False, "error": "File is not a .NET assembly"} + + model = ModelManager.get_model() + + if not model: + logger.warning("No model available from ModelManager") + return {"success": False, "error": "AI model not available for .NET analysis"} + + try: + # Download the file to a temporary location for analysis + file_enriched = get_file_enriched(object_id) + with self.storage.download(object_id) as temp_file: + # Initialize the dotnet analyzer for use by tools + self.dotnet_analyzer = DotnetReversing.from_file(temp_file.name) + + # Get the current prompt from database or default + current_prompt = self.get_prompt() + + # Create agent with tools + agent = Agent( + model=model, + system_prompt=current_prompt, + output_type=DotNetAnalysisResponse, + instrument=ModelManager.is_instrumentation_enabled(), + retries=3, + model_settings=ModelSettings( + temperature=self.llm_temperature, + max_tokens=16384, # Allow for detailed analysis output + ), + ) + + # Add tools to the agent + agent.tool(self.decompile_module) + agent.tool(self.decompile_type) + agent.tool(self.decompile_methods) + agent.tool(self.list_namespaces) + agent.tool(self.list_types_in_namespace) + agent.tool(self.list_methods_in_type) + agent.tool(self.list_types) + agent.tool(self.list_methods) + agent.tool(self.search_for_references) + agent.tool(self.get_call_flows_to_method) + + prompt = f""" +Analyze this .NET assembly for security vulnerabilities. Focus on: +- Input validation issues (SQL injection, path traversal, etc.) +- Authentication/authorization bypasses +- Cryptographic weaknesses +- Unsafe deserialization +- Privilege escalation vectors + +Prioritize findings by exploitability and business impact. + + + +{file_enriched.file_name} (analyzing: {temp_file.name}) +""" + + try: + result = agent.run_sync( + prompt, + usage_limits=UsageLimits( + request_limit=self.request_limit, total_tokens_limit=self.total_tokens_limit + ), + ) + except UsageLimitExceeded as e: + logger.error(".NET analysis hit usage limit", error=str(e), object_id=object_id) + return {"success": False, "error": f"Analysis hit usage limit: {str(e)}"} + logger.debug( + ".NET analysis LLM analysis completed", + object_id=object_id, + total_tokens=result.usage().total_tokens, + request_tokens=result.usage().request_tokens, + response_tokens=result.usage().response_tokens, + ) + + analysis = result.output.analysis + + # Store the analysis as a file + with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8") as tmp_analysis: + tmp_analysis.write(analysis) + tmp_analysis.flush() + analysis_id = self.storage.upload_file(tmp_analysis.name) + + # Add transform to database + with psycopg.connect(self.postgres_connection_url) as conn: + with conn.cursor() as cur: + metadata = { + "file_name": "dotnet_analysis.md", + "display_type_in_dashboard": "markdown", + "display_title": ".NET Assembly Analysis", + "default_display": True, + } + + cur.execute( + """ + INSERT INTO transforms (object_id, type, transform_object_id, metadata) + VALUES (%s, %s, %s, %s) + """, + (object_id, "dotnet_analysis", analysis_id, json.dumps(metadata)), + ) + conn.commit() + + logger.debug(".NET analysis completed", object_id=object_id) + return {"success": True, "transform_id": analysis_id} + + except Exception as e: + logger.error(".NET analysis failed", object_id=object_id, error=str(e)) + return {"success": False, "error": str(e)} + + +def analyze_dotnet_assembly(ctx: WorkflowActivityContext, activity_input: dict) -> dict: + """Wrapper function to maintain compatibility with existing workflow calls.""" + agent = DotNetAnalyzer() + return agent.execute(ctx, activity_input) diff --git a/projects/agents/agents/tasks/jwt.py b/projects/agents/agents/tasks/jwt.py new file mode 100644 index 0000000..bd2c06c --- /dev/null +++ b/projects/agents/agents/tasks/jwt.py @@ -0,0 +1,150 @@ +"""JWT analysis agent for validating JWT findings.""" + +import structlog +from agents.base_agent import BaseAgent +from agents.logger import set_agent_metadata +from agents.schemas import JWTAnalysis, TriageCategory +from dapr.ext.workflow.workflow_activity_context import WorkflowActivityContext + +logger = structlog.get_logger(__name__) + + +class JWTAgent(BaseAgent): + """Agent for analyzing JWT findings.""" + + def __init__(self): + super().__init__() + self.name = "JWT Analyzer" + self.description = "Rule-based JWT analysis that checks expiry status and identifies sample data" + self.agent_type = "rule_based" + self.has_prompt = False + + def analyze_jwt_finding(self, summary: str, file_path: str) -> JWTAnalysis | None: + """ + Analyze JWT findings without LLM - based on expiry status and patterns. + This is a non-LLM task that can run independently. + + Args: + summary: The finding summary text + file_path: Path of the file containing the JWT + + Returns: + JWTAnalysis object with decision, or None if not a JWT finding + """ + + # Check if this is actually a JWT finding + if "JSON Web Token" not in summary: + return None + + logger.debug("Analyzing JWT finding from file", file_path=file_path) + + # Check for expiry status in the summary + has_expired_true = "**Expired**: True" in summary + has_expired_false = "**Expired**: False" in summary + + # Check for sample/test data indicators + is_sample_data = any( + indicator in file_path.lower() + for indicator in ["test", "sample", "example", "demo", "mock", "fixture", "spec"] + ) + + # Determine if there's conflicting information + has_expiry_conflict = has_expired_true and has_expired_false + + # Make triage decision based on rules + if has_expiry_conflict: + # Conflicting expiry info - might be multiple JWTs or parsing issue + decision = TriageCategory.TRUE_POSITIVE + explanation = "Conflicting expiry information found" + is_expired = False # Conservative assumption + elif has_expired_true and not has_expired_false: + # Clearly expired JWT - usually false positive + decision = TriageCategory.FALSE_POSITIVE + explanation = "JWT is expired" + is_expired = True + elif has_expired_false and not has_expired_true: + # Valid (non-expired) JWT - potential security issue + decision = TriageCategory.TRUE_POSITIVE if not is_sample_data else TriageCategory.FALSE_POSITIVE + explanation = "JWT is not expired" + is_expired = False + else: + # No clear expiry information + decision = TriageCategory.NEEDS_REVIEW + explanation = "No expiry information found" + is_expired = False + + # Override decision if clearly sample data + if is_sample_data and decision == TriageCategory.TRUE_POSITIVE: + decision = TriageCategory.FALSE_POSITIVE + + result = JWTAnalysis( + is_expired=is_expired, + has_expiry_conflict=has_expiry_conflict, + is_sample_data=is_sample_data, + decision=decision, + explanation=explanation, + ) + + logger.debug( + "JWT analysis complete", + decision=result.decision, + expired=is_expired, + conflict=has_expiry_conflict, + sample=is_sample_data, + ) + + return result + + def execute(self, ctx: WorkflowActivityContext, activity_input: dict) -> dict: + """Analyze a JWT finding.""" + file_path = activity_input.get("file_path", "") + object_id = activity_input.get("object_id", "") + finding_id = activity_input.get("finding_id", "") + summary = activity_input.get("summary", "") + + logger.debug("validate_jwt_finding activity started", file_path=file_path) + + try: + # Set metadata for this agent run + set_agent_metadata( + agent_name="jwt_analyzer", + file_path=file_path, + object_id=object_id, + finding_id=finding_id, + finding_type="jwt_analysis", + tags=["jwt", "rule_based"], + ) + + result = self.analyze_jwt_finding(summary, file_path) + + if result is None: + # Not a JWT finding, return needs_review + return { + "is_expired": False, + "has_expiry_conflict": False, + "is_sample_data": False, + "decision": TriageCategory.NEEDS_REVIEW, + } + + return { + "is_expired": result.is_expired, + "has_expiry_conflict": result.has_expiry_conflict, + "is_sample_data": result.is_sample_data, + "decision": result.decision, + "explanation": result.explanation, + } + + except Exception as e: + logger.error("JWT analysis failed", file_path=file_path, error=str(e)) + return { + "is_expired": False, + "has_expiry_conflict": False, + "is_sample_data": False, + "decision": TriageCategory.NOT_TRIAGED, + } + + +def validate_jwt_finding(ctx: WorkflowActivityContext, activity_input: dict) -> dict: + """Wrapper function to maintain compatibility with existing workflow calls.""" + agent = JWTAgent() + return agent.execute(ctx, activity_input) diff --git a/projects/agents/agents/tasks/reporting_agent.py b/projects/agents/agents/tasks/reporting_agent.py new file mode 100644 index 0000000..2992bea --- /dev/null +++ b/projects/agents/agents/tasks/reporting_agent.py @@ -0,0 +1,244 @@ +"""Reporting agent for generating LLM-based risk assessments and synthesis reports.""" + +import json + +import structlog +from agents.base_agent import BaseAgent +from agents.model_manager import ModelManager +from agents.prompt_manager import PromptManager +from agents.schemas import ReportSynthesisResponse +from common.db import get_postgres_connection_str +from dapr.ext.workflow.workflow_activity_context import WorkflowActivityContext +from pydantic_ai import Agent +from pydantic_ai.settings import ModelSettings + +logger = structlog.get_logger(__name__) + + +class ReportingAgent(BaseAgent): + """Agent for generating LLM-based risk assessments and synthesis reports.""" + + def __init__(self): + super().__init__() + self.prompt_manager = PromptManager(get_postgres_connection_str()) + self.name = "Report Generator" + self.description = "Generates comprehensive risk assessment reports based on compromise data" + self.agent_type = "llm_based" + self.has_prompt = True + self.llm_temperature = 0.3 + self.system_prompt = """You are a cybersecurity analyst specializing in compromise assessment and risk exposure analysis. +You will receive statistical data about files and security findings from a compromised host or system. + +Your task is to analyze the data and answer: "If this host/system was compromised, what could an +attacker have had access to and what would the risk/impact be?. + +Focus on: +1. Verified findings (marked as true_positive by analysts or AI triage) +2. Decrypted credentials and their potential impact +3. Sensitive data exposure (PII, credentials, proprietary info) +4. Attack surface based on file types and applications discovered + +DO NOT provide: +- Remediation steps or recommendations +- Comparison against security baselines +- Compliance assessments +- Temporal analysis or timeline-based patterns + +In some cases we may be analyzing a disk image for a host - in those cases: +- Registry hives (SYSTEM, SECURITY, SAM) existing is expected and NORMAL - this is NOT a high-risk finding by itself +- Focus on EXTRACTED CREDENTIALS that enable access to OTHER systems or accounts: + * Successfully cracked/decrypted user passwords (not just hashed passwords existing) + * Kerberos tickets that grant access to additional hosts + * Decrypted browser credentials for external services (high value hosts) + * SSH keys, API tokens, cloud service credentials + * Credentials in application config files or documents +- DPAPI masterkeys being decrypted only matters if they were used to decrypt sensitive credentials +- Focus on findings that enable lateral movement or access beyond this single host + +Provide your analysis in markdown format with these sections. + +IMPORTANT: Do NOT include a top-level title (# heading) in your response. +Start directly with the section headings below: + +## Executive Summary +1 paragraph summarizing the overall risk exposure and key concerns. + +## Risk Level Assessment +Classify as High, Medium, or Low with clear justification based on the data. + +## Critical Findings +List the most important security findings that represent the highest risk. + +## Credential/Sensitive Data Exposure Analysis +Analyze what credentials were found, which are decrypted, and the potential impact +as well as PII, sensitive documents, and other data that could be exploited. + +## Attack Surface Analysis +Based on file types and applications, what attack surface exists and what could be targeted. + +Be concise, factual, and focus on risk impact rather than detection methods.""" + + self.postgres_connection_url = get_postgres_connection_str() + + def get_prompt(self) -> str: + """Get the reporting prompt from database or use default.""" + try: + prompt_data = self.prompt_manager.get_prompt(self.name) + + if prompt_data: + return prompt_data["prompt"] + else: + logger.info("No prompt found in database, initializing with default", agent_name=self.name) + success = self.prompt_manager.save_prompt(self.name, self.system_prompt, self.description) + if success: + logger.info("Default prompt saved to database", agent_name=self.name) + else: + logger.debug( + "Could not save default prompt to database (likely during startup)", agent_name=self.name + ) + + return self.system_prompt + + except Exception as e: + logger.warning("Error managing prompt, using default", agent_name=self.name, error=str(e)) + return self.system_prompt + + def execute(self, ctx: WorkflowActivityContext, activity_input: dict) -> dict: + """Generate risk assessment report based on provided statistics.""" + report_data = activity_input.get("report_data", {}) + report_type = activity_input.get("report_type", "source") + source_name = activity_input.get("source_name", "Unknown") + + logger.debug("reporting_agent started", report_type=report_type, source_name=source_name) + + model = ModelManager.get_model() + + if not model: + logger.warning("No model available from ModelManager") + return {"success": False, "error": "AI model not available for report synthesis"} + + try: + # Get the current prompt from database or default + current_prompt = self.get_prompt() + + agent = Agent( + model=model, + system_prompt=current_prompt, + output_type=ReportSynthesisResponse, + instrument=ModelManager.is_instrumentation_enabled(), + retries=3, + model_settings=ModelSettings(temperature=self.llm_temperature), + ) + + # Build the analysis prompt with the report data + if report_type == "source": + analysis_prompt = self._build_source_prompt(source_name, report_data) + else: + analysis_prompt = self._build_system_prompt(report_data) + + # Estimate token count (rough estimate: 4 chars per token) + estimated_tokens = len(analysis_prompt) // 4 + max_tokens = activity_input.get("max_tokens", 150000) + + if estimated_tokens > max_tokens: + logger.warning( + "Report data exceeds token limit, truncating", + estimated_tokens=estimated_tokens, + max_tokens=max_tokens, + ) + # Truncate the prompt to fit within token limit + analysis_prompt = analysis_prompt[: max_tokens * 4] + + result = agent.run_sync(analysis_prompt) + + logger.debug( + "Report synthesis completed", + report_type=report_type, + source_name=source_name, + total_tokens=result.usage().total_tokens, + request_tokens=result.usage().request_tokens, + response_tokens=result.usage().response_tokens, + ) + + synthesis = result.output + + return { + "success": True, + "risk_level": synthesis.risk_level, + "executive_summary": synthesis.executive_summary, + "critical_findings": synthesis.critical_findings, + "credential_exposure": synthesis.credential_exposure, + "sensitive_data_exposure": synthesis.sensitive_data_exposure, + "attack_surface": synthesis.attack_surface, + "full_report_markdown": synthesis.full_report_markdown, + "token_usage": result.usage().total_tokens, + } + + except Exception as e: + logger.error("Report synthesis failed", report_type=report_type, source_name=source_name, error=str(e)) + return {"success": False, "error": str(e)} + + def _build_source_prompt(self, source_name: str, report_data: dict) -> str: + """Build analysis prompt for a source-specific report.""" + prompt = f"""# Risk Assessment Request for Source: {source_name} + +Please analyze the following data and provide a comprehensive risk assessment. + +## Summary Statistics +{json.dumps(report_data.get('summary', {}), indent=2)} + +## Risk Indicators +{json.dumps(report_data.get('risk_indicators', {}), indent=2)} + +## Findings Analysis +{json.dumps(report_data.get('findings_detail', {}), indent=2)} + +## Top Verified Findings +""" + # Add top findings with details + top_findings = report_data.get("top_findings", []) + if top_findings: + for i, finding in enumerate(top_findings[:10], 1): + prompt += f"\n{i}. **{finding.get('finding_name', 'Unknown')}** (Severity: {finding.get('severity', 'N/A')})\n" + prompt += f" - Category: {finding.get('category', 'N/A')}\n" + prompt += f" - Triage: {finding.get('triage_state', 'untriaged')}\n" + prompt += f" - File: {finding.get('file_path', 'N/A')}\n" + else: + prompt += "\nNo findings available.\n" + + prompt += "\n\nBased on this data, provide your risk assessment focusing on what an attacker could access and the potential impact." + + return prompt + + def _build_system_prompt(self, report_data: dict) -> str: + """Build analysis prompt for a system-wide report.""" + prompt = """# System-Wide Risk Assessment Request + +Please analyze the following system-wide data and provide a comprehensive risk assessment. + +## Overall Summary +""" + prompt += json.dumps(report_data.get("summary", {}), indent=2) + + prompt += "\n\n## Findings Breakdown\n" + prompt += f"By Category: {json.dumps(report_data.get('findings_by_category', {}), indent=2)}\n" + prompt += f"By Severity: {json.dumps(report_data.get('findings_by_severity', {}), indent=2)}\n" + + prompt += "\n\n## Source Summary\n" + sources = report_data.get("sources", []) + if sources: + prompt += f"Total Sources: {len(sources)}\n\n" + for source in sources[:20]: # Top 20 sources + prompt += f"- **{source.get('source', 'Unknown')}**: {source.get('file_count', 0)} files, {source.get('finding_count', 0)} findings ({source.get('verified_findings', 0)} verified)\n" + else: + prompt += "No sources available.\n" + + prompt += "\n\nBased on this system-wide data, provide your risk assessment focusing on overall exposure and impact." + + return prompt + + +def generate_report(ctx: WorkflowActivityContext, activity_input: dict) -> dict: + """Wrapper function to maintain compatibility with existing workflow calls.""" + agent = ReportingAgent() + return agent.execute(ctx, activity_input) diff --git a/projects/agents/agents/tasks/summarizer.py b/projects/agents/agents/tasks/summarizer.py new file mode 100644 index 0000000..bd652f9 --- /dev/null +++ b/projects/agents/agents/tasks/summarizer.py @@ -0,0 +1,182 @@ +"""Text summarization agent using Pydantic AI.""" + +import json +import tempfile + +import psycopg +import structlog +from agents.base_agent import BaseAgent +from agents.model_manager import ModelManager +from agents.prompt_manager import PromptManager +from agents.schemas import SummaryResponse +from common.db import get_postgres_connection_str +from common.state_helpers import get_file_enriched +from common.storage import StorageMinio +from dapr.ext.workflow.workflow_activity_context import WorkflowActivityContext +from pydantic_ai import Agent +from pydantic_ai.settings import ModelSettings + +logger = structlog.get_logger(__name__) + + +class TextSummarizer(BaseAgent): + """Agent for summarizing text content using LLM.""" + + def __init__(self): + super().__init__() + self.prompt_manager = PromptManager(get_postgres_connection_str()) + self.name = "Text Summarizer" + self.description = "Creates concise summaries of text content using LLM analysis" + self.agent_type = "llm_based" + self.has_prompt = True + self.llm_temperature = 0.3 + self.system_prompt = """You are a document summarization assistant. Create a concise but thorough summary of the provided text. Focus on key points and main ideas. Include section headers to organize the summary. Use markdown formatting.""" + self.storage = StorageMinio() + self.postgres_connection_url = get_postgres_connection_str() + + def _get_text_content(self, object_id: str) -> str: + """Get text content from file or extracted_text transform.""" + try: + file_enriched = get_file_enriched(object_id) + + if file_enriched.is_plaintext: + try: + file_bytes = self.storage.download_bytes(object_id) + return file_bytes.decode("utf-8", errors="replace") + except Exception as e: + logger.warning(f"Failed to decode plaintext file content: {e}") + + # Look for extracted_text transform + with psycopg.connect(self.postgres_connection_url) as conn: + with conn.cursor() as cur: + cur.execute( + """ + SELECT transform_object_id + FROM transforms + WHERE object_id = %s AND type = 'extracted_text' + LIMIT 1 + """, + (object_id,), + ) + result = cur.fetchone() + transform_object_id = result[0] if result else None + if transform_object_id: + transform_object_id = str(transform_object_id) + try: + transform_bytes = self.storage.download_bytes(transform_object_id) + return transform_bytes.decode("utf-8", errors="replace") + except Exception as e: + logger.error(f"Failed to get extracted text transform content: {e}") + + return "" + + except Exception as e: + logger.error(f"Error getting text content: {e}") + return "" + + def get_prompt(self) -> str: + """Get the text summarization prompt from database or use default.""" + try: + # Try to get prompt from database + prompt_data = self.prompt_manager.get_prompt(self.name) + + if prompt_data: + return prompt_data["prompt"] + else: + # No prompt in database, try to save default + logger.info("No prompt found in database, initializing with default", agent_name=self.name) + success = self.prompt_manager.save_prompt(self.name, self.system_prompt, self.description) + if success: + logger.info("Default prompt saved to database", agent_name=self.name) + else: + # This is expected during startup when event loop is running + logger.debug( + "Could not save default prompt to database (likely during startup)", agent_name=self.name + ) + + return self.system_prompt + + except Exception as e: + logger.warning("Error managing prompt, using default", agent_name=self.name, error=str(e)) + return self.system_prompt + + def execute(self, ctx: WorkflowActivityContext, activity_input: dict) -> dict: + """Summarize text content in the given file.""" + object_id = activity_input.get("object_id", "") + + logger.debug("text_summarization activity started", object_id=object_id) + + model = ModelManager.get_model() + + if not model: + logger.warning("No model available from ModelManager") + return {"success": False, "error": "AI model not available for text summarization"} + + try: + # Get text content + text_content = self._get_text_content(object_id) + if not text_content: + return {"success": False, "error": "No text content found to summarize"} + + # Get the current prompt from database or default + current_prompt = self.get_prompt() + + agent = Agent( + model=model, + system_prompt=current_prompt, + output_type=SummaryResponse, + instrument=ModelManager.is_instrumentation_enabled(), + retries=3, + model_settings=ModelSettings(temperature=self.llm_temperature), + ) + + prompt = f"Please summarize this text:\n\n{text_content}" + + result = agent.run_sync(prompt) + logger.debug( + "Text summarization LLM analysis completed", + object_id=object_id, + total_tokens=result.usage().total_tokens, + request_tokens=result.usage().request_tokens, + response_tokens=result.usage().response_tokens, + ) + + summary = result.output.summary + + # Store the summary as a file + with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8") as tmp_summary: + tmp_summary.write(summary) + tmp_summary.flush() + summary_id = self.storage.upload_file(tmp_summary.name) + + # Add transform to database + with psycopg.connect(self.postgres_connection_url) as conn: + with conn.cursor() as cur: + metadata = { + "file_name": "text_summary.md", + "display_type_in_dashboard": "markdown", + "display_title": "Text Summary", + "default_display": True, + } + + cur.execute( + """ + INSERT INTO transforms (object_id, type, transform_object_id, metadata) + VALUES (%s, %s, %s, %s) + """, + (object_id, "text_summary", summary_id, json.dumps(metadata)), + ) + conn.commit() + + logger.debug("Text summarization completed", object_id=object_id) + return {"success": True, "transform_id": summary_id} + + except Exception as e: + logger.error("Text summarization failed", object_id=object_id, error=str(e)) + return {"success": False, "error": str(e)} + + +def summarize_text(ctx: WorkflowActivityContext, activity_input: dict) -> dict: + """Wrapper function to maintain compatibility with existing workflow calls.""" + agent = TextSummarizer() + return agent.execute(ctx, activity_input) diff --git a/projects/agents/agents/tasks/translate.py b/projects/agents/agents/tasks/translate.py new file mode 100644 index 0000000..af854e1 --- /dev/null +++ b/projects/agents/agents/tasks/translate.py @@ -0,0 +1,184 @@ +"""Text translation agent using Pydantic AI.""" + +import json +import tempfile + +import psycopg +import structlog +from agents.base_agent import BaseAgent +from agents.model_manager import ModelManager +from agents.prompt_manager import PromptManager +from agents.schemas import TranslationResponse +from common.db import get_postgres_connection_str +from common.state_helpers import get_file_enriched +from common.storage import StorageMinio +from dapr.ext.workflow.workflow_activity_context import WorkflowActivityContext +from pydantic_ai import Agent +from pydantic_ai.settings import ModelSettings + +logger = structlog.get_logger(__name__) + + +class TextTranslator(BaseAgent): + """Agent for translating text content using LLM.""" + + def __init__(self): + super().__init__() + self.prompt_manager = PromptManager(get_postgres_connection_str()) + self.name = "Text Translator" + self.description = "Translates text content to a specified target language using a LLM" + self.agent_type = "llm_based" + self.has_prompt = True + self.llm_temperature = 0.3 + self.system_prompt = """You are a document translation assistant. Translate the provided text to the specified target language. Preserve the original formatting, structure, and meaning as much as possible. If the document contains multiple languages, translate all text to the target language. Use markdown formatting where appropriate.""" + self.storage = StorageMinio() + self.postgres_connection_url = get_postgres_connection_str() + + def _get_text_content(self, object_id: str) -> str: + """Get text content from file or extracted_text transform.""" + try: + file_enriched = get_file_enriched(object_id) + + if file_enriched.is_plaintext: + try: + file_bytes = self.storage.download_bytes(object_id) + return file_bytes.decode("utf-8", errors="replace") + except Exception as e: + logger.warning(f"Failed to decode plaintext file content: {e}") + + # Look for extracted_text transform + with psycopg.connect(self.postgres_connection_url) as conn: + with conn.cursor() as cur: + cur.execute( + """ + SELECT transform_object_id + FROM transforms + WHERE object_id = %s AND type = 'extracted_text' + LIMIT 1 + """, + (object_id,), + ) + result = cur.fetchone() + transform_object_id = result[0] if result else None + if transform_object_id: + transform_object_id = str(transform_object_id) + try: + transform_bytes = self.storage.download_bytes(transform_object_id) + return transform_bytes.decode("utf-8", errors="replace") + except Exception as e: + logger.error(f"Failed to get extracted text transform content: {e}") + + return "" + + except Exception as e: + logger.error(f"Error getting text content: {e}") + return "" + + def get_prompt(self) -> str: + """Get the text translation prompt from database or use default.""" + try: + # Try to get prompt from database + prompt_data = self.prompt_manager.get_prompt(self.name) + + if prompt_data: + return prompt_data["prompt"] + else: + # No prompt in database, try to save default + logger.info("No prompt found in database, initializing with default", agent_name=self.name) + success = self.prompt_manager.save_prompt(self.name, self.system_prompt, self.description) + if success: + logger.info("Default prompt saved to database", agent_name=self.name) + else: + # This is expected during startup when event loop is running + logger.debug( + "Could not save default prompt to database (likely during startup)", agent_name=self.name + ) + + return self.system_prompt + + except Exception as e: + logger.warning("Error managing prompt, using default", agent_name=self.name, error=str(e)) + return self.system_prompt + + def execute(self, ctx: WorkflowActivityContext, activity_input: dict) -> dict: + """Translate text content in the given file to the specified target language.""" + object_id = activity_input.get("object_id", "") + target_language = activity_input.get("target_language", "English") + + logger.debug("text_translation activity started", object_id=object_id, target_language=target_language) + + model = ModelManager.get_model() + + if not model: + logger.warning("No model available from ModelManager") + return {"success": False, "error": "AI model not available for text translation"} + + try: + # Get text content + text_content = self._get_text_content(object_id) + if not text_content: + return {"success": False, "error": "No text content found to translate"} + + # Get the current prompt from database or default + current_prompt = self.get_prompt() + + agent = Agent( + model=model, + system_prompt=current_prompt, + output_type=TranslationResponse, + instrument=ModelManager.is_instrumentation_enabled(), + retries=3, + model_settings=ModelSettings(temperature=self.llm_temperature), + ) + + prompt = f"Please translate this text to {target_language}. If the document contains multiple languages, translate all text to {target_language}:\n\n{text_content}" + + result = agent.run_sync(prompt) + logger.debug( + "Text translation LLM analysis completed", + object_id=object_id, + target_language=target_language, + total_tokens=result.usage().total_tokens, + request_tokens=result.usage().request_tokens, + response_tokens=result.usage().response_tokens, + ) + + translated_text = result.output.translated_text + + # Store the translation as a file + with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8") as tmp_translation: + tmp_translation.write(translated_text) + tmp_translation.flush() + translation_id = self.storage.upload_file(tmp_translation.name) + + # Add transform to database + with psycopg.connect(self.postgres_connection_url) as conn: + with conn.cursor() as cur: + metadata = { + "file_name": f"translation_{target_language.lower().replace(' ', '_')}.md", + "display_type_in_dashboard": "markdown", + "display_title": f"Translation ({target_language})", + "default_display": True, + } + + cur.execute( + """ + INSERT INTO transforms (object_id, type, transform_object_id, metadata) + VALUES (%s, %s, %s, %s) + """, + (object_id, "text_translation", translation_id, json.dumps(metadata)), + ) + conn.commit() + + logger.debug("Text translation completed", object_id=object_id, target_language=target_language) + return {"success": True, "transform_id": translation_id} + + except Exception as e: + logger.error("Text translation failed", object_id=object_id, error=str(e)) + return {"success": False, "error": str(e)} + + +def translate_text(ctx: WorkflowActivityContext, activity_input: dict) -> dict: + """Wrapper function to maintain compatibility with existing workflow calls.""" + agent = TextTranslator() + return agent.execute(ctx, activity_input) diff --git a/projects/agents/agents/tasks/validate.py b/projects/agents/agents/tasks/validate.py new file mode 100644 index 0000000..d899721 --- /dev/null +++ b/projects/agents/agents/tasks/validate.py @@ -0,0 +1,151 @@ +"""Validation agent for security findings using Pydantic AI.""" + +import structlog +from agents.base_agent import BaseAgent +from agents.logger import set_agent_metadata +from agents.model_manager import ModelManager +from agents.prompt_manager import PromptManager +from agents.schemas import TriageCategory, ValidateResponse +from common.db import get_postgres_connection_str +from dapr.ext.workflow.workflow_activity_context import WorkflowActivityContext +from pydantic_ai import Agent +from pydantic_ai.settings import ModelSettings + +logger = structlog.get_logger(__name__) + + +class ValidationAgent(BaseAgent): + """Agent for validating security findings.""" + + def __init__(self): + super().__init__() + self.prompt_manager = PromptManager(get_postgres_connection_str()) + self.name = "Finding Validator" + self.description = ( + "Validates security findings by triaging them as true positives, false positives, or needing review" + ) + self.agent_type = "llm_based" + self.has_prompt = True + self.llm_temperature = 0.2 # Low temperature for validation tasks + self.system_prompt = """ +You are a cybersecurity expert triaging security findings. +You are an expert information security analyst skilled at triaging security findings. + +Classify each finding as exactly one of: +- "true_positive": genuine security issue (not from test/mock data) +- "false_positive": incorrect match or test/sample/mock data (look for placeholders) or incorrect regex match +- "needs_review": insufficient information to decide + +Consider file path, contents, and context. You need to be VERY sure for a true_positive. +If this is a true_positive, also return a short sentence of context of what an attacker could do +with this information (i.e., the risk). If it's not a true_positive omit this context. +""" + + def get_prompt(self) -> str: + """Get the validation prompt from database or use default.""" + try: + # Try to get prompt from database + prompt_data = self.prompt_manager.get_prompt(self.name) + + if prompt_data: + return prompt_data["prompt"] + else: + # No prompt in database, try to save default + logger.info("No prompt found in database, initializing with default", agent_name=self.name) + success = self.prompt_manager.save_prompt(self.name, self.system_prompt, self.description) + if success: + logger.info("Default prompt saved to database", agent_name=self.name) + else: + # This is expected during startup when event loop is running + logger.debug( + "Could not save default prompt to database (likely during startup)", agent_name=self.name + ) + + return self.system_prompt + + except Exception as e: + logger.warning("Error managing prompt, using default", agent_name=self.name, error=str(e)) + return self.system_prompt + + def execute(self, ctx: WorkflowActivityContext, activity_input: dict) -> dict: + """Validate a security finding.""" + file_path = activity_input.get("file_path", "") + object_id = activity_input.get("object_id", "") + finding_id = activity_input.get("finding_id", "") + summary = activity_input.get("summary", "") + + logger.debug("validate_finding activity started", file_path=file_path) + + model = ModelManager.get_model() + + if not model: + logger.warning("No model available from ModelManager") + return { + "decision": TriageCategory.NOT_TRIAGED, + "explanation": "AI model not available for validation", + "confidence": 0.0, + } + + try: + # Set metadata for this agent run - this will be picked up by our custom processor + set_agent_metadata( + agent_name="security_finding_validator", + file_path=file_path, + object_id=object_id, + finding_id=finding_id, + finding_type="finding_validation", + tags=["validation"], + ) + + # Get the current prompt from database or default + current_prompt = self.get_prompt() + + agent = Agent( + model=model, + system_prompt=current_prompt, + output_type=ValidateResponse, + instrument=ModelManager.is_instrumentation_enabled(), + retries=3, + model_settings=ModelSettings(temperature=self.llm_temperature), + ) + + prompt = f"""Please triage the following security finding: + +**File Path:** {file_path} +**Finding Summary:** +{summary}""" + + result = agent.run_sync(prompt) + logger.debug( + "Finding LLM validation completed", + file_path=file_path, + total_tokens=result.usage().total_tokens, + request_tokens=result.usage().request_tokens, + response_tokens=result.usage().response_tokens, + ) + + response = { + "decision": result.output.decision, + "explanation": result.output.explanation, + "confidence": result.output.confidence, + } + + # Only include true_positive_context if it exists + if result.output.true_positive_context: + response["true_positive_context"] = result.output.true_positive_context + + return response + + except Exception as e: + logger.error("Validation failed", file_path=file_path, error=str(e)) + return { + "decision": TriageCategory.NOT_TRIAGED, + "explanation": f"Validation error: {str(e)}", + "confidence": 0.0, + } + + +def validate_finding(ctx: WorkflowActivityContext, activity_input: dict) -> dict: + """Wrapper function to maintain compatibility with existing workflow calls.""" + agent = ValidationAgent() + return agent.execute(ctx, activity_input) diff --git a/projects/agents/poetry.lock b/projects/agents/poetry.lock new file mode 100644 index 0000000..08a223e --- /dev/null +++ b/projects/agents/poetry.lock @@ -0,0 +1,4155 @@ +# This file is automatically @generated by Poetry 2.0.1 and should not be changed by hand. + +[[package]] +name = "ag-ui-protocol" +version = "0.1.9" +description = "" +optional = false +python-versions = "<4.0,>=3.9" +groups = ["main"] +files = [ + {file = "ag_ui_protocol-0.1.9-py3-none-any.whl", hash = "sha256:44c1238b0576a3915b3a16e1b3855724e08e92ebc96b1ff29379fbd3bfbd400b"}, + {file = "ag_ui_protocol-0.1.9.tar.gz", hash = "sha256:94d75e3919ff75e0b608a7eed445062ea0e6f11cd33b3386a7649047e0c7abd3"}, +] + +[package.dependencies] +pydantic = ">=2.11.2,<3.0.0" + +[[package]] +name = "aiohappyeyeballs" +version = "2.6.1" +description = "Happy Eyeballs for asyncio" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8"}, + {file = "aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558"}, +] + +[[package]] +name = "aiohttp" +version = "3.13.0" +description = "Async http client/server framework (asyncio)" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "aiohttp-3.13.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ca69ec38adf5cadcc21d0b25e2144f6a25b7db7bea7e730bac25075bc305eff0"}, + {file = "aiohttp-3.13.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:240f99f88a9a6beb53ebadac79a2e3417247aa756202ed234b1dbae13d248092"}, + {file = "aiohttp-3.13.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a4676b978a9711531e7cea499d4cdc0794c617a1c0579310ab46c9fdf5877702"}, + {file = "aiohttp-3.13.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:48fcdd5bc771cbbab8ccc9588b8b6447f6a30f9fe00898b1a5107098e00d6793"}, + {file = "aiohttp-3.13.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:eeea0cdd2f687e210c8f605f322d7b0300ba55145014a5dbe98bd4be6fff1f6c"}, + {file = "aiohttp-3.13.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:10b3f01d5aeb632adaaf39c5e93f040a550464a768d54c514050c635adcbb9d0"}, + {file = "aiohttp-3.13.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a4dc0b83e25267f42ef065ea57653de4365b56d7bc4e4cfc94fabe56998f8ee6"}, + {file = "aiohttp-3.13.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:72714919ed9b90f030f761c20670e529c4af96c31bd000917dd0c9afd1afb731"}, + {file = "aiohttp-3.13.0-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:564be41e85318403fdb176e9e5b3e852d528392f42f2c1d1efcbeeed481126d7"}, + {file = "aiohttp-3.13.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:84912962071087286333f70569362e10793f73f45c48854e6859df11001eb2d3"}, + {file = "aiohttp-3.13.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:90b570f1a146181c3d6ae8f755de66227ded49d30d050479b5ae07710f7894c5"}, + {file = "aiohttp-3.13.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:2d71ca30257ce756e37a6078b1dff2d9475fee13609ad831eac9a6531bea903b"}, + {file = "aiohttp-3.13.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:cd45eb70eca63f41bb156b7dffbe1a7760153b69892d923bdb79a74099e2ed90"}, + {file = "aiohttp-3.13.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:5ae3a19949a27982c7425a7a5a963c1268fdbabf0be15ab59448cbcf0f992519"}, + {file = "aiohttp-3.13.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ea6df292013c9f050cbf3f93eee9953d6e5acd9e64a0bf4ca16404bfd7aa9bcc"}, + {file = "aiohttp-3.13.0-cp310-cp310-win32.whl", hash = "sha256:3b64f22fbb6dcd5663de5ef2d847a5638646ef99112503e6f7704bdecb0d1c4d"}, + {file = "aiohttp-3.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:f8d877aa60d80715b2afc565f0f1aea66565824c229a2d065b31670e09fed6d7"}, + {file = "aiohttp-3.13.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:99eb94e97a42367fef5fc11e28cb2362809d3e70837f6e60557816c7106e2e20"}, + {file = "aiohttp-3.13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4696665b2713021c6eba3e2b882a86013763b442577fe5d2056a42111e732eca"}, + {file = "aiohttp-3.13.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3e6a38366f7f0d0f6ed7a1198055150c52fda552b107dad4785c0852ad7685d1"}, + {file = "aiohttp-3.13.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aab715b1a0c37f7f11f9f1f579c6fbaa51ef569e47e3c0a4644fba46077a9409"}, + {file = "aiohttp-3.13.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7972c82bed87d7bd8e374b60a6b6e816d75ba4f7c2627c2d14eed216e62738e1"}, + {file = "aiohttp-3.13.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ca8313cb852af788c78d5afdea24c40172cbfff8b35e58b407467732fde20390"}, + {file = "aiohttp-3.13.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6c333a2385d2a6298265f4b3e960590f787311b87f6b5e6e21bb8375914ef504"}, + {file = "aiohttp-3.13.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cc6d5fc5edbfb8041d9607f6a417997fa4d02de78284d386bea7ab767b5ea4f3"}, + {file = "aiohttp-3.13.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7ddedba3d0043349edc79df3dc2da49c72b06d59a45a42c1c8d987e6b8d175b8"}, + {file = "aiohttp-3.13.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:23ca762140159417a6bbc959ca1927f6949711851e56f2181ddfe8d63512b5ad"}, + {file = "aiohttp-3.13.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:bfe824d6707a5dc3c5676685f624bc0c63c40d79dc0239a7fd6c034b98c25ebe"}, + {file = "aiohttp-3.13.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:3c11fa5dd2ef773a8a5a6daa40243d83b450915992eab021789498dc87acc114"}, + {file = "aiohttp-3.13.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:00fdfe370cffede3163ba9d3f190b32c0cfc8c774f6f67395683d7b0e48cdb8a"}, + {file = "aiohttp-3.13.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:6475e42ef92717a678bfbf50885a682bb360a6f9c8819fb1a388d98198fdcb80"}, + {file = "aiohttp-3.13.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:77da5305a410910218b99f2a963092f4277d8a9c1f429c1ff1b026d1826bd0b6"}, + {file = "aiohttp-3.13.0-cp311-cp311-win32.whl", hash = "sha256:2f9d9ea547618d907f2ee6670c9a951f059c5994e4b6de8dcf7d9747b420c820"}, + {file = "aiohttp-3.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:0f19f7798996d4458c669bd770504f710014926e9970f4729cf55853ae200469"}, + {file = "aiohttp-3.13.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1c272a9a18a5ecc48a7101882230046b83023bb2a662050ecb9bfcb28d9ab53a"}, + {file = "aiohttp-3.13.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:97891a23d7fd4e1afe9c2f4473e04595e4acb18e4733b910b6577b74e7e21985"}, + {file = "aiohttp-3.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:475bd56492ce5f4cffe32b5533c6533ee0c406d1d0e6924879f83adcf51da0ae"}, + {file = "aiohttp-3.13.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c32ada0abb4bc94c30be2b681c42f058ab104d048da6f0148280a51ce98add8c"}, + {file = "aiohttp-3.13.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:4af1f8877ca46ecdd0bc0d4a6b66d4b2bddc84a79e2e8366bc0d5308e76bceb8"}, + {file = "aiohttp-3.13.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e04ab827ec4f775817736b20cdc8350f40327f9b598dec4e18c9ffdcbea88a93"}, + {file = "aiohttp-3.13.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a6d9487b9471ec36b0faedf52228cd732e89be0a2bbd649af890b5e2ce422353"}, + {file = "aiohttp-3.13.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e66c57416352f36bf98f6641ddadd47c93740a22af7150d3e9a1ef6e983f9a8"}, + {file = "aiohttp-3.13.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:469167d5372f5bb3aedff4fc53035d593884fff2617a75317740e885acd48b04"}, + {file = "aiohttp-3.13.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a9f3546b503975a69b547c9fd1582cad10ede1ce6f3e313a2f547c73a3d7814f"}, + {file = "aiohttp-3.13.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:6b4174fcec98601f0cfdf308ee29a6ae53c55f14359e848dab4e94009112ee7d"}, + {file = "aiohttp-3.13.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a533873a7a4ec2270fb362ee5a0d3b98752e4e1dc9042b257cd54545a96bd8ed"}, + {file = "aiohttp-3.13.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:ce887c5e54411d607ee0959cac15bb31d506d86a9bcaddf0b7e9d63325a7a802"}, + {file = "aiohttp-3.13.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:d871f6a30d43e32fc9252dc7b9febe1a042b3ff3908aa83868d7cf7c9579a59b"}, + {file = "aiohttp-3.13.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:222c828243b4789d79a706a876910f656fad4381661691220ba57b2ab4547865"}, + {file = "aiohttp-3.13.0-cp312-cp312-win32.whl", hash = "sha256:682d2e434ff2f1108314ff7f056ce44e457f12dbed0249b24e106e385cf154b9"}, + {file = "aiohttp-3.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:0a2be20eb23888df130214b91c262a90e2de1553d6fb7de9e9010cec994c0ff2"}, + {file = "aiohttp-3.13.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:00243e51f16f6ec0fb021659d4af92f675f3cf9f9b39efd142aa3ad641d8d1e6"}, + {file = "aiohttp-3.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:059978d2fddc462e9211362cbc8446747ecd930537fa559d3d25c256f032ff54"}, + {file = "aiohttp-3.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:564b36512a7da3b386143c611867e3f7cfb249300a1bf60889bd9985da67ab77"}, + {file = "aiohttp-3.13.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4aa995b9156ae499393d949a456a7ab0b994a8241a96db73a3b73c7a090eff6a"}, + {file = "aiohttp-3.13.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:55ca0e95a3905f62f00900255ed807c580775174252999286f283e646d675a49"}, + {file = "aiohttp-3.13.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:49ce7525853a981fc35d380aa2353536a01a9ec1b30979ea4e35966316cace7e"}, + {file = "aiohttp-3.13.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2117be9883501eaf95503bd313eb4c7a23d567edd44014ba15835a1e9ec6d852"}, + {file = "aiohttp-3.13.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d169c47e40c911f728439da853b6fd06da83761012e6e76f11cb62cddae7282b"}, + {file = "aiohttp-3.13.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:703ad3f742fc81e543638a7bebddd35acadaa0004a5e00535e795f4b6f2c25ca"}, + {file = "aiohttp-3.13.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5bf635c3476f4119b940cc8d94ad454cbe0c377e61b4527f0192aabeac1e9370"}, + {file = "aiohttp-3.13.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:cfe6285ef99e7ee51cef20609be2bc1dd0e8446462b71c9db8bb296ba632810a"}, + {file = "aiohttp-3.13.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:34d8af6391c5f2e69749d7f037b614b8c5c42093c251f336bdbfa4b03c57d6c4"}, + {file = "aiohttp-3.13.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:12f5d820fadc5848d4559ea838aef733cf37ed2a1103bba148ac2f5547c14c29"}, + {file = "aiohttp-3.13.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:0f1338b61ea66f4757a0544ed8a02ccbf60e38d9cfb3225888888dd4475ebb96"}, + {file = "aiohttp-3.13.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:582770f82513419512da096e8df21ca44f86a2e56e25dc93c5ab4df0fe065bf0"}, + {file = "aiohttp-3.13.0-cp313-cp313-win32.whl", hash = "sha256:3194b8cab8dbc882f37c13ef1262e0a3d62064fa97533d3aa124771f7bf1ecee"}, + {file = "aiohttp-3.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:7897298b3eedc790257fef8a6ec582ca04e9dbe568ba4a9a890913b925b8ea21"}, + {file = "aiohttp-3.13.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:c417f8c2e1137775569297c584a8a7144e5d1237789eae56af4faf1894a0b861"}, + {file = "aiohttp-3.13.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:f84b53326abf8e56ebc28a35cebf4a0f396a13a76300f500ab11fe0573bf0b52"}, + {file = "aiohttp-3.13.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:990a53b9d6a30b2878789e490758e568b12b4a7fb2527d0c89deb9650b0e5813"}, + {file = "aiohttp-3.13.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c811612711e01b901e18964b3e5dec0d35525150f5f3f85d0aee2935f059910a"}, + {file = "aiohttp-3.13.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:ee433e594d7948e760b5c2a78cc06ac219df33b0848793cf9513d486a9f90a52"}, + {file = "aiohttp-3.13.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:19bb08e56f57c215e9572cd65cb6f8097804412c54081d933997ddde3e5ac579"}, + {file = "aiohttp-3.13.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f27b7488144eb5dd9151cf839b195edd1569629d90ace4c5b6b18e4e75d1e63a"}, + {file = "aiohttp-3.13.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d812838c109757a11354a161c95708ae4199c4fd4d82b90959b20914c1d097f6"}, + {file = "aiohttp-3.13.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7c20db99da682f9180fa5195c90b80b159632fb611e8dbccdd99ba0be0970620"}, + {file = "aiohttp-3.13.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:cf8b0870047900eb1f17f453b4b3953b8ffbf203ef56c2f346780ff930a4d430"}, + {file = "aiohttp-3.13.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:5b8a5557d5af3f4e3add52a58c4cf2b8e6e59fc56b261768866f5337872d596d"}, + {file = "aiohttp-3.13.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:052bcdd80c1c54b8a18a9ea0cd5e36f473dc8e38d51b804cea34841f677a9971"}, + {file = "aiohttp-3.13.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:76484ba17b2832776581b7ab466d094e48eba74cb65a60aea20154dae485e8bd"}, + {file = "aiohttp-3.13.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:62d8a0adcdaf62ee56bfb37737153251ac8e4b27845b3ca065862fb01d99e247"}, + {file = "aiohttp-3.13.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5004d727499ecb95f7c9147dd0bfc5b5670f71d355f0bd26d7af2d3af8e07d2f"}, + {file = "aiohttp-3.13.0-cp314-cp314-win32.whl", hash = "sha256:a1c20c26af48aea984f63f96e5d7af7567c32cb527e33b60a0ef0a6313cf8b03"}, + {file = "aiohttp-3.13.0-cp314-cp314-win_amd64.whl", hash = "sha256:56f7d230ec66e799fbfd8350e9544f8a45a4353f1cf40c1fea74c1780f555b8f"}, + {file = "aiohttp-3.13.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:2fd35177dc483ae702f07b86c782f4f4b100a8ce4e7c5778cea016979023d9fd"}, + {file = "aiohttp-3.13.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:4df1984c8804ed336089e88ac81a9417b1fd0db7c6f867c50a9264488797e778"}, + {file = "aiohttp-3.13.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:e68c0076052dd911a81d3acc4ef2911cc4ef65bf7cadbfbc8ae762da24da858f"}, + {file = "aiohttp-3.13.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bc95c49853cd29613e4fe4ff96d73068ff89b89d61e53988442e127e8da8e7ba"}, + {file = "aiohttp-3.13.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3b3bdc89413117b40cc39baae08fd09cbdeb839d421c4e7dce6a34f6b54b3ac1"}, + {file = "aiohttp-3.13.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3e77a729df23be2116acc4e9de2767d8e92445fbca68886dd991dc912f473755"}, + {file = "aiohttp-3.13.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e88ab34826d6eeb6c67e6e92400b9ec653faf5092a35f07465f44c9f1c429f82"}, + {file = "aiohttp-3.13.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:019dbef24fe28ce2301419dd63a2b97250d9760ca63ee2976c2da2e3f182f82e"}, + {file = "aiohttp-3.13.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:2c4aeaedd20771b7b4bcdf0ae791904445df6d856c02fc51d809d12d17cffdc7"}, + {file = "aiohttp-3.13.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:b3a8e6a2058a0240cfde542b641d0e78b594311bc1a710cbcb2e1841417d5cb3"}, + {file = "aiohttp-3.13.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:f8e38d55ca36c15f36d814ea414ecb2401d860de177c49f84a327a25b3ee752b"}, + {file = "aiohttp-3.13.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:a921edbe971aade1bf45bcbb3494e30ba6863a5c78f28be992c42de980fd9108"}, + {file = "aiohttp-3.13.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:474cade59a447cb4019c0dce9f0434bf835fb558ea932f62c686fe07fe6db6a1"}, + {file = "aiohttp-3.13.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:99a303ad960747c33b65b1cb65d01a62ac73fa39b72f08a2e1efa832529b01ed"}, + {file = "aiohttp-3.13.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:bb34001fc1f05f6b323e02c278090c07a47645caae3aa77ed7ed8a3ce6abcce9"}, + {file = "aiohttp-3.13.0-cp314-cp314t-win32.whl", hash = "sha256:dea698b64235d053def7d2f08af9302a69fcd760d1c7bd9988fd5d3b6157e657"}, + {file = "aiohttp-3.13.0-cp314-cp314t-win_amd64.whl", hash = "sha256:1f164699a060c0b3616459d13c1464a981fddf36f892f0a5027cbd45121fb14b"}, + {file = "aiohttp-3.13.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:fcc425fb6fd2a00c6d91c85d084c6b75a61bc8bc12159d08e17c5711df6c5ba4"}, + {file = "aiohttp-3.13.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7c2c4c9ce834801651f81d6760d0a51035b8b239f58f298de25162fcf6f8bb64"}, + {file = "aiohttp-3.13.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f91e8f9053a07177868e813656ec57599cd2a63238844393cd01bd69c2e40147"}, + {file = "aiohttp-3.13.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:df46d9a3d78ec19b495b1107bf26e4fcf97c900279901f4f4819ac5bb2a02a4c"}, + {file = "aiohttp-3.13.0-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3b1eb9871cbe43b6ca6fac3544682971539d8a1d229e6babe43446279679609d"}, + {file = "aiohttp-3.13.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:62a3cddf8d9a2eae1f79585fa81d32e13d0c509bb9e7ad47d33c83b45a944df7"}, + {file = "aiohttp-3.13.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0f735e680c323ee7e9ef8e2ea26425c7dbc2ede0086fa83ce9d7ccab8a089f26"}, + {file = "aiohttp-3.13.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6a51839f778b0e283b43cd82bb17f1835ee2cc1bf1101765e90ae886e53e751c"}, + {file = "aiohttp-3.13.0-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ac90cfab65bc281d6752f22db5fa90419e33220af4b4fa53b51f5948f414c0e7"}, + {file = "aiohttp-3.13.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:62fd54f3e6f17976962ba67f911d62723c760a69d54f5d7b74c3ceb1a4e9ef8d"}, + {file = "aiohttp-3.13.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:cf2b60b65df05b6b2fa0d887f2189991a0dbf44a0dd18359001dc8fcdb7f1163"}, + {file = "aiohttp-3.13.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:1ccedfe280e804d9a9d7fe8b8c4309d28e364b77f40309c86596baa754af50b1"}, + {file = "aiohttp-3.13.0-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:ea01ffbe23df53ece0c8732d1585b3d6079bb8c9ee14f3745daf000051415a31"}, + {file = "aiohttp-3.13.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:19ba8625fa69523627b67f7e9901b587a4952470f68814d79cdc5bc460e9b885"}, + {file = "aiohttp-3.13.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:4b14bfae90598d331b5061fd15a7c290ea0c15b34aeb1cf620464bb5ec02a602"}, + {file = "aiohttp-3.13.0-cp39-cp39-win32.whl", hash = "sha256:cf7a4b976da219e726d0043fc94ae8169c0dba1d3a059b3c1e2c964bafc5a77d"}, + {file = "aiohttp-3.13.0-cp39-cp39-win_amd64.whl", hash = "sha256:6b9697d15231aeaed4786f090c9c8bc3ab5f0e0a6da1e76c135a310def271020"}, + {file = "aiohttp-3.13.0.tar.gz", hash = "sha256:378dbc57dd8cf341ce243f13fa1fa5394d68e2e02c15cd5f28eae35a70ec7f67"}, +] + +[package.dependencies] +aiohappyeyeballs = ">=2.5.0" +aiosignal = ">=1.4.0" +attrs = ">=17.3.0" +frozenlist = ">=1.1.1" +multidict = ">=4.5,<7.0" +propcache = ">=0.2.0" +yarl = ">=1.17.0,<2.0" + +[package.extras] +speedups = ["Brotli", "aiodns (>=3.3.0)", "brotlicffi", "zstandard"] + +[[package]] +name = "aiosignal" +version = "1.4.0" +description = "aiosignal: a list of registered asynchronous callbacks" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e"}, + {file = "aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7"}, +] + +[package.dependencies] +frozenlist = ">=1.1.0" +typing-extensions = {version = ">=4.2", markers = "python_version < \"3.13\""} + +[[package]] +name = "annotated-types" +version = "0.7.0" +description = "Reusable constraint types to use with typing.Annotated" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, + {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, +] + +[[package]] +name = "anthropic" +version = "0.69.0" +description = "The official Python library for the anthropic API" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "anthropic-0.69.0-py3-none-any.whl", hash = "sha256:1f73193040f33f11e27c2cd6ec25f24fe7c3f193dc1c5cde6b7a08b18a16bcc5"}, + {file = "anthropic-0.69.0.tar.gz", hash = "sha256:c604d287f4d73640f40bd2c0f3265a2eb6ce034217ead0608f6b07a8bc5ae5f2"}, +] + +[package.dependencies] +anyio = ">=3.5.0,<5" +distro = ">=1.7.0,<2" +docstring-parser = ">=0.15,<1" +httpx = ">=0.25.0,<1" +jiter = ">=0.4.0,<1" +pydantic = ">=1.9.0,<3" +sniffio = "*" +typing-extensions = ">=4.10,<5" + +[package.extras] +aiohttp = ["aiohttp", "httpx-aiohttp (>=0.1.8)"] +bedrock = ["boto3 (>=1.28.57)", "botocore (>=1.31.57)"] +vertex = ["google-auth[requests] (>=2,<3)"] + +[[package]] +name = "anyio" +version = "4.11.0" +description = "High-level concurrency and networking framework on top of asyncio or Trio" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "anyio-4.11.0-py3-none-any.whl", hash = "sha256:0287e96f4d26d4149305414d4e3bc32f0dcd0862365a4bddea19d7a1ec38c4fc"}, + {file = "anyio-4.11.0.tar.gz", hash = "sha256:82a8d0b81e318cc5ce71a5f1f8b5c4e63619620b63141ef8c995fa0db95a57c4"}, +] + +[package.dependencies] +idna = ">=2.8" +sniffio = ">=1.1" +typing_extensions = {version = ">=4.5", markers = "python_version < \"3.13\""} + +[package.extras] +trio = ["trio (>=0.31.0)"] + +[[package]] +name = "argcomplete" +version = "3.6.2" +description = "Bash tab completion for argparse" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "argcomplete-3.6.2-py3-none-any.whl", hash = "sha256:65b3133a29ad53fb42c48cf5114752c7ab66c1c38544fdf6460f450c09b42591"}, + {file = "argcomplete-3.6.2.tar.gz", hash = "sha256:d0519b1bc867f5f4f4713c41ad0aba73a4a5f007449716b16f385f2166dc6adf"}, +] + +[package.extras] +test = ["coverage", "mypy", "pexpect", "ruff", "wheel"] + +[[package]] +name = "argon2-cffi" +version = "25.1.0" +description = "Argon2 for Python" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "argon2_cffi-25.1.0-py3-none-any.whl", hash = "sha256:fdc8b074db390fccb6eb4a3604ae7231f219aa669a2652e0f20e16ba513d5741"}, + {file = "argon2_cffi-25.1.0.tar.gz", hash = "sha256:694ae5cc8a42f4c4e2bf2ca0e64e51e23a040c6a517a85074683d3959e1346c1"}, +] + +[package.dependencies] +argon2-cffi-bindings = "*" + +[[package]] +name = "argon2-cffi-bindings" +version = "25.1.0" +description = "Low-level CFFI bindings for Argon2" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:3d3f05610594151994ca9ccb3c771115bdb4daef161976a266f0dd8aa9996b8f"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:8b8efee945193e667a396cbc7b4fb7d357297d6234d30a489905d96caabde56b"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:3c6702abc36bf3ccba3f802b799505def420a1b7039862014a65db3205967f5a"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a1c70058c6ab1e352304ac7e3b52554daadacd8d453c1752e547c76e9c99ac44"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e2fd3bfbff3c5d74fef31a722f729bf93500910db650c925c2d6ef879a7e51cb"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c4f9665de60b1b0e99bcd6be4f17d90339698ce954cfd8d9cf4f91c995165a92"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ba92837e4a9aa6a508c8d2d7883ed5a8f6c308c89a4790e1e447a220deb79a85"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-win32.whl", hash = "sha256:84a461d4d84ae1295871329b346a97f68eade8c53b6ed9a7ca2d7467f3c8ff6f"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b55aec3565b65f56455eebc9b9f34130440404f27fe21c3b375bf1ea4d8fbae6"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:87c33a52407e4c41f3b70a9c2d3f6056d88b10dad7695be708c5021673f55623"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:aecba1723ae35330a008418a91ea6cfcedf6d31e5fbaa056a166462ff066d500"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:2630b6240b495dfab90aebe159ff784d08ea999aa4b0d17efa734055a07d2f44"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:7aef0c91e2c0fbca6fc68e7555aa60ef7008a739cbe045541e438373bc54d2b0"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e021e87faa76ae0d413b619fe2b65ab9a037f24c60a1e6cc43457ae20de6dc6"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d3e924cfc503018a714f94a49a149fdc0b644eaead5d1f089330399134fa028a"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:c87b72589133f0346a1cb8d5ecca4b933e3c9b64656c9d175270a000e73b288d"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1db89609c06afa1a214a69a462ea741cf735b29a57530478c06eb81dd403de99"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-win32.whl", hash = "sha256:473bcb5f82924b1becbb637b63303ec8d10e84c8d241119419897a26116515d2"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-win_amd64.whl", hash = "sha256:a98cd7d17e9f7ce244c0803cad3c23a7d379c301ba618a5fa76a67d116618b98"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-win_arm64.whl", hash = "sha256:b0fdbcf513833809c882823f98dc2f931cf659d9a1429616ac3adebb49f5db94"}, + {file = "argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:6dca33a9859abf613e22733131fc9194091c1fa7cb3e131c143056b4856aa47e"}, + {file = "argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:21378b40e1b8d1655dd5310c84a40fc19a9aa5e6366e835ceb8576bf0fea716d"}, + {file = "argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d588dec224e2a83edbdc785a5e6f3c6cd736f46bfd4b441bbb5aa1f5085e584"}, + {file = "argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5acb4e41090d53f17ca1110c3427f0a130f944b896fc8c83973219c97f57b690"}, + {file = "argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:da0c79c23a63723aa5d782250fbf51b768abca630285262fb5144ba5ae01e520"}, + {file = "argon2_cffi_bindings-25.1.0.tar.gz", hash = "sha256:b957f3e6ea4d55d820e40ff76f450952807013d361a65d7f28acc0acbf29229d"}, +] + +[package.dependencies] +cffi = {version = ">=1.0.1", markers = "python_version < \"3.14\""} + +[[package]] +name = "asgiref" +version = "3.10.0" +description = "ASGI specs, helper code, and adapters" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "asgiref-3.10.0-py3-none-any.whl", hash = "sha256:aef8a81283a34d0ab31630c9b7dfe70c812c95eba78171367ca8745e88124734"}, + {file = "asgiref-3.10.0.tar.gz", hash = "sha256:d89f2d8cd8b56dada7d52fa7dc8075baa08fb836560710d38c292a7a3f78c04e"}, +] + +[package.extras] +tests = ["mypy (>=1.14.0)", "pytest", "pytest-asyncio"] + +[[package]] +name = "asyncio" +version = "4.0.0" +description = "Deprecated backport of asyncio; use the stdlib package instead" +optional = false +python-versions = ">=3.4" +groups = ["main"] +files = [ + {file = "asyncio-4.0.0-py3-none-any.whl", hash = "sha256:c1eddb0659231837046809e68103969b2bef8b0400d59cfa6363f6b5ed8cc88b"}, + {file = "asyncio-4.0.0.tar.gz", hash = "sha256:570cd9e50db83bc1629152d4d0b7558d6451bb1bfd5dfc2e935d96fc2f40329b"}, +] + +[[package]] +name = "asyncpg" +version = "0.30.0" +description = "An asyncio PostgreSQL driver" +optional = false +python-versions = ">=3.8.0" +groups = ["main"] +files = [ + {file = "asyncpg-0.30.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bfb4dd5ae0699bad2b233672c8fc5ccbd9ad24b89afded02341786887e37927e"}, + {file = "asyncpg-0.30.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dc1f62c792752a49f88b7e6f774c26077091b44caceb1983509edc18a2222ec0"}, + {file = "asyncpg-0.30.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3152fef2e265c9c24eec4ee3d22b4f4d2703d30614b0b6753e9ed4115c8a146f"}, + {file = "asyncpg-0.30.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c7255812ac85099a0e1ffb81b10dc477b9973345793776b128a23e60148dd1af"}, + {file = "asyncpg-0.30.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:578445f09f45d1ad7abddbff2a3c7f7c291738fdae0abffbeb737d3fc3ab8b75"}, + {file = "asyncpg-0.30.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c42f6bb65a277ce4d93f3fba46b91a265631c8df7250592dd4f11f8b0152150f"}, + {file = "asyncpg-0.30.0-cp310-cp310-win32.whl", hash = "sha256:aa403147d3e07a267ada2ae34dfc9324e67ccc4cdca35261c8c22792ba2b10cf"}, + {file = "asyncpg-0.30.0-cp310-cp310-win_amd64.whl", hash = "sha256:fb622c94db4e13137c4c7f98834185049cc50ee01d8f657ef898b6407c7b9c50"}, + {file = "asyncpg-0.30.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5e0511ad3dec5f6b4f7a9e063591d407eee66b88c14e2ea636f187da1dcfff6a"}, + {file = "asyncpg-0.30.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:915aeb9f79316b43c3207363af12d0e6fd10776641a7de8a01212afd95bdf0ed"}, + {file = "asyncpg-0.30.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c198a00cce9506fcd0bf219a799f38ac7a237745e1d27f0e1f66d3707c84a5a"}, + {file = "asyncpg-0.30.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3326e6d7381799e9735ca2ec9fd7be4d5fef5dcbc3cb555d8a463d8460607956"}, + {file = "asyncpg-0.30.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:51da377487e249e35bd0859661f6ee2b81db11ad1f4fc036194bc9cb2ead5056"}, + {file = "asyncpg-0.30.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bc6d84136f9c4d24d358f3b02be4b6ba358abd09f80737d1ac7c444f36108454"}, + {file = "asyncpg-0.30.0-cp311-cp311-win32.whl", hash = "sha256:574156480df14f64c2d76450a3f3aaaf26105869cad3865041156b38459e935d"}, + {file = "asyncpg-0.30.0-cp311-cp311-win_amd64.whl", hash = "sha256:3356637f0bd830407b5597317b3cb3571387ae52ddc3bca6233682be88bbbc1f"}, + {file = "asyncpg-0.30.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c902a60b52e506d38d7e80e0dd5399f657220f24635fee368117b8b5fce1142e"}, + {file = "asyncpg-0.30.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:aca1548e43bbb9f0f627a04666fedaca23db0a31a84136ad1f868cb15deb6e3a"}, + {file = "asyncpg-0.30.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c2a2ef565400234a633da0eafdce27e843836256d40705d83ab7ec42074efb3"}, + {file = "asyncpg-0.30.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1292b84ee06ac8a2ad8e51c7475aa309245874b61333d97411aab835c4a2f737"}, + {file = "asyncpg-0.30.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0f5712350388d0cd0615caec629ad53c81e506b1abaaf8d14c93f54b35e3595a"}, + {file = "asyncpg-0.30.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:db9891e2d76e6f425746c5d2da01921e9a16b5a71a1c905b13f30e12a257c4af"}, + {file = "asyncpg-0.30.0-cp312-cp312-win32.whl", hash = "sha256:68d71a1be3d83d0570049cd1654a9bdfe506e794ecc98ad0873304a9f35e411e"}, + {file = "asyncpg-0.30.0-cp312-cp312-win_amd64.whl", hash = "sha256:9a0292c6af5c500523949155ec17b7fe01a00ace33b68a476d6b5059f9630305"}, + {file = "asyncpg-0.30.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:05b185ebb8083c8568ea8a40e896d5f7af4b8554b64d7719c0eaa1eb5a5c3a70"}, + {file = "asyncpg-0.30.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c47806b1a8cbb0a0db896f4cd34d89942effe353a5035c62734ab13b9f938da3"}, + {file = "asyncpg-0.30.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b6fde867a74e8c76c71e2f64f80c64c0f3163e687f1763cfaf21633ec24ec33"}, + {file = "asyncpg-0.30.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46973045b567972128a27d40001124fbc821c87a6cade040cfcd4fa8a30bcdc4"}, + {file = "asyncpg-0.30.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9110df111cabc2ed81aad2f35394a00cadf4f2e0635603db6ebbd0fc896f46a4"}, + {file = "asyncpg-0.30.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:04ff0785ae7eed6cc138e73fc67b8e51d54ee7a3ce9b63666ce55a0bf095f7ba"}, + {file = "asyncpg-0.30.0-cp313-cp313-win32.whl", hash = "sha256:ae374585f51c2b444510cdf3595b97ece4f233fde739aa14b50e0d64e8a7a590"}, + {file = "asyncpg-0.30.0-cp313-cp313-win_amd64.whl", hash = "sha256:f59b430b8e27557c3fb9869222559f7417ced18688375825f8f12302c34e915e"}, + {file = "asyncpg-0.30.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:29ff1fc8b5bf724273782ff8b4f57b0f8220a1b2324184846b39d1ab4122031d"}, + {file = "asyncpg-0.30.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:64e899bce0600871b55368b8483e5e3e7f1860c9482e7f12e0a771e747988168"}, + {file = "asyncpg-0.30.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b290f4726a887f75dcd1b3006f484252db37602313f806e9ffc4e5996cfe5cb"}, + {file = "asyncpg-0.30.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f86b0e2cd3f1249d6fe6fd6cfe0cd4538ba994e2d8249c0491925629b9104d0f"}, + {file = "asyncpg-0.30.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:393af4e3214c8fa4c7b86da6364384c0d1b3298d45803375572f415b6f673f38"}, + {file = "asyncpg-0.30.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:fd4406d09208d5b4a14db9a9dbb311b6d7aeeab57bded7ed2f8ea41aeef39b34"}, + {file = "asyncpg-0.30.0-cp38-cp38-win32.whl", hash = "sha256:0b448f0150e1c3b96cb0438a0d0aa4871f1472e58de14a3ec320dbb2798fb0d4"}, + {file = "asyncpg-0.30.0-cp38-cp38-win_amd64.whl", hash = "sha256:f23b836dd90bea21104f69547923a02b167d999ce053f3d502081acea2fba15b"}, + {file = "asyncpg-0.30.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6f4e83f067b35ab5e6371f8a4c93296e0439857b4569850b178a01385e82e9ad"}, + {file = "asyncpg-0.30.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5df69d55add4efcd25ea2a3b02025b669a285b767bfbf06e356d68dbce4234ff"}, + {file = "asyncpg-0.30.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3479a0d9a852c7c84e822c073622baca862d1217b10a02dd57ee4a7a081f708"}, + {file = "asyncpg-0.30.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26683d3b9a62836fad771a18ecf4659a30f348a561279d6227dab96182f46144"}, + {file = "asyncpg-0.30.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1b982daf2441a0ed314bd10817f1606f1c28b1136abd9e4f11335358c2c631cb"}, + {file = "asyncpg-0.30.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1c06a3a50d014b303e5f6fc1e5f95eb28d2cee89cf58384b700da621e5d5e547"}, + {file = "asyncpg-0.30.0-cp39-cp39-win32.whl", hash = "sha256:1b11a555a198b08f5c4baa8f8231c74a366d190755aa4f99aacec5970afe929a"}, + {file = "asyncpg-0.30.0-cp39-cp39-win_amd64.whl", hash = "sha256:8b684a3c858a83cd876f05958823b68e8d14ec01bb0c0d14a6704c5bf9711773"}, + {file = "asyncpg-0.30.0.tar.gz", hash = "sha256:c551e9928ab6707602f44811817f82ba3c446e018bfe1d3abecc8ba5f3eac851"}, +] + +[package.extras] +docs = ["Sphinx (>=8.1.3,<8.2.0)", "sphinx-rtd-theme (>=1.2.2)"] +gssauth = ["gssapi", "sspilib"] +test = ["distro (>=1.9.0,<1.10.0)", "flake8 (>=6.1,<7.0)", "flake8-pyi (>=24.1.0,<24.2.0)", "gssapi", "k5test", "mypy (>=1.8.0,<1.9.0)", "sspilib", "uvloop (>=0.15.3)"] + +[[package]] +name = "attrs" +version = "25.4.0" +description = "Classes Without Boilerplate" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373"}, + {file = "attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11"}, +] + +[[package]] +name = "backoff" +version = "2.2.1" +description = "Function decoration for backoff and retry" +optional = false +python-versions = ">=3.7,<4.0" +groups = ["main"] +files = [ + {file = "backoff-2.2.1-py3-none-any.whl", hash = "sha256:63579f9a0628e06278f7e47b7d7d5b6ce20dc65c5e96a6f3ca99a6adca0396e8"}, + {file = "backoff-2.2.1.tar.gz", hash = "sha256:03f829f5bb1923180821643f8753b0502c3b682293992485b0eef2807afa5cba"}, +] + +[[package]] +name = "boto3" +version = "1.40.50" +description = "The AWS SDK for Python" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "boto3-1.40.50-py3-none-any.whl", hash = "sha256:62901bc616c64236700001f530fc66b659ecd1acb4f541ddac6fcae3a1d37ea6"}, + {file = "boto3-1.40.50.tar.gz", hash = "sha256:ae34363e8f34a49ab130d10c507a611926c1101d5d14d70be5598ca308e13266"}, +] + +[package.dependencies] +botocore = ">=1.40.50,<1.41.0" +jmespath = ">=0.7.1,<2.0.0" +s3transfer = ">=0.14.0,<0.15.0" + +[package.extras] +crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] + +[[package]] +name = "botocore" +version = "1.40.50" +description = "Low-level, data-driven core of boto 3." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "botocore-1.40.50-py3-none-any.whl", hash = "sha256:53126c153fae0670dc54f03d01c89b1af144acedb1020199b133dedb309e434d"}, + {file = "botocore-1.40.50.tar.gz", hash = "sha256:1d3d5b5759c9cb30202cd5ad231ec8afb1abe5be0c088a1707195c2cbae0e742"}, +] + +[package.dependencies] +jmespath = ">=0.7.1,<2.0.0" +python-dateutil = ">=2.1,<3.0.0" +urllib3 = {version = ">=1.25.4,<2.2.0 || >2.2.0,<3", markers = "python_version >= \"3.10\""} + +[package.extras] +crt = ["awscrt (==0.27.6)"] + +[[package]] +name = "cachetools" +version = "6.2.0" +description = "Extensible memoizing collections and decorators" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "cachetools-6.2.0-py3-none-any.whl", hash = "sha256:1c76a8960c0041fcc21097e357f882197c79da0dbff766e7317890a65d7d8ba6"}, + {file = "cachetools-6.2.0.tar.gz", hash = "sha256:38b328c0889450f05f5e120f56ab68c8abaf424e1275522b138ffc93253f7e32"}, +] + +[[package]] +name = "certifi" +version = "2025.10.5" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "certifi-2025.10.5-py3-none-any.whl", hash = "sha256:0f212c2744a9bb6de0c56639a6f68afe01ecd92d91f14ae897c4fe7bbeeef0de"}, + {file = "certifi-2025.10.5.tar.gz", hash = "sha256:47c09d31ccf2acf0be3f701ea53595ee7e0b8fa08801c6624be771df09ae7b43"}, +] + +[[package]] +name = "cffi" +version = "2.0.0" +description = "Foreign Function Interface for Python calling C code." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44"}, + {file = "cffi-2.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49"}, + {file = "cffi-2.0.0-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c"}, + {file = "cffi-2.0.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb"}, + {file = "cffi-2.0.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0"}, + {file = "cffi-2.0.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4"}, + {file = "cffi-2.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453"}, + {file = "cffi-2.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495"}, + {file = "cffi-2.0.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5"}, + {file = "cffi-2.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb"}, + {file = "cffi-2.0.0-cp310-cp310-win32.whl", hash = "sha256:1f72fb8906754ac8a2cc3f9f5aaa298070652a0ffae577e0ea9bd480dc3c931a"}, + {file = "cffi-2.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:b18a3ed7d5b3bd8d9ef7a8cb226502c6bf8308df1525e1cc676c3680e7176739"}, + {file = "cffi-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe"}, + {file = "cffi-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c"}, + {file = "cffi-2.0.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92"}, + {file = "cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93"}, + {file = "cffi-2.0.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5"}, + {file = "cffi-2.0.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664"}, + {file = "cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26"}, + {file = "cffi-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9"}, + {file = "cffi-2.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414"}, + {file = "cffi-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743"}, + {file = "cffi-2.0.0-cp311-cp311-win32.whl", hash = "sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5"}, + {file = "cffi-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5"}, + {file = "cffi-2.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d"}, + {file = "cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d"}, + {file = "cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c"}, + {file = "cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe"}, + {file = "cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062"}, + {file = "cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e"}, + {file = "cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037"}, + {file = "cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba"}, + {file = "cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94"}, + {file = "cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187"}, + {file = "cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18"}, + {file = "cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5"}, + {file = "cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6"}, + {file = "cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb"}, + {file = "cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca"}, + {file = "cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b"}, + {file = "cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b"}, + {file = "cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2"}, + {file = "cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3"}, + {file = "cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26"}, + {file = "cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c"}, + {file = "cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b"}, + {file = "cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27"}, + {file = "cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75"}, + {file = "cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91"}, + {file = "cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5"}, + {file = "cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13"}, + {file = "cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b"}, + {file = "cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c"}, + {file = "cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef"}, + {file = "cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775"}, + {file = "cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205"}, + {file = "cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1"}, + {file = "cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f"}, + {file = "cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25"}, + {file = "cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad"}, + {file = "cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9"}, + {file = "cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d"}, + {file = "cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c"}, + {file = "cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8"}, + {file = "cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc"}, + {file = "cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592"}, + {file = "cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512"}, + {file = "cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4"}, + {file = "cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e"}, + {file = "cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6"}, + {file = "cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9"}, + {file = "cffi-2.0.0-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:fe562eb1a64e67dd297ccc4f5addea2501664954f2692b69a76449ec7913ecbf"}, + {file = "cffi-2.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:de8dad4425a6ca6e4e5e297b27b5c824ecc7581910bf9aee86cb6835e6812aa7"}, + {file = "cffi-2.0.0-cp39-cp39-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:4647afc2f90d1ddd33441e5b0e85b16b12ddec4fca55f0d9671fef036ecca27c"}, + {file = "cffi-2.0.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3f4d46d8b35698056ec29bca21546e1551a205058ae1a181d871e278b0b28165"}, + {file = "cffi-2.0.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:e6e73b9e02893c764e7e8d5bb5ce277f1a009cd5243f8228f75f842bf937c534"}, + {file = "cffi-2.0.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:cb527a79772e5ef98fb1d700678fe031e353e765d1ca2d409c92263c6d43e09f"}, + {file = "cffi-2.0.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:61d028e90346df14fedc3d1e5441df818d095f3b87d286825dfcbd6459b7ef63"}, + {file = "cffi-2.0.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0f6084a0ea23d05d20c3edcda20c3d006f9b6f3fefeac38f59262e10cef47ee2"}, + {file = "cffi-2.0.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:1cd13c99ce269b3ed80b417dcd591415d3372bcac067009b6e0f59c7d4015e65"}, + {file = "cffi-2.0.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:89472c9762729b5ae1ad974b777416bfda4ac5642423fa93bd57a09204712322"}, + {file = "cffi-2.0.0-cp39-cp39-win32.whl", hash = "sha256:2081580ebb843f759b9f617314a24ed5738c51d2aee65d31e02f6f7a2b97707a"}, + {file = "cffi-2.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:b882b3df248017dba09d6b16defe9b5c407fe32fc7c65a9c69798e6175601be9"}, + {file = "cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529"}, +] + +[package.dependencies] +pycparser = {version = "*", markers = "implementation_name != \"PyPy\""} + +[[package]] +name = "charset-normalizer" +version = "3.4.3" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "charset_normalizer-3.4.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fb7f67a1bfa6e40b438170ebdc8158b78dc465a5a67b6dde178a46987b244a72"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cc9370a2da1ac13f0153780040f465839e6cccb4a1e44810124b4e22483c93fe"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:07a0eae9e2787b586e129fdcbe1af6997f8d0e5abaa0bc98c0e20e124d67e601"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:74d77e25adda8581ffc1c720f1c81ca082921329452eba58b16233ab1842141c"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d0e909868420b7049dafd3a31d45125b31143eec59235311fc4c57ea26a4acd2"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c6f162aabe9a91a309510d74eeb6507fab5fff92337a15acbe77753d88d9dcf0"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:4ca4c094de7771a98d7fbd67d9e5dbf1eb73efa4f744a730437d8a3a5cf994f0"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:02425242e96bcf29a49711b0ca9f37e451da7c70562bc10e8ed992a5a7a25cc0"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:78deba4d8f9590fe4dae384aeff04082510a709957e968753ff3c48399f6f92a"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-win32.whl", hash = "sha256:d79c198e27580c8e958906f803e63cddb77653731be08851c7df0b1a14a8fc0f"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-win_amd64.whl", hash = "sha256:c6e490913a46fa054e03699c70019ab869e990270597018cef1d8562132c2669"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b256ee2e749283ef3ddcff51a675ff43798d92d746d1a6e4631bf8c707d22d0b"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:13faeacfe61784e2559e690fc53fa4c5ae97c6fcedb8eb6fb8d0a15b475d2c64"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:00237675befef519d9af72169d8604a067d92755e84fe76492fef5441db05b91"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:585f3b2a80fbd26b048a0be90c5aae8f06605d3c92615911c3a2b03a8a3b796f"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e78314bdc32fa80696f72fa16dc61168fda4d6a0c014e0380f9d02f0e5d8a07"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:96b2b3d1a83ad55310de8c7b4a2d04d9277d5591f40761274856635acc5fcb30"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:939578d9d8fd4299220161fdd76e86c6a251987476f5243e8864a7844476ba14"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:fd10de089bcdcd1be95a2f73dbe6254798ec1bda9f450d5828c96f93e2536b9c"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1e8ac75d72fa3775e0b7cb7e4629cec13b7514d928d15ef8ea06bca03ef01cae"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-win32.whl", hash = "sha256:6cf8fd4c04756b6b60146d98cd8a77d0cdae0e1ca20329da2ac85eed779b6849"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:31a9a6f775f9bcd865d88ee350f0ffb0e25936a7f930ca98995c05abf1faf21c"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e28e334d3ff134e88989d90ba04b47d84382a828c061d0d1027b1b12a62b39b1"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0cacf8f7297b0c4fcb74227692ca46b4a5852f8f4f24b3c766dd94a1075c4884"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c6fd51128a41297f5409deab284fecbe5305ebd7e5a1f959bee1c054622b7018"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3cfb2aad70f2c6debfbcb717f23b7eb55febc0bb23dcffc0f076009da10c6392"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1606f4a55c0fd363d754049cdf400175ee96c992b1f8018b993941f221221c5f"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:027b776c26d38b7f15b26a5da1044f376455fb3766df8fc38563b4efbc515154"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:42e5088973e56e31e4fa58eb6bd709e42fc03799c11c42929592889a2e54c491"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:cc34f233c9e71701040d772aa7490318673aa7164a0efe3172b2981218c26d93"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:320e8e66157cc4e247d9ddca8e21f427efc7a04bbd0ac8a9faf56583fa543f9f"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-win32.whl", hash = "sha256:fb6fecfd65564f208cbf0fba07f107fb661bcd1a7c389edbced3f7a493f70e37"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:86df271bf921c2ee3818f0522e9a5b8092ca2ad8b065ece5d7d9d0e9f4849bcc"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:14c2a87c65b351109f6abfc424cab3927b3bdece6f706e4d12faaf3d52ee5efe"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:41d1fc408ff5fdfb910200ec0e74abc40387bccb3252f3f27c0676731df2b2c8"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1bb60174149316da1c35fa5233681f7c0f9f514509b8e399ab70fea5f17e45c9"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:30d006f98569de3459c2fc1f2acde170b7b2bd265dc1943e87e1a4efe1b67c31"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:416175faf02e4b0810f1f38bcb54682878a4af94059a1cd63b8747244420801f"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6aab0f181c486f973bc7262a97f5aca3ee7e1437011ef0c2ec04b5a11d16c927"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:fdabf8315679312cfa71302f9bd509ded4f2f263fb5b765cf1433b39106c3cc9"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:bd28b817ea8c70215401f657edef3a8aa83c29d447fb0b622c35403780ba11d5"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:18343b2d246dc6761a249ba1fb13f9ee9a2bcd95decc767319506056ea4ad4dc"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-win32.whl", hash = "sha256:6fb70de56f1859a3f71261cbe41005f56a7842cc348d3aeb26237560bfa5e0ce"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-win_amd64.whl", hash = "sha256:cf1ebb7d78e1ad8ec2a8c4732c7be2e736f6e5123a4146c5b89c9d1f585f8cef"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3cd35b7e8aedeb9e34c41385fda4f73ba609e561faedfae0a9e75e44ac558a15"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b89bc04de1d83006373429975f8ef9e7932534b8cc9ca582e4db7d20d91816db"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2001a39612b241dae17b4687898843f254f8748b796a2e16f1051a17078d991d"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8dcfc373f888e4fb39a7bc57e93e3b845e7f462dacc008d9749568b1c4ece096"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:18b97b8404387b96cdbd30ad660f6407799126d26a39ca65729162fd810a99aa"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ccf600859c183d70eb47e05a44cd80a4ce77394d1ac0f79dbd2dd90a69a3a049"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:53cd68b185d98dde4ad8990e56a58dea83a4162161b1ea9272e5c9182ce415e0"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:30a96e1e1f865f78b030d65241c1ee850cdf422d869e9028e2fc1d5e4db73b92"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d716a916938e03231e86e43782ca7878fb602a125a91e7acb8b5112e2e96ac16"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-win32.whl", hash = "sha256:c6dbd0ccdda3a2ba7c2ecd9d77b37f3b5831687d8dc1b6ca5f56a4880cc7b7ce"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-win_amd64.whl", hash = "sha256:73dc19b562516fc9bcf6e5d6e596df0b4eb98d87e4f79f3ae71840e6ed21361c"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0f2be7e0cf7754b9a30eb01f4295cc3d4358a479843b31f328afd210e2c7598c"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c60e092517a73c632ec38e290eba714e9627abe9d301c8c8a12ec32c314a2a4b"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:252098c8c7a873e17dd696ed98bbe91dbacd571da4b87df3736768efa7a792e4"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3653fad4fe3ed447a596ae8638b437f827234f01a8cd801842e43f3d0a6b281b"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8999f965f922ae054125286faf9f11bc6932184b93011d138925a1773830bbe9"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d95bfb53c211b57198bb91c46dd5a2d8018b3af446583aab40074bf7988401cb"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:5b413b0b1bfd94dbf4023ad6945889f374cd24e3f62de58d6bb102c4d9ae534a"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:b5e3b2d152e74e100a9e9573837aba24aab611d39428ded46f4e4022ea7d1942"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:a2d08ac246bb48479170408d6c19f6385fa743e7157d716e144cad849b2dd94b"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-win32.whl", hash = "sha256:ec557499516fc90fd374bf2e32349a2887a876fbf162c160e3c01b6849eaf557"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:5d8d01eac18c423815ed4f4a2ec3b439d654e55ee4ad610e153cf02faf67ea40"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:70bfc5f2c318afece2f5838ea5e4c3febada0be750fcf4775641052bbba14d05"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:23b6b24d74478dc833444cbd927c338349d6ae852ba53a0d02a2de1fce45b96e"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:34a7f768e3f985abdb42841e20e17b330ad3aaf4bb7e7aeeb73db2e70f077b99"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fb731e5deb0c7ef82d698b0f4c5bb724633ee2a489401594c5c88b02e6cb15f7"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:257f26fed7d7ff59921b78244f3cd93ed2af1800ff048c33f624c87475819dd7"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1ef99f0456d3d46a50945c98de1774da86f8e992ab5c77865ea8b8195341fc19"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:2c322db9c8c89009a990ef07c3bcc9f011a3269bc06782f916cd3d9eed7c9312"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:511729f456829ef86ac41ca78c63a5cb55240ed23b4b737faca0eb1abb1c41bc"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:88ab34806dea0671532d3f82d82b85e8fc23d7b2dd12fa837978dad9bb392a34"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-win32.whl", hash = "sha256:16a8770207946ac75703458e2c743631c79c59c5890c80011d536248f8eaa432"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:d22dbedd33326a4a5190dd4fe9e9e693ef12160c77382d9e87919bce54f3d4ca"}, + {file = "charset_normalizer-3.4.3-py3-none-any.whl", hash = "sha256:ce571ab16d890d23b5c278547ba694193a45011ff86a9162a71307ed9f86759a"}, + {file = "charset_normalizer-3.4.3.tar.gz", hash = "sha256:6fce4b8500244f6fcb71465d4a4930d132ba9ab8e71a7859e6a5d59851068d14"}, +] + +[[package]] +name = "click" +version = "8.3.0" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "click-8.3.0-py3-none-any.whl", hash = "sha256:9b9f285302c6e3064f4330c05f05b81945b2a39544279343e6e7c5f27a9baddc"}, + {file = "click-8.3.0.tar.gz", hash = "sha256:e7b8232224eba16f4ebe410c25ced9f7875cb5f3263ffc93cc3e8da705e229c4"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "clr-loader" +version = "0.2.7.post0" +description = "Generic pure Python loader for .NET runtimes" +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "clr_loader-0.2.7.post0-py3-none-any.whl", hash = "sha256:e0b9fcc107d48347a4311a28ffe3ae78c4968edb216ffb6564cb03f7ace0bb47"}, + {file = "clr_loader-0.2.7.post0.tar.gz", hash = "sha256:b7a8b3f8fbb1bcbbb6382d887e21d1742d4f10b5ea209e4ad95568fe97e1c7c6"}, +] + +[package.dependencies] +cffi = {version = ">=1.17", markers = "python_version >= \"3.8\""} + +[[package]] +name = "cohere" +version = "5.18.0" +description = "" +optional = false +python-versions = "<4.0,>=3.9" +groups = ["main"] +markers = "platform_system != \"Emscripten\"" +files = [ + {file = "cohere-5.18.0-py3-none-any.whl", hash = "sha256:885e7be360206418db39425faa60dbcd7f38e39e7f84b824ee68442e6a436e93"}, + {file = "cohere-5.18.0.tar.gz", hash = "sha256:93a7753458a45cd30c796300182d22bb1889eadc510727e1de3d8342cb2bc0bf"}, +] + +[package.dependencies] +fastavro = ">=1.9.4,<2.0.0" +httpx = ">=0.21.2" +httpx-sse = "0.4.0" +pydantic = ">=1.9.2" +pydantic-core = ">=2.18.2,<3.0.0" +requests = ">=2.0.0,<3.0.0" +tokenizers = ">=0.15,<1" +types-requests = ">=2.0.0,<3.0.0" +typing_extensions = ">=4.0.0" + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["main", "dev"] +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] +markers = {dev = "sys_platform == \"win32\""} + +[[package]] +name = "colorlog" +version = "6.9.0" +description = "Add colours to the output of Python's logging module." +optional = false +python-versions = ">=3.6" +groups = ["main"] +files = [ + {file = "colorlog-6.9.0-py3-none-any.whl", hash = "sha256:5906e71acd67cb07a71e779c47c4bcb45fb8c2993eebe9e5adcd6a6f1b283eff"}, + {file = "colorlog-6.9.0.tar.gz", hash = "sha256:bfba54a1b93b94f54e1f4fe48395725a3d92fd2a4af702f6bd70946bdc0c6ac2"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} + +[package.extras] +development = ["black", "flake8", "mypy", "pytest", "types-colorama"] + +[[package]] +name = "common" +version = "0.1.0" +description = "" +optional = false +python-versions = ">=3.12,<4.0" +groups = ["main"] +files = [] +develop = true + +[package.dependencies] +dapr = "1.16.0" +fastapi = "^0.115.6" +minio = "^7.2.14" +pydantic = "^2.10.5" +structlog = "^25.1.0" + +[package.source] +type = "directory" +url = "../../libs/common" + +[[package]] +name = "dapr" +version = "1.16.0" +description = "The official release of Dapr Python SDK." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "dapr-1.16.0-py3-none-any.whl", hash = "sha256:076dd559a0b450eae24b1c2ae779c9299ed3e06a05c1f72719a6613af8d19ced"}, + {file = "dapr-1.16.0.tar.gz", hash = "sha256:c7e3d005552a598d07608d0d502b2bc432e86678f94b2beccc13a096b9198684"}, +] + +[package.dependencies] +aiohttp = ">=3.9.0b0" +grpcio = ">=1.37.0" +grpcio-status = ">=1.37.0" +protobuf = ">=4.22" +python-dateutil = ">=2.8.1" +typing-extensions = ">=4.4.0" + +[[package]] +name = "dapr-ext-fastapi" +version = "1.16.0" +description = "The official release of Dapr FastAPI extension." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "dapr-ext-fastapi-1.16.0.tar.gz", hash = "sha256:10108c3831ae2164c1589c86e6b86fe8ee146650514961841d9ab5eb783f4a76"}, + {file = "dapr_ext_fastapi-1.16.0-py3-none-any.whl", hash = "sha256:9dcc0aaceb361c5132295450a71d4f9ddec09ab5848dbfe2a8b0cf9050fd903e"}, +] + +[package.dependencies] +dapr = ">=1.16.0" +fastapi = ">=0.60.1" +uvicorn = ">=0.11.6" + +[[package]] +name = "dapr-ext-workflow" +version = "1.16.0" +description = "The official release of Dapr Python SDK Workflow Authoring Extension." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "dapr-ext-workflow-1.16.0.tar.gz", hash = "sha256:7487d174394d305e668784f4bac2dcecc757a1e0a8ddf6e5e1cb32c0a887be78"}, + {file = "dapr_ext_workflow-1.16.0-py3-none-any.whl", hash = "sha256:028f6b3a340a5a8f0b061eacdef60de1ce52de2340f9636f517f799f73437ee8"}, +] + +[package.dependencies] +dapr = ">=1.16.0" +durabletask-dapr = ">=0.2.0a8" + +[[package]] +name = "distro" +version = "1.9.0" +description = "Distro - an OS platform information API" +optional = false +python-versions = ">=3.6" +groups = ["main"] +files = [ + {file = "distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2"}, + {file = "distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed"}, +] + +[[package]] +name = "docstring-parser" +version = "0.17.0" +description = "Parse Python docstrings in reST, Google and Numpydoc format" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "docstring_parser-0.17.0-py3-none-any.whl", hash = "sha256:cf2569abd23dce8099b300f9b4fa8191e9582dda731fd533daf54c4551658708"}, + {file = "docstring_parser-0.17.0.tar.gz", hash = "sha256:583de4a309722b3315439bb31d64ba3eebada841f2e2cee23b99df001434c912"}, +] + +[package.extras] +dev = ["pre-commit (>=2.16.0)", "pydoctor (>=25.4.0)", "pytest"] +docs = ["pydoctor (>=25.4.0)"] +test = ["pytest"] + +[[package]] +name = "durabletask-dapr" +version = "0.2.0a9" +description = "A Durable Task Client SDK for Python" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "durabletask_dapr-0.2.0a9-py3-none-any.whl", hash = "sha256:48c401c30c05a6122bdd1ee245e9b65c56dabb2dde3dfe6fe1d4ee47085e4a2e"}, + {file = "durabletask_dapr-0.2.0a9.tar.gz", hash = "sha256:ec481840a043a9d15f67628386b0694e60a04de8015f8f56883f55e490ebbb56"}, +] + +[package.dependencies] +asyncio = "*" +grpcio = "*" +protobuf = "*" + +[[package]] +name = "eval-type-backport" +version = "0.2.2" +description = "Like `typing._eval_type`, but lets older Python versions use newer typing features." +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "eval_type_backport-0.2.2-py3-none-any.whl", hash = "sha256:cb6ad7c393517f476f96d456d0412ea80f0a8cf96f6892834cd9340149111b0a"}, + {file = "eval_type_backport-0.2.2.tar.gz", hash = "sha256:f0576b4cf01ebb5bd358d02314d31846af5e07678387486e2c798af0e7d849c1"}, +] + +[package.extras] +tests = ["pytest"] + +[[package]] +name = "executing" +version = "2.2.1" +description = "Get the currently executing AST node of a frame, and other information" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "executing-2.2.1-py2.py3-none-any.whl", hash = "sha256:760643d3452b4d777d295bb167ccc74c64a81df23fb5e08eff250c425a4b2017"}, + {file = "executing-2.2.1.tar.gz", hash = "sha256:3632cc370565f6648cc328b32435bd120a1e4ebb20c77e3fdde9a13cd1e533c4"}, +] + +[package.extras] +tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipython", "littleutils", "pytest", "rich"] + +[[package]] +name = "fastapi" +version = "0.115.14" +description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "fastapi-0.115.14-py3-none-any.whl", hash = "sha256:6c0c8bf9420bd58f565e585036d971872472b4f7d3f6c73b698e10cffdefb3ca"}, + {file = "fastapi-0.115.14.tar.gz", hash = "sha256:b1de15cdc1c499a4da47914db35d0e4ef8f1ce62b624e94e0e5824421df99739"}, +] + +[package.dependencies] +pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0" +starlette = ">=0.40.0,<0.47.0" +typing-extensions = ">=4.8.0" + +[package.extras] +all = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=3.1.5)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.18)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"] +standard = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "jinja2 (>=3.1.5)", "python-multipart (>=0.0.18)", "uvicorn[standard] (>=0.12.0)"] + +[[package]] +name = "fastavro" +version = "1.12.1" +description = "Fast read/write of AVRO files" +optional = false +python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system != \"Emscripten\"" +files = [ + {file = "fastavro-1.12.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:00650ca533907361edda22e6ffe8cf87ab2091c5d8aee5c8000b0f2dcdda7ed3"}, + {file = "fastavro-1.12.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ac76d6d95f909c72ee70d314b460b7e711d928845771531d823eb96a10952d26"}, + {file = "fastavro-1.12.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1f55eef18c41d4476bd32a82ed5dd86aabc3f614e1b66bdb09ffa291612e1670"}, + {file = "fastavro-1.12.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:81563e1f93570e6565487cdb01ba241a36a00e58cff9c5a0614af819d1155d8f"}, + {file = "fastavro-1.12.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:bec207360f76f0b3de540758a297193c5390e8e081c43c3317f610b1414d8c8f"}, + {file = "fastavro-1.12.1-cp310-cp310-win_amd64.whl", hash = "sha256:c0390bfe4a9f8056a75ac6785fbbff8f5e317f5356481d2e29ec980877d2314b"}, + {file = "fastavro-1.12.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6b632b713bc5d03928a87d811fa4a11d5f25cd43e79c161e291c7d3f7aa740fd"}, + {file = "fastavro-1.12.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eaa7ab3769beadcebb60f0539054c7755f63bd9cf7666e2c15e615ab605f89a8"}, + {file = "fastavro-1.12.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:123fb221df3164abd93f2d042c82f538a1d5a43ce41375f12c91ce1355a9141e"}, + {file = "fastavro-1.12.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:632a4e3ff223f834ddb746baae0cc7cee1068eb12c32e4d982c2fee8a5b483d0"}, + {file = "fastavro-1.12.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:83e6caf4e7a8717d932a3b1ff31595ad169289bbe1128a216be070d3a8391671"}, + {file = "fastavro-1.12.1-cp311-cp311-win_amd64.whl", hash = "sha256:b91a0fe5a173679a6c02d53ca22dcaad0a2c726b74507e0c1c2e71a7c3f79ef9"}, + {file = "fastavro-1.12.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:509818cb24b98a804fc80be9c5fed90f660310ae3d59382fc811bfa187122167"}, + {file = "fastavro-1.12.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:089e155c0c76e0d418d7e79144ce000524dd345eab3bc1e9c5ae69d500f71b14"}, + {file = "fastavro-1.12.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:44cbff7518901c91a82aab476fcab13d102e4999499df219d481b9e15f61af34"}, + {file = "fastavro-1.12.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a275e48df0b1701bb764b18a8a21900b24cf882263cb03d35ecdba636bbc830b"}, + {file = "fastavro-1.12.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2de72d786eb38be6b16d556b27232b1bf1b2797ea09599507938cdb7a9fe3e7c"}, + {file = "fastavro-1.12.1-cp312-cp312-win_amd64.whl", hash = "sha256:9090f0dee63fe022ee9cc5147483366cc4171c821644c22da020d6b48f576b4f"}, + {file = "fastavro-1.12.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:78df838351e4dff9edd10a1c41d1324131ffecbadefb9c297d612ef5363c049a"}, + {file = "fastavro-1.12.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:780476c23175d2ae457c52f45b9ffa9d504593499a36cd3c1929662bf5b7b14b"}, + {file = "fastavro-1.12.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0714b285160fcd515eb0455540f40dd6dac93bdeacdb03f24e8eac3d8aa51f8d"}, + {file = "fastavro-1.12.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a8bc2dcec5843d499f2489bfe0747999108f78c5b29295d877379f1972a3d41a"}, + {file = "fastavro-1.12.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:3b1921ac35f3d89090a5816b626cf46e67dbecf3f054131f84d56b4e70496f45"}, + {file = "fastavro-1.12.1-cp313-cp313-win_amd64.whl", hash = "sha256:5aa777b8ee595b50aa084104cd70670bf25a7bbb9fd8bb5d07524b0785ee1699"}, + {file = "fastavro-1.12.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:c3d67c47f177e486640404a56f2f50b165fe892cc343ac3a34673b80cc7f1dd6"}, + {file = "fastavro-1.12.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5217f773492bac43dae15ff2931432bce2d7a80be7039685a78d3fab7df910bd"}, + {file = "fastavro-1.12.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:469fecb25cba07f2e1bfa4c8d008477cd6b5b34a59d48715e1b1a73f6160097d"}, + {file = "fastavro-1.12.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:d71c8aa841ef65cfab709a22bb887955f42934bced3ddb571e98fdbdade4c609"}, + {file = "fastavro-1.12.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:b81fc04e85dfccf7c028e0580c606e33aa8472370b767ef058aae2c674a90746"}, + {file = "fastavro-1.12.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:9445da127751ba65975d8e4bdabf36bfcfdad70fc35b2d988e3950cce0ec0e7c"}, + {file = "fastavro-1.12.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ed924233272719b5d5a6a0b4d80ef3345fc7e84fc7a382b6232192a9112d38a6"}, + {file = "fastavro-1.12.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3616e2f0e1c9265e92954fa099db79c6e7817356d3ff34f4bcc92699ae99697c"}, + {file = "fastavro-1.12.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:cb0337b42fd3c047fcf0e9b7597bd6ad25868de719f29da81eabb6343f08d399"}, + {file = "fastavro-1.12.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:64961ab15b74b7c168717bbece5660e0f3d457837c3cc9d9145181d011199fa7"}, + {file = "fastavro-1.12.1-cp314-cp314-win_amd64.whl", hash = "sha256:792356d320f6e757e89f7ac9c22f481e546c886454a6709247f43c0dd7058004"}, + {file = "fastavro-1.12.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:120aaf82ac19d60a1016afe410935fe94728752d9c2d684e267e5b7f0e70f6d9"}, + {file = "fastavro-1.12.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b6a3462934b20a74f9ece1daa49c2e4e749bd9a35fa2657b53bf62898fba80f5"}, + {file = "fastavro-1.12.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:1f81011d54dd47b12437b51dd93a70a9aa17b61307abf26542fc3c13efbc6c51"}, + {file = "fastavro-1.12.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:43ded16b3f4a9f1a42f5970c2aa618acb23ea59c4fcaa06680bdf470b255e5a8"}, + {file = "fastavro-1.12.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:02281432dcb11c78b3280da996eff61ee0eff39c5de06c6e0fbf19275093e6d4"}, + {file = "fastavro-1.12.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4128978b930aaf930332db4b3acc290783183f3be06a241ae4a482f3ed8ce892"}, + {file = "fastavro-1.12.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:546ffffda6610fca672f0ed41149808e106d8272bb246aa7539fa8bb6f117f17"}, + {file = "fastavro-1.12.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:a7d840ccd9aacada3ddc80fbcc4ea079b658107fe62e9d289a0de9d54e95d366"}, + {file = "fastavro-1.12.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:3100ad643e7fa658469a2a2db229981c1a000ff16b8037c0b58ce3ec4d2107e8"}, + {file = "fastavro-1.12.1-cp39-cp39-win_amd64.whl", hash = "sha256:a38607444281619eda3a9c1be9f5397634012d1b237142eee1540e810b30ac8b"}, + {file = "fastavro-1.12.1.tar.gz", hash = "sha256:2f285be49e45bc047ab2f6bed040bb349da85db3f3c87880e4b92595ea093b2b"}, +] + +[package.extras] +codecs = ["cramjam", "lz4", "zstandard"] +lz4 = ["lz4"] +snappy = ["cramjam"] +zstandard = ["zstandard"] + +[[package]] +name = "filelock" +version = "3.20.0" +description = "A platform independent file lock." +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "filelock-3.20.0-py3-none-any.whl", hash = "sha256:339b4732ffda5cd79b13f4e2711a31b0365ce445d95d243bb996273d072546a2"}, + {file = "filelock-3.20.0.tar.gz", hash = "sha256:711e943b4ec6be42e1d4e6690b48dc175c822967466bb31c0c293f34334c13f4"}, +] + +[[package]] +name = "frozenlist" +version = "1.8.0" +description = "A list-like structure which implements collections.abc.MutableSequence" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "frozenlist-1.8.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b37f6d31b3dcea7deb5e9696e529a6aa4a898adc33db82da12e4c60a7c4d2011"}, + {file = "frozenlist-1.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ef2b7b394f208233e471abc541cc6991f907ffd47dc72584acee3147899d6565"}, + {file = "frozenlist-1.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a88f062f072d1589b7b46e951698950e7da00442fc1cacbe17e19e025dc327ad"}, + {file = "frozenlist-1.8.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f57fb59d9f385710aa7060e89410aeb5058b99e62f4d16b08b91986b9a2140c2"}, + {file = "frozenlist-1.8.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:799345ab092bee59f01a915620b5d014698547afd011e691a208637312db9186"}, + {file = "frozenlist-1.8.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c23c3ff005322a6e16f71bf8692fcf4d5a304aaafe1e262c98c6d4adc7be863e"}, + {file = "frozenlist-1.8.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8a76ea0f0b9dfa06f254ee06053d93a600865b3274358ca48a352ce4f0798450"}, + {file = "frozenlist-1.8.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c7366fe1418a6133d5aa824ee53d406550110984de7637d65a178010f759c6ef"}, + {file = "frozenlist-1.8.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:13d23a45c4cebade99340c4165bd90eeb4a56c6d8a9d8aa49568cac19a6d0dc4"}, + {file = "frozenlist-1.8.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:e4a3408834f65da56c83528fb52ce7911484f0d1eaf7b761fc66001db1646eff"}, + {file = "frozenlist-1.8.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:42145cd2748ca39f32801dad54aeea10039da6f86e303659db90db1c4b614c8c"}, + {file = "frozenlist-1.8.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:e2de870d16a7a53901e41b64ffdf26f2fbb8917b3e6ebf398098d72c5b20bd7f"}, + {file = "frozenlist-1.8.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:20e63c9493d33ee48536600d1a5c95eefc870cd71e7ab037763d1fbb89cc51e7"}, + {file = "frozenlist-1.8.0-cp310-cp310-win32.whl", hash = "sha256:adbeebaebae3526afc3c96fad434367cafbfd1b25d72369a9e5858453b1bb71a"}, + {file = "frozenlist-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:667c3777ca571e5dbeb76f331562ff98b957431df140b54c85fd4d52eea8d8f6"}, + {file = "frozenlist-1.8.0-cp310-cp310-win_arm64.whl", hash = "sha256:80f85f0a7cc86e7a54c46d99c9e1318ff01f4687c172ede30fd52d19d1da1c8e"}, + {file = "frozenlist-1.8.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:09474e9831bc2b2199fad6da3c14c7b0fbdd377cce9d3d77131be28906cb7d84"}, + {file = "frozenlist-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:17c883ab0ab67200b5f964d2b9ed6b00971917d5d8a92df149dc2c9779208ee9"}, + {file = "frozenlist-1.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fa47e444b8ba08fffd1c18e8cdb9a75db1b6a27f17507522834ad13ed5922b93"}, + {file = "frozenlist-1.8.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2552f44204b744fba866e573be4c1f9048d6a324dfe14475103fd51613eb1d1f"}, + {file = "frozenlist-1.8.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:957e7c38f250991e48a9a73e6423db1bb9dd14e722a10f6b8bb8e16a0f55f695"}, + {file = "frozenlist-1.8.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8585e3bb2cdea02fc88ffa245069c36555557ad3609e83be0ec71f54fd4abb52"}, + {file = "frozenlist-1.8.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:edee74874ce20a373d62dc28b0b18b93f645633c2943fd90ee9d898550770581"}, + {file = "frozenlist-1.8.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c9a63152fe95756b85f31186bddf42e4c02c6321207fd6601a1c89ebac4fe567"}, + {file = "frozenlist-1.8.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b6db2185db9be0a04fecf2f241c70b63b1a242e2805be291855078f2b404dd6b"}, + {file = "frozenlist-1.8.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:f4be2e3d8bc8aabd566f8d5b8ba7ecc09249d74ba3c9ed52e54dc23a293f0b92"}, + {file = "frozenlist-1.8.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:c8d1634419f39ea6f5c427ea2f90ca85126b54b50837f31497f3bf38266e853d"}, + {file = "frozenlist-1.8.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:1a7fa382a4a223773ed64242dbe1c9c326ec09457e6b8428efb4118c685c3dfd"}, + {file = "frozenlist-1.8.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:11847b53d722050808926e785df837353bd4d75f1d494377e59b23594d834967"}, + {file = "frozenlist-1.8.0-cp311-cp311-win32.whl", hash = "sha256:27c6e8077956cf73eadd514be8fb04d77fc946a7fe9f7fe167648b0b9085cc25"}, + {file = "frozenlist-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:ac913f8403b36a2c8610bbfd25b8013488533e71e62b4b4adce9c86c8cea905b"}, + {file = "frozenlist-1.8.0-cp311-cp311-win_arm64.whl", hash = "sha256:d4d3214a0f8394edfa3e303136d0575eece0745ff2b47bd2cb2e66dd92d4351a"}, + {file = "frozenlist-1.8.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:78f7b9e5d6f2fdb88cdde9440dc147259b62b9d3b019924def9f6478be254ac1"}, + {file = "frozenlist-1.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:229bf37d2e4acdaf808fd3f06e854a4a7a3661e871b10dc1f8f1896a3b05f18b"}, + {file = "frozenlist-1.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f833670942247a14eafbb675458b4e61c82e002a148f49e68257b79296e865c4"}, + {file = "frozenlist-1.8.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:494a5952b1c597ba44e0e78113a7266e656b9794eec897b19ead706bd7074383"}, + {file = "frozenlist-1.8.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96f423a119f4777a4a056b66ce11527366a8bb92f54e541ade21f2374433f6d4"}, + {file = "frozenlist-1.8.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3462dd9475af2025c31cc61be6652dfa25cbfb56cbbf52f4ccfe029f38decaf8"}, + {file = "frozenlist-1.8.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4c800524c9cd9bac5166cd6f55285957fcfc907db323e193f2afcd4d9abd69b"}, + {file = "frozenlist-1.8.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d6a5df73acd3399d893dafc71663ad22534b5aa4f94e8a2fabfe856c3c1b6a52"}, + {file = "frozenlist-1.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:405e8fe955c2280ce66428b3ca55e12b3c4e9c336fb2103a4937e891c69a4a29"}, + {file = "frozenlist-1.8.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:908bd3f6439f2fef9e85031b59fd4f1297af54415fb60e4254a95f75b3cab3f3"}, + {file = "frozenlist-1.8.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:294e487f9ec720bd8ffcebc99d575f7eff3568a08a253d1ee1a0378754b74143"}, + {file = "frozenlist-1.8.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:74c51543498289c0c43656701be6b077f4b265868fa7f8a8859c197006efb608"}, + {file = "frozenlist-1.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:776f352e8329135506a1d6bf16ac3f87bc25b28e765949282dcc627af36123aa"}, + {file = "frozenlist-1.8.0-cp312-cp312-win32.whl", hash = "sha256:433403ae80709741ce34038da08511d4a77062aa924baf411ef73d1146e74faf"}, + {file = "frozenlist-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:34187385b08f866104f0c0617404c8eb08165ab1272e884abc89c112e9c00746"}, + {file = "frozenlist-1.8.0-cp312-cp312-win_arm64.whl", hash = "sha256:fe3c58d2f5db5fbd18c2987cba06d51b0529f52bc3a6cdc33d3f4eab725104bd"}, + {file = "frozenlist-1.8.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8d92f1a84bb12d9e56f818b3a746f3efba93c1b63c8387a73dde655e1e42282a"}, + {file = "frozenlist-1.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:96153e77a591c8adc2ee805756c61f59fef4cf4073a9275ee86fe8cba41241f7"}, + {file = "frozenlist-1.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f21f00a91358803399890ab167098c131ec2ddd5f8f5fd5fe9c9f2c6fcd91e40"}, + {file = "frozenlist-1.8.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fb30f9626572a76dfe4293c7194a09fb1fe93ba94c7d4f720dfae3b646b45027"}, + {file = "frozenlist-1.8.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eaa352d7047a31d87dafcacbabe89df0aa506abb5b1b85a2fb91bc3faa02d822"}, + {file = "frozenlist-1.8.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:03ae967b4e297f58f8c774c7eabcce57fe3c2434817d4385c50661845a058121"}, + {file = "frozenlist-1.8.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f6292f1de555ffcc675941d65fffffb0a5bcd992905015f85d0592201793e0e5"}, + {file = "frozenlist-1.8.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29548f9b5b5e3460ce7378144c3010363d8035cea44bc0bf02d57f5a685e084e"}, + {file = "frozenlist-1.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ec3cc8c5d4084591b4237c0a272cc4f50a5b03396a47d9caaf76f5d7b38a4f11"}, + {file = "frozenlist-1.8.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:517279f58009d0b1f2e7c1b130b377a349405da3f7621ed6bfae50b10adf20c1"}, + {file = "frozenlist-1.8.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:db1e72ede2d0d7ccb213f218df6a078a9c09a7de257c2fe8fcef16d5925230b1"}, + {file = "frozenlist-1.8.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b4dec9482a65c54a5044486847b8a66bf10c9cb4926d42927ec4e8fd5db7fed8"}, + {file = "frozenlist-1.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:21900c48ae04d13d416f0e1e0c4d81f7931f73a9dfa0b7a8746fb2fe7dd970ed"}, + {file = "frozenlist-1.8.0-cp313-cp313-win32.whl", hash = "sha256:8b7b94a067d1c504ee0b16def57ad5738701e4ba10cec90529f13fa03c833496"}, + {file = "frozenlist-1.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:878be833caa6a3821caf85eb39c5ba92d28e85df26d57afb06b35b2efd937231"}, + {file = "frozenlist-1.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:44389d135b3ff43ba8cc89ff7f51f5a0bb6b63d829c8300f79a2fe4fe61bcc62"}, + {file = "frozenlist-1.8.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:e25ac20a2ef37e91c1b39938b591457666a0fa835c7783c3a8f33ea42870db94"}, + {file = "frozenlist-1.8.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:07cdca25a91a4386d2e76ad992916a85038a9b97561bf7a3fd12d5d9ce31870c"}, + {file = "frozenlist-1.8.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4e0c11f2cc6717e0a741f84a527c52616140741cd812a50422f83dc31749fb52"}, + {file = "frozenlist-1.8.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b3210649ee28062ea6099cfda39e147fa1bc039583c8ee4481cb7811e2448c51"}, + {file = "frozenlist-1.8.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:581ef5194c48035a7de2aefc72ac6539823bb71508189e5de01d60c9dcd5fa65"}, + {file = "frozenlist-1.8.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3ef2d026f16a2b1866e1d86fc4e1291e1ed8a387b2c333809419a2f8b3a77b82"}, + {file = "frozenlist-1.8.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5500ef82073f599ac84d888e3a8c1f77ac831183244bfd7f11eaa0289fb30714"}, + {file = "frozenlist-1.8.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:50066c3997d0091c411a66e710f4e11752251e6d2d73d70d8d5d4c76442a199d"}, + {file = "frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:5c1c8e78426e59b3f8005e9b19f6ff46e5845895adbde20ece9218319eca6506"}, + {file = "frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:eefdba20de0d938cec6a89bd4d70f346a03108a19b9df4248d3cf0d88f1b0f51"}, + {file = "frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:cf253e0e1c3ceb4aaff6df637ce033ff6535fb8c70a764a8f46aafd3d6ab798e"}, + {file = "frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:032efa2674356903cd0261c4317a561a6850f3ac864a63fc1583147fb05a79b0"}, + {file = "frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6da155091429aeba16851ecb10a9104a108bcd32f6c1642867eadaee401c1c41"}, + {file = "frozenlist-1.8.0-cp313-cp313t-win32.whl", hash = "sha256:0f96534f8bfebc1a394209427d0f8a63d343c9779cda6fc25e8e121b5fd8555b"}, + {file = "frozenlist-1.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5d63a068f978fc69421fb0e6eb91a9603187527c86b7cd3f534a5b77a592b888"}, + {file = "frozenlist-1.8.0-cp313-cp313t-win_arm64.whl", hash = "sha256:bf0a7e10b077bf5fb9380ad3ae8ce20ef919a6ad93b4552896419ac7e1d8e042"}, + {file = "frozenlist-1.8.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cee686f1f4cadeb2136007ddedd0aaf928ab95216e7691c63e50a8ec066336d0"}, + {file = "frozenlist-1.8.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:119fb2a1bd47307e899c2fac7f28e85b9a543864df47aa7ec9d3c1b4545f096f"}, + {file = "frozenlist-1.8.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4970ece02dbc8c3a92fcc5228e36a3e933a01a999f7094ff7c23fbd2beeaa67c"}, + {file = "frozenlist-1.8.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:cba69cb73723c3f329622e34bdbf5ce1f80c21c290ff04256cff1cd3c2036ed2"}, + {file = "frozenlist-1.8.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:778a11b15673f6f1df23d9586f83c4846c471a8af693a22e066508b77d201ec8"}, + {file = "frozenlist-1.8.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0325024fe97f94c41c08872db482cf8ac4800d80e79222c6b0b7b162d5b13686"}, + {file = "frozenlist-1.8.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:97260ff46b207a82a7567b581ab4190bd4dfa09f4db8a8b49d1a958f6aa4940e"}, + {file = "frozenlist-1.8.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:54b2077180eb7f83dd52c40b2750d0a9f175e06a42e3213ce047219de902717a"}, + {file = "frozenlist-1.8.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2f05983daecab868a31e1da44462873306d3cbfd76d1f0b5b69c473d21dbb128"}, + {file = "frozenlist-1.8.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:33f48f51a446114bc5d251fb2954ab0164d5be02ad3382abcbfe07e2531d650f"}, + {file = "frozenlist-1.8.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:154e55ec0655291b5dd1b8731c637ecdb50975a2ae70c606d100750a540082f7"}, + {file = "frozenlist-1.8.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:4314debad13beb564b708b4a496020e5306c7333fa9a3ab90374169a20ffab30"}, + {file = "frozenlist-1.8.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:073f8bf8becba60aa931eb3bc420b217bb7d5b8f4750e6f8b3be7f3da85d38b7"}, + {file = "frozenlist-1.8.0-cp314-cp314-win32.whl", hash = "sha256:bac9c42ba2ac65ddc115d930c78d24ab8d4f465fd3fc473cdedfccadb9429806"}, + {file = "frozenlist-1.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:3e0761f4d1a44f1d1a47996511752cf3dcec5bbdd9cc2b4fe595caf97754b7a0"}, + {file = "frozenlist-1.8.0-cp314-cp314-win_arm64.whl", hash = "sha256:d1eaff1d00c7751b7c6662e9c5ba6eb2c17a2306ba5e2a37f24ddf3cc953402b"}, + {file = "frozenlist-1.8.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:d3bb933317c52d7ea5004a1c442eef86f426886fba134ef8cf4226ea6ee1821d"}, + {file = "frozenlist-1.8.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:8009897cdef112072f93a0efdce29cd819e717fd2f649ee3016efd3cd885a7ed"}, + {file = "frozenlist-1.8.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2c5dcbbc55383e5883246d11fd179782a9d07a986c40f49abe89ddf865913930"}, + {file = "frozenlist-1.8.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:39ecbc32f1390387d2aa4f5a995e465e9e2f79ba3adcac92d68e3e0afae6657c"}, + {file = "frozenlist-1.8.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92db2bf818d5cc8d9c1f1fc56b897662e24ea5adb36ad1f1d82875bd64e03c24"}, + {file = "frozenlist-1.8.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2dc43a022e555de94c3b68a4ef0b11c4f747d12c024a520c7101709a2144fb37"}, + {file = "frozenlist-1.8.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cb89a7f2de3602cfed448095bab3f178399646ab7c61454315089787df07733a"}, + {file = "frozenlist-1.8.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:33139dc858c580ea50e7e60a1b0ea003efa1fd42e6ec7fdbad78fff65fad2fd2"}, + {file = "frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:168c0969a329b416119507ba30b9ea13688fafffac1b7822802537569a1cb0ef"}, + {file = "frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:28bd570e8e189d7f7b001966435f9dac6718324b5be2990ac496cf1ea9ddb7fe"}, + {file = "frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b2a095d45c5d46e5e79ba1e5b9cb787f541a8dee0433836cea4b96a2c439dcd8"}, + {file = "frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:eab8145831a0d56ec9c4139b6c3e594c7a83c2c8be25d5bcf2d86136a532287a"}, + {file = "frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:974b28cf63cc99dfb2188d8d222bc6843656188164848c4f679e63dae4b0708e"}, + {file = "frozenlist-1.8.0-cp314-cp314t-win32.whl", hash = "sha256:342c97bf697ac5480c0a7ec73cd700ecfa5a8a40ac923bd035484616efecc2df"}, + {file = "frozenlist-1.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:06be8f67f39c8b1dc671f5d83aaefd3358ae5cdcf8314552c57e7ed3e6475bdd"}, + {file = "frozenlist-1.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:102e6314ca4da683dca92e3b1355490fed5f313b768500084fbe6371fddfdb79"}, + {file = "frozenlist-1.8.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:d8b7138e5cd0647e4523d6685b0eac5d4be9a184ae9634492f25c6eb38c12a47"}, + {file = "frozenlist-1.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a6483e309ca809f1efd154b4d37dc6d9f61037d6c6a81c2dc7a15cb22c8c5dca"}, + {file = "frozenlist-1.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1b9290cf81e95e93fdf90548ce9d3c1211cf574b8e3f4b3b7cb0537cf2227068"}, + {file = "frozenlist-1.8.0-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:59a6a5876ca59d1b63af8cd5e7ffffb024c3dc1e9cf9301b21a2e76286505c95"}, + {file = "frozenlist-1.8.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6dc4126390929823e2d2d9dc79ab4046ed74680360fc5f38b585c12c66cdf459"}, + {file = "frozenlist-1.8.0-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:332db6b2563333c5671fecacd085141b5800cb866be16d5e3eb15a2086476675"}, + {file = "frozenlist-1.8.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9ff15928d62a0b80bb875655c39bf517938c7d589554cbd2669be42d97c2cb61"}, + {file = "frozenlist-1.8.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7bf6cdf8e07c8151fba6fe85735441240ec7f619f935a5205953d58009aef8c6"}, + {file = "frozenlist-1.8.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:48e6d3f4ec5c7273dfe83ff27c91083c6c9065af655dc2684d2c200c94308bb5"}, + {file = "frozenlist-1.8.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:1a7607e17ad33361677adcd1443edf6f5da0ce5e5377b798fba20fae194825f3"}, + {file = "frozenlist-1.8.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:5a3a935c3a4e89c733303a2d5a7c257ea44af3a56c8202df486b7f5de40f37e1"}, + {file = "frozenlist-1.8.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:940d4a017dbfed9daf46a3b086e1d2167e7012ee297fef9e1c545c4d022f5178"}, + {file = "frozenlist-1.8.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:b9be22a69a014bc47e78072d0ecae716f5eb56c15238acca0f43d6eb8e4a5bda"}, + {file = "frozenlist-1.8.0-cp39-cp39-win32.whl", hash = "sha256:1aa77cb5697069af47472e39612976ed05343ff2e84a3dcf15437b232cbfd087"}, + {file = "frozenlist-1.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:7398c222d1d405e796970320036b1b563892b65809d9e5261487bb2c7f7b5c6a"}, + {file = "frozenlist-1.8.0-cp39-cp39-win_arm64.whl", hash = "sha256:b4f3b365f31c6cd4af24545ca0a244a53688cad8834e32f56831c4923b50a103"}, + {file = "frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d"}, + {file = "frozenlist-1.8.0.tar.gz", hash = "sha256:3ede829ed8d842f6cd48fc7081d7a41001a56f1f38603f9d49bf3020d59a31ad"}, +] + +[[package]] +name = "fsspec" +version = "2025.9.0" +description = "File-system specification" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "fsspec-2025.9.0-py3-none-any.whl", hash = "sha256:530dc2a2af60a414a832059574df4a6e10cce927f6f4a78209390fe38955cfb7"}, + {file = "fsspec-2025.9.0.tar.gz", hash = "sha256:19fd429483d25d28b65ec68f9f4adc16c17ea2c7c7bf54ec61360d478fb19c19"}, +] + +[package.extras] +abfs = ["adlfs"] +adl = ["adlfs"] +arrow = ["pyarrow (>=1)"] +dask = ["dask", "distributed"] +dev = ["pre-commit", "ruff (>=0.5)"] +doc = ["numpydoc", "sphinx", "sphinx-design", "sphinx-rtd-theme", "yarl"] +dropbox = ["dropbox", "dropboxdrivefs", "requests"] +full = ["adlfs", "aiohttp (!=4.0.0a0,!=4.0.0a1)", "dask", "distributed", "dropbox", "dropboxdrivefs", "fusepy", "gcsfs", "libarchive-c", "ocifs", "panel", "paramiko", "pyarrow (>=1)", "pygit2", "requests", "s3fs", "smbprotocol", "tqdm"] +fuse = ["fusepy"] +gcs = ["gcsfs"] +git = ["pygit2"] +github = ["requests"] +gs = ["gcsfs"] +gui = ["panel"] +hdfs = ["pyarrow (>=1)"] +http = ["aiohttp (!=4.0.0a0,!=4.0.0a1)"] +libarchive = ["libarchive-c"] +oci = ["ocifs"] +s3 = ["s3fs"] +sftp = ["paramiko"] +smb = ["smbprotocol"] +ssh = ["paramiko"] +test = ["aiohttp (!=4.0.0a0,!=4.0.0a1)", "numpy", "pytest", "pytest-asyncio (!=0.22.0)", "pytest-benchmark", "pytest-cov", "pytest-mock", "pytest-recording", "pytest-rerunfailures", "requests"] +test-downstream = ["aiobotocore (>=2.5.4,<3.0.0)", "dask[dataframe,test]", "moto[server] (>4,<5)", "pytest-timeout", "xarray"] +test-full = ["adlfs", "aiohttp (!=4.0.0a0,!=4.0.0a1)", "cloudpickle", "dask", "distributed", "dropbox", "dropboxdrivefs", "fastparquet", "fusepy", "gcsfs", "jinja2", "kerchunk", "libarchive-c", "lz4", "notebook", "numpy", "ocifs", "pandas", "panel", "paramiko", "pyarrow", "pyarrow (>=1)", "pyftpdlib", "pygit2", "pytest", "pytest-asyncio (!=0.22.0)", "pytest-benchmark", "pytest-cov", "pytest-mock", "pytest-recording", "pytest-rerunfailures", "python-snappy", "requests", "smbprotocol", "tqdm", "urllib3", "zarr", "zstandard"] +tqdm = ["tqdm"] + +[[package]] +name = "google-auth" +version = "2.41.1" +description = "Google Authentication Library" +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "google_auth-2.41.1-py2.py3-none-any.whl", hash = "sha256:754843be95575b9a19c604a848a41be03f7f2afd8c019f716dc1f51ee41c639d"}, + {file = "google_auth-2.41.1.tar.gz", hash = "sha256:b76b7b1f9e61f0cb7e88870d14f6a94aeef248959ef6992670efee37709cbfd2"}, +] + +[package.dependencies] +cachetools = ">=2.0.0,<7.0" +pyasn1-modules = ">=0.2.1" +rsa = ">=3.1.4,<5" + +[package.extras] +aiohttp = ["aiohttp (>=3.6.2,<4.0.0)", "requests (>=2.20.0,<3.0.0)"] +enterprise-cert = ["cryptography", "pyopenssl"] +pyjwt = ["cryptography (<39.0.0)", "cryptography (>=38.0.3)", "pyjwt (>=2.0)"] +pyopenssl = ["cryptography (<39.0.0)", "cryptography (>=38.0.3)", "pyopenssl (>=20.0.0)"] +reauth = ["pyu2f (>=0.1.5)"] +requests = ["requests (>=2.20.0,<3.0.0)"] +testing = ["aiohttp (<3.10.0)", "aiohttp (>=3.6.2,<4.0.0)", "aioresponses", "cryptography (<39.0.0)", "cryptography (<39.0.0)", "cryptography (>=38.0.3)", "cryptography (>=38.0.3)", "flask", "freezegun", "grpcio", "mock", "oauth2client", "packaging", "pyjwt (>=2.0)", "pyopenssl (<24.3.0)", "pyopenssl (>=20.0.0)", "pytest", "pytest-asyncio", "pytest-cov", "pytest-localserver", "pyu2f (>=0.1.5)", "requests (>=2.20.0,<3.0.0)", "responses", "urllib3"] +urllib3 = ["packaging", "urllib3"] + +[[package]] +name = "google-genai" +version = "1.43.0" +description = "GenAI Python SDK" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "google_genai-1.43.0-py3-none-any.whl", hash = "sha256:be1d4b1acab268125d536fd81b73c38694a70cb08266759089154718924434fd"}, + {file = "google_genai-1.43.0.tar.gz", hash = "sha256:84eb219d320759c5882bc2cdb4e2ac84544d00f5d12c7892c79fb03d71bfc9a4"}, +] + +[package.dependencies] +anyio = ">=4.8.0,<5.0.0" +google-auth = ">=2.14.1,<3.0.0" +httpx = ">=0.28.1,<1.0.0" +pydantic = ">=2.0.0,<3.0.0" +requests = ">=2.28.1,<3.0.0" +tenacity = ">=8.2.3,<9.2.0" +typing-extensions = ">=4.11.0,<5.0.0" +websockets = ">=13.0.0,<15.1.0" + +[package.extras] +aiohttp = ["aiohttp (<4.0.0)"] +local-tokenizer = ["protobuf", "sentencepiece (>=0.2.0)"] + +[[package]] +name = "googleapis-common-protos" +version = "1.70.0" +description = "Common protobufs used in Google APIs" +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "googleapis_common_protos-1.70.0-py3-none-any.whl", hash = "sha256:b8bfcca8c25a2bb253e0e0b0adaf8c00773e5e6af6fd92397576680b807e0fd8"}, + {file = "googleapis_common_protos-1.70.0.tar.gz", hash = "sha256:0e1b44e0ea153e6594f9f394fef15193a68aaaea2d843f83e2742717ca753257"}, +] + +[package.dependencies] +protobuf = ">=3.20.2,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<7.0.0" + +[package.extras] +grpc = ["grpcio (>=1.44.0,<2.0.0)"] + +[[package]] +name = "gql" +version = "3.5.3" +description = "GraphQL client for Python" +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "gql-3.5.3-py2.py3-none-any.whl", hash = "sha256:e1fcbde2893fcafdd28114ece87ff47f1cc339a31db271fc4e1d528f5a1d4fbc"}, + {file = "gql-3.5.3.tar.gz", hash = "sha256:393b8c049d58e0d2f5461b9d738a2b5f904186a40395500b4a84dd092d56e42b"}, +] + +[package.dependencies] +anyio = ">=3.0,<5" +backoff = ">=1.11.1,<3.0" +graphql-core = ">=3.2,<3.2.7" +requests = {version = ">=2.26,<3", optional = true, markers = "extra == \"requests\""} +requests-toolbelt = {version = ">=1.0.0,<2", optional = true, markers = "extra == \"requests\""} +yarl = ">=1.6,<2.0" + +[package.extras] +aiohttp = ["aiohttp (>=3.8.0,<4)", "aiohttp (>=3.9.0b0,<4)"] +all = ["aiohttp (>=3.8.0,<4)", "aiohttp (>=3.9.0b0,<4)", "botocore (>=1.21,<2)", "httpx (>=0.23.1,<1)", "requests (>=2.26,<3)", "requests-toolbelt (>=1.0.0,<2)", "websockets (>=10,<12)"] +botocore = ["botocore (>=1.21,<2)"] +dev = ["aiofiles", "aiohttp (>=3.8.0,<4)", "aiohttp (>=3.9.0b0,<4)", "black (==22.3.0)", "botocore (>=1.21,<2)", "check-manifest (>=0.42,<1)", "flake8 (==3.8.1)", "httpx (>=0.23.1,<1)", "isort (==4.3.21)", "mock (==4.0.2)", "mypy (==0.910)", "parse (==1.15.0)", "pytest (==7.4.2)", "pytest-asyncio (==0.21.1)", "pytest-console-scripts (==1.3.1)", "pytest-cov (==3.0.0)", "requests (>=2.26,<3)", "requests-toolbelt (>=1.0.0,<2)", "sphinx (>=5.3.0,<6)", "sphinx-argparse (==0.2.5)", "sphinx-rtd-theme (>=0.4,<1)", "types-aiofiles", "types-mock", "types-requests", "vcrpy (==4.4.0)", "vcrpy (==7.0.0)", "websockets (>=10,<12)"] +httpx = ["httpx (>=0.23.1,<1)"] +requests = ["requests (>=2.26,<3)", "requests-toolbelt (>=1.0.0,<2)"] +test = ["aiofiles", "aiohttp (>=3.8.0,<4)", "aiohttp (>=3.9.0b0,<4)", "botocore (>=1.21,<2)", "httpx (>=0.23.1,<1)", "mock (==4.0.2)", "parse (==1.15.0)", "pytest (==7.4.2)", "pytest-asyncio (==0.21.1)", "pytest-console-scripts (==1.3.1)", "pytest-cov (==3.0.0)", "requests (>=2.26,<3)", "requests-toolbelt (>=1.0.0,<2)", "vcrpy (==4.4.0)", "vcrpy (==7.0.0)", "websockets (>=10,<12)"] +test-no-transport = ["aiofiles", "mock (==4.0.2)", "parse (==1.15.0)", "pytest (==7.4.2)", "pytest-asyncio (==0.21.1)", "pytest-console-scripts (==1.3.1)", "pytest-cov (==3.0.0)", "vcrpy (==4.4.0)", "vcrpy (==7.0.0)"] +websockets = ["websockets (>=10,<12)"] + +[[package]] +name = "graphql-core" +version = "3.2.6" +description = "GraphQL implementation for Python, a port of GraphQL.js, the JavaScript reference implementation for GraphQL." +optional = false +python-versions = "<4,>=3.6" +groups = ["main"] +files = [ + {file = "graphql_core-3.2.6-py3-none-any.whl", hash = "sha256:78b016718c161a6fb20a7d97bbf107f331cd1afe53e45566c59f776ed7f0b45f"}, + {file = "graphql_core-3.2.6.tar.gz", hash = "sha256:c08eec22f9e40f0bd61d805907e3b3b1b9a320bc606e23dc145eebca07c8fbab"}, +] + +[[package]] +name = "griffe" +version = "1.14.0" +description = "Signatures for entire Python programs. Extract the structure, the frame, the skeleton of your project, to generate API documentation or find breaking changes in your API." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "griffe-1.14.0-py3-none-any.whl", hash = "sha256:0e9d52832cccf0f7188cfe585ba962d2674b241c01916d780925df34873bceb0"}, + {file = "griffe-1.14.0.tar.gz", hash = "sha256:9d2a15c1eca966d68e00517de5d69dd1bc5c9f2335ef6c1775362ba5b8651a13"}, +] + +[package.dependencies] +colorama = ">=0.4" + +[[package]] +name = "groq" +version = "0.32.0" +description = "The official Python library for the groq API" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "groq-0.32.0-py3-none-any.whl", hash = "sha256:0ed0be290042f8826f851f3a1defaac4f979dcfce86ec4a0681a23af00ec800b"}, + {file = "groq-0.32.0.tar.gz", hash = "sha256:fb1ade61f47a06d1a1c1dc0fab690d269b799ebd57ad1dd867efaeaa7adeb2af"}, +] + +[package.dependencies] +anyio = ">=3.5.0,<5" +distro = ">=1.7.0,<2" +httpx = ">=0.23.0,<1" +pydantic = ">=1.9.0,<3" +sniffio = "*" +typing-extensions = ">=4.10,<5" + +[package.extras] +aiohttp = ["aiohttp", "httpx-aiohttp (>=0.1.8)"] + +[[package]] +name = "grpcio" +version = "1.75.1" +description = "HTTP/2-based RPC framework" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "grpcio-1.75.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:1712b5890b22547dd29f3215c5788d8fc759ce6dd0b85a6ba6e2731f2d04c088"}, + {file = "grpcio-1.75.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:8d04e101bba4b55cea9954e4aa71c24153ba6182481b487ff376da28d4ba46cf"}, + {file = "grpcio-1.75.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:683cfc70be0c1383449097cba637317e4737a357cfc185d887fd984206380403"}, + {file = "grpcio-1.75.1-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:491444c081a54dcd5e6ada57314321ae526377f498d4aa09d975c3241c5b9e1c"}, + {file = "grpcio-1.75.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ce08d4e112d0d38487c2b631ec8723deac9bc404e9c7b1011426af50a79999e4"}, + {file = "grpcio-1.75.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5a2acda37fc926ccc4547977ac3e56b1df48fe200de968e8c8421f6e3093df6c"}, + {file = "grpcio-1.75.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:745c5fe6bf05df6a04bf2d11552c7d867a2690759e7ab6b05c318a772739bd75"}, + {file = "grpcio-1.75.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:259526a7159d39e2db40d566fe3e8f8e034d0fb2db5bf9c00e09aace655a4c2b"}, + {file = "grpcio-1.75.1-cp310-cp310-win32.whl", hash = "sha256:f4b29b9aabe33fed5df0a85e5f13b09ff25e2c05bd5946d25270a8bd5682dac9"}, + {file = "grpcio-1.75.1-cp310-cp310-win_amd64.whl", hash = "sha256:cf2e760978dcce7ff7d465cbc7e276c3157eedc4c27aa6de7b594c7a295d3d61"}, + {file = "grpcio-1.75.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:573855ca2e58e35032aff30bfbd1ee103fbcf4472e4b28d4010757700918e326"}, + {file = "grpcio-1.75.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:6a4996a2c8accc37976dc142d5991adf60733e223e5c9a2219e157dc6a8fd3a2"}, + {file = "grpcio-1.75.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b1ea1bbe77ecbc1be00af2769f4ae4a88ce93be57a4f3eebd91087898ed749f9"}, + {file = "grpcio-1.75.1-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:e5b425aee54cc5e3e3c58f00731e8a33f5567965d478d516d35ef99fd648ab68"}, + {file = "grpcio-1.75.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0049a7bf547dafaeeb1db17079ce79596c298bfe308fc084d023c8907a845b9a"}, + {file = "grpcio-1.75.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5b8ea230c7f77c0a1a3208a04a1eda164633fb0767b4cefd65a01079b65e5b1f"}, + {file = "grpcio-1.75.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:36990d629c3c9fb41e546414e5af52d0a7af37ce7113d9682c46d7e2919e4cca"}, + {file = "grpcio-1.75.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b10ad908118d38c2453ade7ff790e5bce36580c3742919007a2a78e3a1e521ca"}, + {file = "grpcio-1.75.1-cp311-cp311-win32.whl", hash = "sha256:d6be2b5ee7bea656c954dcf6aa8093c6f0e6a3ef9945c99d99fcbfc88c5c0bfe"}, + {file = "grpcio-1.75.1-cp311-cp311-win_amd64.whl", hash = "sha256:61c692fb05956b17dd6d1ab480f7f10ad0536dba3bc8fd4e3c7263dc244ed772"}, + {file = "grpcio-1.75.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:7b888b33cd14085d86176b1628ad2fcbff94cfbbe7809465097aa0132e58b018"}, + {file = "grpcio-1.75.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:8775036efe4ad2085975531d221535329f5dac99b6c2a854a995456098f99546"}, + {file = "grpcio-1.75.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:bb658f703468d7fbb5dcc4037c65391b7dc34f808ac46ed9136c24fc5eeb041d"}, + {file = "grpcio-1.75.1-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:4b7177a1cdb3c51b02b0c0a256b0a72fdab719600a693e0e9037949efffb200b"}, + {file = "grpcio-1.75.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7d4fa6ccc3ec2e68a04f7b883d354d7fea22a34c44ce535a2f0c0049cf626ddf"}, + {file = "grpcio-1.75.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3d86880ecaeb5b2f0a8afa63824de93adb8ebe4e49d0e51442532f4e08add7d6"}, + {file = "grpcio-1.75.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a8041d2f9e8a742aeae96f4b047ee44e73619f4f9d24565e84d5446c623673b6"}, + {file = "grpcio-1.75.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3652516048bf4c314ce12be37423c79829f46efffb390ad64149a10c6071e8de"}, + {file = "grpcio-1.75.1-cp312-cp312-win32.whl", hash = "sha256:44b62345d8403975513af88da2f3d5cc76f73ca538ba46596f92a127c2aea945"}, + {file = "grpcio-1.75.1-cp312-cp312-win_amd64.whl", hash = "sha256:b1e191c5c465fa777d4cafbaacf0c01e0d5278022082c0abbd2ee1d6454ed94d"}, + {file = "grpcio-1.75.1-cp313-cp313-linux_armv7l.whl", hash = "sha256:3bed22e750d91d53d9e31e0af35a7b0b51367e974e14a4ff229db5b207647884"}, + {file = "grpcio-1.75.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:5b8f381eadcd6ecaa143a21e9e80a26424c76a0a9b3d546febe6648f3a36a5ac"}, + {file = "grpcio-1.75.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5bf4001d3293e3414d0cf99ff9b1139106e57c3a66dfff0c5f60b2a6286ec133"}, + {file = "grpcio-1.75.1-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:9f82ff474103e26351dacfe8d50214e7c9322960d8d07ba7fa1d05ff981c8b2d"}, + {file = "grpcio-1.75.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0ee119f4f88d9f75414217823d21d75bfe0e6ed40135b0cbbfc6376bc9f7757d"}, + {file = "grpcio-1.75.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:664eecc3abe6d916fa6cf8dd6b778e62fb264a70f3430a3180995bf2da935446"}, + {file = "grpcio-1.75.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:c32193fa08b2fbebf08fe08e84f8a0aad32d87c3ad42999c65e9449871b1c66e"}, + {file = "grpcio-1.75.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5cebe13088b9254f6e615bcf1da9131d46cfa4e88039454aca9cb65f639bd3bc"}, + {file = "grpcio-1.75.1-cp313-cp313-win32.whl", hash = "sha256:4b4c678e7ed50f8ae8b8dbad15a865ee73ce12668b6aaf411bf3258b5bc3f970"}, + {file = "grpcio-1.75.1-cp313-cp313-win_amd64.whl", hash = "sha256:5573f51e3f296a1bcf71e7a690c092845fb223072120f4bdb7a5b48e111def66"}, + {file = "grpcio-1.75.1-cp314-cp314-linux_armv7l.whl", hash = "sha256:c05da79068dd96723793bffc8d0e64c45f316248417515f28d22204d9dae51c7"}, + {file = "grpcio-1.75.1-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:06373a94fd16ec287116a825161dca179a0402d0c60674ceeec8c9fba344fe66"}, + {file = "grpcio-1.75.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4484f4b7287bdaa7a5b3980f3c7224c3c622669405d20f69549f5fb956ad0421"}, + {file = "grpcio-1.75.1-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:2720c239c1180eee69f7883c1d4c83fc1a495a2535b5fa322887c70bf02b16e8"}, + {file = "grpcio-1.75.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:07a554fa31c668cf0e7a188678ceeca3cb8fead29bbe455352e712ec33ca701c"}, + {file = "grpcio-1.75.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:3e71a2105210366bfc398eef7f57a664df99194f3520edb88b9c3a7e46ee0d64"}, + {file = "grpcio-1.75.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:8679aa8a5b67976776d3c6b0521e99d1c34db8a312a12bcfd78a7085cb9b604e"}, + {file = "grpcio-1.75.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:aad1c774f4ebf0696a7f148a56d39a3432550612597331792528895258966dc0"}, + {file = "grpcio-1.75.1-cp314-cp314-win32.whl", hash = "sha256:62ce42d9994446b307649cb2a23335fa8e927f7ab2cbf5fcb844d6acb4d85f9c"}, + {file = "grpcio-1.75.1-cp314-cp314-win_amd64.whl", hash = "sha256:f86e92275710bea3000cb79feca1762dc0ad3b27830dd1a74e82ab321d4ee464"}, + {file = "grpcio-1.75.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:c09fba33327c3ac11b5c33dbdd8218eef8990d78f83b1656d628831812a8c0fb"}, + {file = "grpcio-1.75.1-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:7e21400b037be29545704889e72e586c238e346dcb2d08d8a7288d16c883a9ec"}, + {file = "grpcio-1.75.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c12121e509b9f8b0914d10054d24120237d19e870b1cd82acbb8a9b9ddd198a3"}, + {file = "grpcio-1.75.1-cp39-cp39-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:73577a93e692b3474b1bfe84285d098de36705dbd838bb4d6a056d326e4dc880"}, + {file = "grpcio-1.75.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e19e7dfa0d7ca7dea22be464339e18ac608fd75d88c56770c646cdabe54bc724"}, + {file = "grpcio-1.75.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:4e1c28f51c1cf67eccdfc1065e8e866c9ed622f09773ca60947089c117f848a1"}, + {file = "grpcio-1.75.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:030a6164bc2ca726052778c0cf8e3249617a34e368354f9e6107c27ad4af8c28"}, + {file = "grpcio-1.75.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:67697efef5a98d46d5db7b1720fa4043536f8b8e5072a5d61cfca762f287e939"}, + {file = "grpcio-1.75.1-cp39-cp39-win32.whl", hash = "sha256:52015cf73eb5d76f6404e0ce0505a69b51fd1f35810b3a01233b34b10baafb41"}, + {file = "grpcio-1.75.1-cp39-cp39-win_amd64.whl", hash = "sha256:9fe51e4a1f896ea84ac750900eae34d9e9b896b5b1e4a30b02dc31ad29f36383"}, + {file = "grpcio-1.75.1.tar.gz", hash = "sha256:3e81d89ece99b9ace23a6916880baca613c03a799925afb2857887efa8b1b3d2"}, +] + +[package.dependencies] +typing-extensions = ">=4.12,<5.0" + +[package.extras] +protobuf = ["grpcio-tools (>=1.75.1)"] + +[[package]] +name = "grpcio-status" +version = "1.75.1" +description = "Status proto mapping for gRPC" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "grpcio_status-1.75.1-py3-none-any.whl", hash = "sha256:f681b301be26dcf7abf5c765d4a22e4098765e1a65cbdfa3efca384edf8e4e3c"}, + {file = "grpcio_status-1.75.1.tar.gz", hash = "sha256:8162afa21833a2085c91089cc395ad880fac1378a1d60233d976649ed724cbf8"}, +] + +[package.dependencies] +googleapis-common-protos = ">=1.5.5" +grpcio = ">=1.75.1" +protobuf = ">=6.31.1,<7.0.0" + +[[package]] +name = "h11" +version = "0.16.0" +description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86"}, + {file = "h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1"}, +] + +[[package]] +name = "hf-xet" +version = "1.1.10" +description = "Fast transfer of large files with the Hugging Face Hub." +optional = false +python-versions = ">=3.8" +groups = ["main"] +markers = "platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"arm64\" or platform_machine == \"aarch64\"" +files = [ + {file = "hf_xet-1.1.10-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:686083aca1a6669bc85c21c0563551cbcdaa5cf7876a91f3d074a030b577231d"}, + {file = "hf_xet-1.1.10-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:71081925383b66b24eedff3013f8e6bbd41215c3338be4b94ba75fd75b21513b"}, + {file = "hf_xet-1.1.10-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b6bceb6361c80c1cc42b5a7b4e3efd90e64630bcf11224dcac50ef30a47e435"}, + {file = "hf_xet-1.1.10-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:eae7c1fc8a664e54753ffc235e11427ca61f4b0477d757cc4eb9ae374b69f09c"}, + {file = "hf_xet-1.1.10-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:0a0005fd08f002180f7a12d4e13b22be277725bc23ed0529f8add5c7a6309c06"}, + {file = "hf_xet-1.1.10-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:f900481cf6e362a6c549c61ff77468bd59d6dd082f3170a36acfef2eb6a6793f"}, + {file = "hf_xet-1.1.10-cp37-abi3-win_amd64.whl", hash = "sha256:5f54b19cc347c13235ae7ee98b330c26dd65ef1df47e5316ffb1e87713ca7045"}, + {file = "hf_xet-1.1.10.tar.gz", hash = "sha256:408aef343800a2102374a883f283ff29068055c111f003ff840733d3b715bb97"}, +] + +[package.extras] +tests = ["pytest"] + +[[package]] +name = "httpcore" +version = "1.0.9" +description = "A minimal low-level HTTP client." +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55"}, + {file = "httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8"}, +] + +[package.dependencies] +certifi = "*" +h11 = ">=0.16" + +[package.extras] +asyncio = ["anyio (>=4.0,<5.0)"] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (==1.*)"] +trio = ["trio (>=0.22.0,<1.0)"] + +[[package]] +name = "httpx" +version = "0.28.1" +description = "The next generation HTTP client." +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad"}, + {file = "httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc"}, +] + +[package.dependencies] +anyio = "*" +certifi = "*" +httpcore = "==1.*" +idna = "*" + +[package.extras] +brotli = ["brotli", "brotlicffi"] +cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (==1.*)"] +zstd = ["zstandard (>=0.18.0)"] + +[[package]] +name = "httpx-sse" +version = "0.4.0" +description = "Consume Server-Sent Event (SSE) messages with HTTPX." +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "httpx-sse-0.4.0.tar.gz", hash = "sha256:1e81a3a3070ce322add1d3529ed42eb5f70817f45ed6ec915ab753f961139721"}, + {file = "httpx_sse-0.4.0-py3-none-any.whl", hash = "sha256:f329af6eae57eaa2bdfd962b42524764af68075ea87370a2de920af5341e318f"}, +] + +[[package]] +name = "huggingface-hub" +version = "0.35.3" +description = "Client library to download and publish models, datasets and other repos on the huggingface.co hub" +optional = false +python-versions = ">=3.8.0" +groups = ["main"] +files = [ + {file = "huggingface_hub-0.35.3-py3-none-any.whl", hash = "sha256:0e3a01829c19d86d03793e4577816fe3bdfc1602ac62c7fb220d593d351224ba"}, + {file = "huggingface_hub-0.35.3.tar.gz", hash = "sha256:350932eaa5cc6a4747efae85126ee220e4ef1b54e29d31c3b45c5612ddf0b32a"}, +] + +[package.dependencies] +aiohttp = {version = "*", optional = true, markers = "extra == \"inference\""} +filelock = "*" +fsspec = ">=2023.5.0" +hf-xet = {version = ">=1.1.3,<2.0.0", markers = "platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"arm64\" or platform_machine == \"aarch64\""} +packaging = ">=20.9" +pyyaml = ">=5.1" +requests = "*" +tqdm = ">=4.42.1" +typing-extensions = ">=3.7.4.3" + +[package.extras] +all = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "authlib (>=1.3.2)", "fastapi", "gradio (>=4.0.0)", "httpx", "itsdangerous", "jedi", "libcst (>=1.4.0)", "mypy (==1.15.0)", "mypy (>=1.14.1,<1.15.0)", "numpy", "pytest (>=8.1.1,<8.2.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures (<16.0)", "pytest-vcr", "pytest-xdist", "ruff (>=0.9.0)", "soundfile", "ty", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)", "urllib3 (<2.0)"] +cli = ["InquirerPy (==0.3.4)"] +dev = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "authlib (>=1.3.2)", "fastapi", "gradio (>=4.0.0)", "httpx", "itsdangerous", "jedi", "libcst (>=1.4.0)", "mypy (==1.15.0)", "mypy (>=1.14.1,<1.15.0)", "numpy", "pytest (>=8.1.1,<8.2.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures (<16.0)", "pytest-vcr", "pytest-xdist", "ruff (>=0.9.0)", "soundfile", "ty", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)", "urllib3 (<2.0)"] +fastai = ["fastai (>=2.4)", "fastcore (>=1.3.27)", "toml"] +hf-transfer = ["hf-transfer (>=0.1.4)"] +hf-xet = ["hf-xet (>=1.1.2,<2.0.0)"] +inference = ["aiohttp"] +mcp = ["aiohttp", "mcp (>=1.8.0)", "typer"] +oauth = ["authlib (>=1.3.2)", "fastapi", "httpx", "itsdangerous"] +quality = ["libcst (>=1.4.0)", "mypy (==1.15.0)", "mypy (>=1.14.1,<1.15.0)", "ruff (>=0.9.0)", "ty"] +tensorflow = ["graphviz", "pydot", "tensorflow"] +tensorflow-testing = ["keras (<3.0)", "tensorflow"] +testing = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "authlib (>=1.3.2)", "fastapi", "gradio (>=4.0.0)", "httpx", "itsdangerous", "jedi", "numpy", "pytest (>=8.1.1,<8.2.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures (<16.0)", "pytest-vcr", "pytest-xdist", "soundfile", "urllib3 (<2.0)"] +torch = ["safetensors[torch]", "torch"] +typing = ["types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)"] + +[[package]] +name = "idna" +version = "3.10" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.6" +groups = ["main"] +files = [ + {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, + {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, +] + +[package.extras] +all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] + +[[package]] +name = "importlib-metadata" +version = "8.7.0" +description = "Read metadata from Python packages" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "importlib_metadata-8.7.0-py3-none-any.whl", hash = "sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd"}, + {file = "importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000"}, +] + +[package.dependencies] +zipp = ">=3.20" + +[package.extras] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] +cover = ["pytest-cov"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +enabler = ["pytest-enabler (>=2.2)"] +perf = ["ipython"] +test = ["flufl.flake8", "importlib_resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] +type = ["pytest-mypy"] + +[[package]] +name = "iniconfig" +version = "2.1.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760"}, + {file = "iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"}, +] + +[[package]] +name = "invoke" +version = "2.2.1" +description = "Pythonic task execution" +optional = false +python-versions = ">=3.6" +groups = ["main"] +files = [ + {file = "invoke-2.2.1-py3-none-any.whl", hash = "sha256:2413bc441b376e5cd3f55bb5d364f973ad8bdd7bf87e53c79de3c11bf3feecc8"}, + {file = "invoke-2.2.1.tar.gz", hash = "sha256:515bf49b4a48932b79b024590348da22f39c4942dff991ad1fb8b8baea1be707"}, +] + +[[package]] +name = "jiter" +version = "0.11.0" +description = "Fast iterable JSON parser." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "jiter-0.11.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3893ce831e1c0094a83eeaf56c635a167d6fa8cc14393cc14298fd6fdc2a2449"}, + {file = "jiter-0.11.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:25c625b9b61b5a8725267fdf867ef2e51b429687f6a4eef211f4612e95607179"}, + {file = "jiter-0.11.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd4ca85fb6a62cf72e1c7f5e34ddef1b660ce4ed0886ec94a1ef9777d35eaa1f"}, + {file = "jiter-0.11.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:572208127034725e79c28437b82414028c3562335f2b4f451d98136d0fc5f9cd"}, + {file = "jiter-0.11.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:494ba627c7f550ad3dabb21862864b8f2216098dc18ff62f37b37796f2f7c325"}, + {file = "jiter-0.11.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b8da18a99f58bca3ecc2d2bba99cac000a924e115b6c4f0a2b98f752b6fbf39a"}, + {file = "jiter-0.11.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4ffd3b0fff3fabbb02cc09910c08144db6bb5697a98d227a074401e01ee63dd"}, + {file = "jiter-0.11.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8fe6530aa738a4f7d4e4702aa8f9581425d04036a5f9e25af65ebe1f708f23be"}, + {file = "jiter-0.11.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e35d66681c133a03d7e974e7eedae89720fe8ca3bd09f01a4909b86a8adf31f5"}, + {file = "jiter-0.11.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c59459beca2fbc9718b6f1acb7bfb59ebc3eb4294fa4d40e9cb679dafdcc6c60"}, + {file = "jiter-0.11.0-cp310-cp310-win32.whl", hash = "sha256:b7b0178417b0dcfc5f259edbc6db2b1f5896093ed9035ee7bab0f2be8854726d"}, + {file = "jiter-0.11.0-cp310-cp310-win_amd64.whl", hash = "sha256:11df2bf99fb4754abddd7f5d940a48e51f9d11624d6313ca4314145fcad347f0"}, + {file = "jiter-0.11.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:cb5d9db02979c3f49071fce51a48f4b4e4cf574175fb2b11c7a535fa4867b222"}, + {file = "jiter-0.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1dc6a123f3471c4730db7ca8ba75f1bb3dcb6faeb8d46dd781083e7dee88b32d"}, + {file = "jiter-0.11.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09858f8d230f031c7b8e557429102bf050eea29c77ad9c34c8fe253c5329acb7"}, + {file = "jiter-0.11.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:dbe2196c4a0ce760925a74ab4456bf644748ab0979762139626ad138f6dac72d"}, + {file = "jiter-0.11.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5beb56d22b63647bafd0b74979216fdee80c580c0c63410be8c11053860ffd09"}, + {file = "jiter-0.11.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:97025d09ef549795d8dc720a824312cee3253c890ac73c621721ddfc75066789"}, + {file = "jiter-0.11.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d50880a6da65d8c23a2cf53c412847d9757e74cc9a3b95c5704a1d1a24667347"}, + {file = "jiter-0.11.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:452d80a1c86c095a242007bd9fc5d21b8a8442307193378f891cb8727e469648"}, + {file = "jiter-0.11.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e84e58198d4894668eec2da660ffff60e0f3e60afa790ecc50cb12b0e02ca1d4"}, + {file = "jiter-0.11.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:df64edcfc5dd5279a791eea52aa113d432c933119a025b0b5739f90d2e4e75f1"}, + {file = "jiter-0.11.0-cp311-cp311-win32.whl", hash = "sha256:144fc21337d21b1d048f7f44bf70881e1586401d405ed3a98c95a114a9994982"}, + {file = "jiter-0.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:b0f32e644d241293b892b1a6dd8f0b9cc029bfd94c97376b2681c36548aabab7"}, + {file = "jiter-0.11.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:2fb7b377688cc3850bbe5c192a6bd493562a0bc50cbc8b047316428fbae00ada"}, + {file = "jiter-0.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a1b7cbe3f25bd0d8abb468ba4302a5d45617ee61b2a7a638f63fee1dc086be99"}, + {file = "jiter-0.11.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c0a7f0ec81d5b7588c5cade1eb1925b91436ae6726dc2df2348524aeabad5de6"}, + {file = "jiter-0.11.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:07630bb46ea2a6b9c6ed986c6e17e35b26148cce2c535454b26ee3f0e8dcaba1"}, + {file = "jiter-0.11.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7764f27d28cd4a9cbc61704dfcd80c903ce3aad106a37902d3270cd6673d17f4"}, + {file = "jiter-0.11.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1d4a6c4a737d486f77f842aeb22807edecb4a9417e6700c7b981e16d34ba7c72"}, + {file = "jiter-0.11.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf408d2a0abd919b60de8c2e7bc5eeab72d4dafd18784152acc7c9adc3291591"}, + {file = "jiter-0.11.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cdef53eda7d18e799625023e1e250dbc18fbc275153039b873ec74d7e8883e09"}, + {file = "jiter-0.11.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:53933a38ef7b551dd9c7f1064f9d7bb235bb3168d0fa5f14f0798d1b7ea0d9c5"}, + {file = "jiter-0.11.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:11840d2324c9ab5162fc1abba23bc922124fedcff0d7b7f85fffa291e2f69206"}, + {file = "jiter-0.11.0-cp312-cp312-win32.whl", hash = "sha256:4f01a744d24a5f2bb4a11657a1b27b61dc038ae2e674621a74020406e08f749b"}, + {file = "jiter-0.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:29fff31190ab3a26de026da2f187814f4b9c6695361e20a9ac2123e4d4378a4c"}, + {file = "jiter-0.11.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:4441a91b80a80249f9a6452c14b2c24708f139f64de959943dfeaa6cb915e8eb"}, + {file = "jiter-0.11.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ff85fc6d2a431251ad82dbd1ea953affb5a60376b62e7d6809c5cd058bb39471"}, + {file = "jiter-0.11.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5e86126d64706fd28dfc46f910d496923c6f95b395138c02d0e252947f452bd"}, + {file = "jiter-0.11.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4ad8bd82165961867a10f52010590ce0b7a8c53da5ddd8bbb62fef68c181b921"}, + {file = "jiter-0.11.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b42c2cd74273455ce439fd9528db0c6e84b5623cb74572305bdd9f2f2961d3df"}, + {file = "jiter-0.11.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f0062dab98172dd0599fcdbf90214d0dcde070b1ff38a00cc1b90e111f071982"}, + {file = "jiter-0.11.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb948402821bc76d1f6ef0f9e19b816f9b09f8577844ba7140f0b6afe994bc64"}, + {file = "jiter-0.11.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:25a5b1110cca7329fd0daf5060faa1234be5c11e988948e4f1a1923b6a457fe1"}, + {file = "jiter-0.11.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:bf11807e802a214daf6c485037778843fadd3e2ec29377ae17e0706ec1a25758"}, + {file = "jiter-0.11.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:dbb57da40631c267861dd0090461222060960012d70fd6e4c799b0f62d0ba166"}, + {file = "jiter-0.11.0-cp313-cp313-win32.whl", hash = "sha256:8e36924dad32c48d3c5e188d169e71dc6e84d6cb8dedefea089de5739d1d2f80"}, + {file = "jiter-0.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:452d13e4fd59698408087235259cebe67d9d49173b4dacb3e8d35ce4acf385d6"}, + {file = "jiter-0.11.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:089f9df9f69532d1339e83142438668f52c97cd22ee2d1195551c2b1a9e6cf33"}, + {file = "jiter-0.11.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:29ed1fe69a8c69bf0f2a962d8d706c7b89b50f1332cd6b9fbda014f60bd03a03"}, + {file = "jiter-0.11.0-cp313-cp313t-win_amd64.whl", hash = "sha256:a4d71d7ea6ea8786291423fe209acf6f8d398a0759d03e7f24094acb8ab686ba"}, + {file = "jiter-0.11.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:9a6dff27eca70930bdbe4cbb7c1a4ba8526e13b63dc808c0670083d2d51a4a72"}, + {file = "jiter-0.11.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:b1ae2a7593a62132c7d4c2abbee80bbbb94fdc6d157e2c6cc966250c564ef774"}, + {file = "jiter-0.11.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b13a431dba4b059e9e43019d3022346d009baf5066c24dcdea321a303cde9f0"}, + {file = "jiter-0.11.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:af62e84ca3889604ebb645df3b0a3f3bcf6b92babbff642bd214616f57abb93a"}, + {file = "jiter-0.11.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c6f3b32bb723246e6b351aecace52aba78adb8eeb4b2391630322dc30ff6c773"}, + {file = "jiter-0.11.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:adcab442f4a099a358a7f562eaa54ed6456fb866e922c6545a717be51dbed7d7"}, + {file = "jiter-0.11.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9967c2ab338ee2b2c0102fd379ec2693c496abf71ffd47e4d791d1f593b68e2"}, + {file = "jiter-0.11.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e7d0bed3b187af8b47a981d9742ddfc1d9b252a7235471ad6078e7e4e5fe75c2"}, + {file = "jiter-0.11.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:f6fe0283e903ebc55f1a6cc569b8c1f3bf4abd026fed85e3ff8598a9e6f982f0"}, + {file = "jiter-0.11.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:4ee5821e3d66606b29ae5b497230b304f1376f38137d69e35f8d2bd5f310ff73"}, + {file = "jiter-0.11.0-cp314-cp314-win32.whl", hash = "sha256:c2d13ba7567ca8799f17c76ed56b1d49be30df996eb7fa33e46b62800562a5e2"}, + {file = "jiter-0.11.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:fb4790497369d134a07fc763cc88888c46f734abdd66f9fdf7865038bf3a8f40"}, + {file = "jiter-0.11.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e2bbf24f16ba5ad4441a9845e40e4ea0cb9eed00e76ba94050664ef53ef4406"}, + {file = "jiter-0.11.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:719891c2fb7628a41adff4f2f54c19380a27e6fdfdb743c24680ef1a54c67bd0"}, + {file = "jiter-0.11.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:df7f1927cbdf34cb91262a5418ca06920fd42f1cf733936d863aeb29b45a14ef"}, + {file = "jiter-0.11.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e71ae6d969d0c9bab336c5e9e2fabad31e74d823f19e3604eaf96d9a97f463df"}, + {file = "jiter-0.11.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5661469a7b2be25ade3a4bb6c21ffd1e142e13351a0759f264dfdd3ad99af1ab"}, + {file = "jiter-0.11.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:76c15ef0d3d02f8b389066fa4c410a0b89e9cc6468a1f0674c5925d2f3c3e890"}, + {file = "jiter-0.11.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:63782a1350917a27817030716566ed3d5b3c731500fd42d483cbd7094e2c5b25"}, + {file = "jiter-0.11.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a7092b699646a1ddc03a7b112622d9c066172627c7382659befb0d2996f1659"}, + {file = "jiter-0.11.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f637b8e818f6d75540f350a6011ce21252573c0998ea1b4365ee54b7672c23c5"}, + {file = "jiter-0.11.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a624d87719e1b5d09c15286eaee7e1532a40c692a096ea7ca791121365f548c1"}, + {file = "jiter-0.11.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9d0146d8d9b3995821bb586fc8256636258947c2f39da5bab709f3a28fb1a0b"}, + {file = "jiter-0.11.0-cp39-cp39-win32.whl", hash = "sha256:d067655a7cf0831eb8ec3e39cbd752995e9b69a2206df3535b3a067fac23b032"}, + {file = "jiter-0.11.0-cp39-cp39-win_amd64.whl", hash = "sha256:f05d03775a11aaf132c447436983169958439f1219069abf24662a672851f94e"}, + {file = "jiter-0.11.0-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:902b43386c04739229076bd1c4c69de5d115553d982ab442a8ae82947c72ede7"}, + {file = "jiter-0.11.0.tar.gz", hash = "sha256:1d9637eaf8c1d6a63d6562f2a6e5ab3af946c66037eb1b894e8fad75422266e4"}, +] + +[[package]] +name = "jmespath" +version = "1.0.1" +description = "JSON Matching Expressions" +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "jmespath-1.0.1-py3-none-any.whl", hash = "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980"}, + {file = "jmespath-1.0.1.tar.gz", hash = "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe"}, +] + +[[package]] +name = "jsonschema" +version = "4.25.1" +description = "An implementation of JSON Schema validation for Python" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "jsonschema-4.25.1-py3-none-any.whl", hash = "sha256:3fba0169e345c7175110351d456342c364814cfcf3b964ba4587f22915230a63"}, + {file = "jsonschema-4.25.1.tar.gz", hash = "sha256:e4a9655ce0da0c0b67a085847e00a3a51449e1157f4f75e9fb5aa545e122eb85"}, +] + +[package.dependencies] +attrs = ">=22.2.0" +jsonschema-specifications = ">=2023.03.6" +referencing = ">=0.28.4" +rpds-py = ">=0.7.1" + +[package.extras] +format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] +format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "rfc3987-syntax (>=1.1.0)", "uri-template", "webcolors (>=24.6.0)"] + +[[package]] +name = "jsonschema-specifications" +version = "2025.9.1" +description = "The JSON Schema meta-schemas and vocabularies, exposed as a Registry" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe"}, + {file = "jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d"}, +] + +[package.dependencies] +referencing = ">=0.31.0" + +[[package]] +name = "logfire" +version = "4.13.0" +description = "The best Python observability tool! 🪵🔥" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "logfire-4.13.0-py3-none-any.whl", hash = "sha256:6da1ecf9f1d73dda2faea24ab10c9405ddc46dcd7e03c29c0ca3ead4ec59e56e"}, + {file = "logfire-4.13.0.tar.gz", hash = "sha256:692a203e75343ac9b1a2fed1fbb4ed62d6285cfc63439c985087b8e8972024f1"}, +] + +[package.dependencies] +executing = ">=2.0.1" +opentelemetry-exporter-otlp-proto-http = ">=1.35.0,<1.38.0" +opentelemetry-instrumentation = ">=0.41b0" +opentelemetry-sdk = ">=1.35.0,<1.38.0" +protobuf = ">=4.23.4" +rich = ">=13.4.2" +typing-extensions = ">=4.1.0" + +[package.extras] +aiohttp = ["opentelemetry-instrumentation-aiohttp-client (>=0.42b0)"] +aiohttp-client = ["opentelemetry-instrumentation-aiohttp-client (>=0.42b0)"] +aiohttp-server = ["opentelemetry-instrumentation-aiohttp-server (>=0.55b0)"] +asgi = ["opentelemetry-instrumentation-asgi (>=0.42b0)"] +asyncpg = ["opentelemetry-instrumentation-asyncpg (>=0.42b0)"] +aws-lambda = ["opentelemetry-instrumentation-aws-lambda (>=0.42b0)"] +celery = ["opentelemetry-instrumentation-celery (>=0.42b0)"] +django = ["opentelemetry-instrumentation-asgi (>=0.42b0)", "opentelemetry-instrumentation-django (>=0.42b0)"] +fastapi = ["opentelemetry-instrumentation-fastapi (>=0.42b0)"] +flask = ["opentelemetry-instrumentation-flask (>=0.42b0)"] +google-genai = ["opentelemetry-instrumentation-google-genai (>=0)"] +httpx = ["opentelemetry-instrumentation-httpx (>=0.42b0)"] +litellm = ["openinference-instrumentation-litellm (>=0)"] +mysql = ["opentelemetry-instrumentation-mysql (>=0.42b0)"] +psycopg = ["opentelemetry-instrumentation-psycopg (>=0.42b0)", "packaging"] +psycopg2 = ["opentelemetry-instrumentation-psycopg2 (>=0.42b0)", "packaging"] +pymongo = ["opentelemetry-instrumentation-pymongo (>=0.42b0)"] +redis = ["opentelemetry-instrumentation-redis (>=0.42b0)"] +requests = ["opentelemetry-instrumentation-requests (>=0.42b0)"] +sqlalchemy = ["opentelemetry-instrumentation-sqlalchemy (>=0.42b0)"] +sqlite3 = ["opentelemetry-instrumentation-sqlite3 (>=0.42b0)"] +starlette = ["opentelemetry-instrumentation-starlette (>=0.42b0)"] +system-metrics = ["opentelemetry-instrumentation-system-metrics (>=0.42b0)"] +wsgi = ["opentelemetry-instrumentation-wsgi (>=0.42b0)"] + +[[package]] +name = "logfire-api" +version = "4.13.0" +description = "Shim for the Logfire SDK which does nothing unless Logfire is installed" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "logfire_api-4.13.0-py3-none-any.whl", hash = "sha256:6ec2d761e07b7ab65592b464cf8c9ca798fabfa3740b6cd7e2d4eb7909726d2a"}, + {file = "logfire_api-4.13.0.tar.gz", hash = "sha256:24e1508d625af8d0fba3bfe54fd4684d42705cb5b1fafc0a3702df3a880b3270"}, +] + +[[package]] +name = "markdown-it-py" +version = "4.0.0" +description = "Python port of markdown-it. Markdown parsing, done right!" +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147"}, + {file = "markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3"}, +] + +[package.dependencies] +mdurl = ">=0.1,<1.0" + +[package.extras] +benchmarking = ["psutil", "pytest", "pytest-benchmark"] +compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "markdown-it-pyrs", "mistletoe (>=1.0,<2.0)", "mistune (>=3.0,<4.0)", "panflute (>=2.3,<3.0)"] +linkify = ["linkify-it-py (>=1,<3)"] +plugins = ["mdit-py-plugins (>=0.5.0)"] +profiling = ["gprof2dot"] +rtd = ["ipykernel", "jupyter_sphinx", "mdit-py-plugins (>=0.5.0)", "myst-parser", "pyyaml", "sphinx", "sphinx-book-theme (>=1.0,<2.0)", "sphinx-copybutton", "sphinx-design"] +testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions", "requests"] + +[[package]] +name = "mcp" +version = "1.17.0" +description = "Model Context Protocol SDK" +optional = false +python-versions = ">=3.10" +groups = ["main"] +files = [ + {file = "mcp-1.17.0-py3-none-any.whl", hash = "sha256:0660ef275cada7a545af154db3082f176cf1d2681d5e35ae63e014faf0a35d40"}, + {file = "mcp-1.17.0.tar.gz", hash = "sha256:1b57fabf3203240ccc48e39859faf3ae1ccb0b571ff798bbedae800c73c6df90"}, +] + +[package.dependencies] +anyio = ">=4.5" +httpx = ">=0.27.1" +httpx-sse = ">=0.4" +jsonschema = ">=4.20.0" +pydantic = ">=2.11.0,<3.0.0" +pydantic-settings = ">=2.5.2" +python-multipart = ">=0.0.9" +pywin32 = {version = ">=310", markers = "sys_platform == \"win32\""} +sse-starlette = ">=1.6.1" +starlette = ">=0.27" +uvicorn = {version = ">=0.31.1", markers = "sys_platform != \"emscripten\""} + +[package.extras] +cli = ["python-dotenv (>=1.0.0)", "typer (>=0.16.0)"] +rich = ["rich (>=13.9.4)"] +ws = ["websockets (>=15.0.1)"] + +[[package]] +name = "mdurl" +version = "0.1.2" +description = "Markdown URL utilities" +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, + {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, +] + +[[package]] +name = "minio" +version = "7.2.18" +description = "MinIO Python SDK for Amazon S3 Compatible Cloud Storage" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "minio-7.2.18-py3-none-any.whl", hash = "sha256:f23a6edbff8d0bc4b5c1a61b2628a01c5a3342aefc613ff9c276012e6321108f"}, + {file = "minio-7.2.18.tar.gz", hash = "sha256:173402a5716099159c5659f9de75be204ebe248557b9f1cc9cf45aa70e9d3024"}, +] + +[package.dependencies] +argon2-cffi = "*" +certifi = "*" +pycryptodome = "*" +typing-extensions = "*" +urllib3 = "*" + +[[package]] +name = "mistralai" +version = "1.9.11" +description = "Python Client SDK for the Mistral AI API." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "mistralai-1.9.11-py3-none-any.whl", hash = "sha256:7a3dc2b8ef3fceaa3582220234261b5c4e3e03a972563b07afa150e44a25a6d3"}, + {file = "mistralai-1.9.11.tar.gz", hash = "sha256:3df9e403c31a756ec79e78df25ee73cea3eb15f86693773e16b16adaf59c9b8a"}, +] + +[package.dependencies] +eval-type-backport = ">=0.2.0" +httpx = ">=0.28.1" +invoke = ">=2.2.0,<3.0.0" +pydantic = ">=2.10.3" +python-dateutil = ">=2.8.2" +pyyaml = ">=6.0.2,<7.0.0" +typing-inspection = ">=0.4.0" + +[package.extras] +agents = ["authlib (>=1.5.2,<2.0)", "griffe (>=1.7.3,<2.0)", "mcp (>=1.0,<2.0)"] +gcp = ["google-auth (>=2.27.0)", "requests (>=2.32.3)"] + +[[package]] +name = "multidict" +version = "6.7.0" +description = "multidict implementation" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "multidict-6.7.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:9f474ad5acda359c8758c8accc22032c6abe6dc87a8be2440d097785e27a9349"}, + {file = "multidict-6.7.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4b7a9db5a870f780220e931d0002bbfd88fb53aceb6293251e2c839415c1b20e"}, + {file = "multidict-6.7.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:03ca744319864e92721195fa28c7a3b2bc7b686246b35e4078c1e4d0eb5466d3"}, + {file = "multidict-6.7.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f0e77e3c0008bc9316e662624535b88d360c3a5d3f81e15cf12c139a75250046"}, + {file = "multidict-6.7.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:08325c9e5367aa379a3496aa9a022fe8837ff22e00b94db256d3a1378c76ab32"}, + {file = "multidict-6.7.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e2862408c99f84aa571ab462d25236ef9cb12a602ea959ba9c9009a54902fc73"}, + {file = "multidict-6.7.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4d72a9a2d885f5c208b0cb91ff2ed43636bb7e345ec839ff64708e04f69a13cc"}, + {file = "multidict-6.7.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:478cc36476687bac1514d651cbbaa94b86b0732fb6855c60c673794c7dd2da62"}, + {file = "multidict-6.7.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6843b28b0364dc605f21481c90fadb5f60d9123b442eb8a726bb74feef588a84"}, + {file = "multidict-6.7.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:23bfeee5316266e5ee2d625df2d2c602b829435fc3a235c2ba2131495706e4a0"}, + {file = "multidict-6.7.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:680878b9f3d45c31e1f730eef731f9b0bc1da456155688c6745ee84eb818e90e"}, + {file = "multidict-6.7.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:eb866162ef2f45063acc7a53a88ef6fe8bf121d45c30ea3c9cd87ce7e191a8d4"}, + {file = "multidict-6.7.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:df0e3bf7993bdbeca5ac25aa859cf40d39019e015c9c91809ba7093967f7a648"}, + {file = "multidict-6.7.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:661709cdcd919a2ece2234f9bae7174e5220c80b034585d7d8a755632d3e2111"}, + {file = "multidict-6.7.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:096f52730c3fb8ed419db2d44391932b63891b2c5ed14850a7e215c0ba9ade36"}, + {file = "multidict-6.7.0-cp310-cp310-win32.whl", hash = "sha256:afa8a2978ec65d2336305550535c9c4ff50ee527914328c8677b3973ade52b85"}, + {file = "multidict-6.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:b15b3afff74f707b9275d5ba6a91ae8f6429c3ffb29bbfd216b0b375a56f13d7"}, + {file = "multidict-6.7.0-cp310-cp310-win_arm64.whl", hash = "sha256:4b73189894398d59131a66ff157837b1fafea9974be486d036bb3d32331fdbf0"}, + {file = "multidict-6.7.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4d409aa42a94c0b3fa617708ef5276dfe81012ba6753a0370fcc9d0195d0a1fc"}, + {file = "multidict-6.7.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:14c9e076eede3b54c636f8ce1c9c252b5f057c62131211f0ceeec273810c9721"}, + {file = "multidict-6.7.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4c09703000a9d0fa3c3404b27041e574cc7f4df4c6563873246d0e11812a94b6"}, + {file = "multidict-6.7.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:a265acbb7bb33a3a2d626afbe756371dce0279e7b17f4f4eda406459c2b5ff1c"}, + {file = "multidict-6.7.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:51cb455de290ae462593e5b1cb1118c5c22ea7f0d3620d9940bf695cea5a4bd7"}, + {file = "multidict-6.7.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:db99677b4457c7a5c5a949353e125ba72d62b35f74e26da141530fbb012218a7"}, + {file = "multidict-6.7.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f470f68adc395e0183b92a2f4689264d1ea4b40504a24d9882c27375e6662bb9"}, + {file = "multidict-6.7.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0db4956f82723cc1c270de9c6e799b4c341d327762ec78ef82bb962f79cc07d8"}, + {file = "multidict-6.7.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3e56d780c238f9e1ae66a22d2adf8d16f485381878250db8d496623cd38b22bd"}, + {file = "multidict-6.7.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9d14baca2ee12c1a64740d4531356ba50b82543017f3ad6de0deb943c5979abb"}, + {file = "multidict-6.7.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:295a92a76188917c7f99cda95858c822f9e4aae5824246bba9b6b44004ddd0a6"}, + {file = "multidict-6.7.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:39f1719f57adbb767ef592a50ae5ebb794220d1188f9ca93de471336401c34d2"}, + {file = "multidict-6.7.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:0a13fb8e748dfc94749f622de065dd5c1def7e0d2216dba72b1d8069a389c6ff"}, + {file = "multidict-6.7.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e3aa16de190d29a0ea1b48253c57d99a68492c8dd8948638073ab9e74dc9410b"}, + {file = "multidict-6.7.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a048ce45dcdaaf1defb76b2e684f997fb5abf74437b6cb7b22ddad934a964e34"}, + {file = "multidict-6.7.0-cp311-cp311-win32.whl", hash = "sha256:a90af66facec4cebe4181b9e62a68be65e45ac9b52b67de9eec118701856e7ff"}, + {file = "multidict-6.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:95b5ffa4349df2887518bb839409bcf22caa72d82beec453216802f475b23c81"}, + {file = "multidict-6.7.0-cp311-cp311-win_arm64.whl", hash = "sha256:329aa225b085b6f004a4955271a7ba9f1087e39dcb7e65f6284a988264a63912"}, + {file = "multidict-6.7.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8a3862568a36d26e650a19bb5cbbba14b71789032aebc0423f8cc5f150730184"}, + {file = "multidict-6.7.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:960c60b5849b9b4f9dcc9bea6e3626143c252c74113df2c1540aebce70209b45"}, + {file = "multidict-6.7.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2049be98fb57a31b4ccf870bf377af2504d4ae35646a19037ec271e4c07998aa"}, + {file = "multidict-6.7.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0934f3843a1860dd465d38895c17fce1f1cb37295149ab05cd1b9a03afacb2a7"}, + {file = "multidict-6.7.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b3e34f3a1b8131ba06f1a73adab24f30934d148afcd5f5de9a73565a4404384e"}, + {file = "multidict-6.7.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:efbb54e98446892590dc2458c19c10344ee9a883a79b5cec4bc34d6656e8d546"}, + {file = "multidict-6.7.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a35c5fc61d4f51eb045061e7967cfe3123d622cd500e8868e7c0c592a09fedc4"}, + {file = "multidict-6.7.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29fe6740ebccba4175af1b9b87bf553e9c15cd5868ee967e010efcf94e4fd0f1"}, + {file = "multidict-6.7.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:123e2a72e20537add2f33a79e605f6191fba2afda4cbb876e35c1a7074298a7d"}, + {file = "multidict-6.7.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b284e319754366c1aee2267a2036248b24eeb17ecd5dc16022095e747f2f4304"}, + {file = "multidict-6.7.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:803d685de7be4303b5a657b76e2f6d1240e7e0a8aa2968ad5811fa2285553a12"}, + {file = "multidict-6.7.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c04a328260dfd5db8c39538f999f02779012268f54614902d0afc775d44e0a62"}, + {file = "multidict-6.7.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8a19cdb57cd3df4cd865849d93ee14920fb97224300c88501f16ecfa2604b4e0"}, + {file = "multidict-6.7.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9b2fd74c52accced7e75de26023b7dccee62511a600e62311b918ec5c168fc2a"}, + {file = "multidict-6.7.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3e8bfdd0e487acf992407a140d2589fe598238eaeffa3da8448d63a63cd363f8"}, + {file = "multidict-6.7.0-cp312-cp312-win32.whl", hash = "sha256:dd32a49400a2c3d52088e120ee00c1e3576cbff7e10b98467962c74fdb762ed4"}, + {file = "multidict-6.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:92abb658ef2d7ef22ac9f8bb88e8b6c3e571671534e029359b6d9e845923eb1b"}, + {file = "multidict-6.7.0-cp312-cp312-win_arm64.whl", hash = "sha256:490dab541a6a642ce1a9d61a4781656b346a55c13038f0b1244653828e3a83ec"}, + {file = "multidict-6.7.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:bee7c0588aa0076ce77c0ea5d19a68d76ad81fcd9fe8501003b9a24f9d4000f6"}, + {file = "multidict-6.7.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7ef6b61cad77091056ce0e7ce69814ef72afacb150b7ac6a3e9470def2198159"}, + {file = "multidict-6.7.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9c0359b1ec12b1d6849c59f9d319610b7f20ef990a6d454ab151aa0e3b9f78ca"}, + {file = "multidict-6.7.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cd240939f71c64bd658f186330603aac1a9a81bf6273f523fca63673cb7378a8"}, + {file = "multidict-6.7.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a60a4d75718a5efa473ebd5ab685786ba0c67b8381f781d1be14da49f1a2dc60"}, + {file = "multidict-6.7.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:53a42d364f323275126aff81fb67c5ca1b7a04fda0546245730a55c8c5f24bc4"}, + {file = "multidict-6.7.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3b29b980d0ddbecb736735ee5bef69bb2ddca56eff603c86f3f29a1128299b4f"}, + {file = "multidict-6.7.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f8a93b1c0ed2d04b97a5e9336fd2d33371b9a6e29ab7dd6503d63407c20ffbaf"}, + {file = "multidict-6.7.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9ff96e8815eecacc6645da76c413eb3b3d34cfca256c70b16b286a687d013c32"}, + {file = "multidict-6.7.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7516c579652f6a6be0e266aec0acd0db80829ca305c3d771ed898538804c2036"}, + {file = "multidict-6.7.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:040f393368e63fb0f3330e70c26bfd336656bed925e5cbe17c9da839a6ab13ec"}, + {file = "multidict-6.7.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b3bc26a951007b1057a1c543af845f1c7e3e71cc240ed1ace7bf4484aa99196e"}, + {file = "multidict-6.7.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:7b022717c748dd1992a83e219587aabe45980d88969f01b316e78683e6285f64"}, + {file = "multidict-6.7.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:9600082733859f00d79dee64effc7aef1beb26adb297416a4ad2116fd61374bd"}, + {file = "multidict-6.7.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:94218fcec4d72bc61df51c198d098ce2b378e0ccbac41ddbed5ef44092913288"}, + {file = "multidict-6.7.0-cp313-cp313-win32.whl", hash = "sha256:a37bd74c3fa9d00be2d7b8eca074dc56bd8077ddd2917a839bd989612671ed17"}, + {file = "multidict-6.7.0-cp313-cp313-win_amd64.whl", hash = "sha256:30d193c6cc6d559db42b6bcec8a5d395d34d60c9877a0b71ecd7c204fcf15390"}, + {file = "multidict-6.7.0-cp313-cp313-win_arm64.whl", hash = "sha256:ea3334cabe4d41b7ccd01e4d349828678794edbc2d3ae97fc162a3312095092e"}, + {file = "multidict-6.7.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:ad9ce259f50abd98a1ca0aa6e490b58c316a0fce0617f609723e40804add2c00"}, + {file = "multidict-6.7.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:07f5594ac6d084cbb5de2df218d78baf55ef150b91f0ff8a21cc7a2e3a5a58eb"}, + {file = "multidict-6.7.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:0591b48acf279821a579282444814a2d8d0af624ae0bc600aa4d1b920b6e924b"}, + {file = "multidict-6.7.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:749a72584761531d2b9467cfbdfd29487ee21124c304c4b6cb760d8777b27f9c"}, + {file = "multidict-6.7.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b4c3d199f953acd5b446bf7c0de1fe25d94e09e79086f8dc2f48a11a129cdf1"}, + {file = "multidict-6.7.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:9fb0211dfc3b51efea2f349ec92c114d7754dd62c01f81c3e32b765b70c45c9b"}, + {file = "multidict-6.7.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a027ec240fe73a8d6281872690b988eed307cd7d91b23998ff35ff577ca688b5"}, + {file = "multidict-6.7.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1d964afecdf3a8288789df2f5751dc0a8261138c3768d9af117ed384e538fad"}, + {file = "multidict-6.7.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:caf53b15b1b7df9fbd0709aa01409000a2b4dd03a5f6f5cc548183c7c8f8b63c"}, + {file = "multidict-6.7.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:654030da3197d927f05a536a66186070e98765aa5142794c9904555d3a9d8fb5"}, + {file = "multidict-6.7.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:2090d3718829d1e484706a2f525e50c892237b2bf9b17a79b059cb98cddc2f10"}, + {file = "multidict-6.7.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:2d2cfeec3f6f45651b3d408c4acec0ebf3daa9bc8a112a084206f5db5d05b754"}, + {file = "multidict-6.7.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:4ef089f985b8c194d341eb2c24ae6e7408c9a0e2e5658699c92f497437d88c3c"}, + {file = "multidict-6.7.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e93a0617cd16998784bf4414c7e40f17a35d2350e5c6f0bd900d3a8e02bd3762"}, + {file = "multidict-6.7.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f0feece2ef8ebc42ed9e2e8c78fc4aa3cf455733b507c09ef7406364c94376c6"}, + {file = "multidict-6.7.0-cp313-cp313t-win32.whl", hash = "sha256:19a1d55338ec1be74ef62440ca9e04a2f001a04d0cc49a4983dc320ff0f3212d"}, + {file = "multidict-6.7.0-cp313-cp313t-win_amd64.whl", hash = "sha256:3da4fb467498df97e986af166b12d01f05d2e04f978a9c1c680ea1988e0bc4b6"}, + {file = "multidict-6.7.0-cp313-cp313t-win_arm64.whl", hash = "sha256:b4121773c49a0776461f4a904cdf6264c88e42218aaa8407e803ca8025872792"}, + {file = "multidict-6.7.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3bab1e4aff7adaa34410f93b1f8e57c4b36b9af0426a76003f441ee1d3c7e842"}, + {file = "multidict-6.7.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:b8512bac933afc3e45fb2b18da8e59b78d4f408399a960339598374d4ae3b56b"}, + {file = "multidict-6.7.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:79dcf9e477bc65414ebfea98ffd013cb39552b5ecd62908752e0e413d6d06e38"}, + {file = "multidict-6.7.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:31bae522710064b5cbeddaf2e9f32b1abab70ac6ac91d42572502299e9953128"}, + {file = "multidict-6.7.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a0df7ff02397bb63e2fd22af2c87dfa39e8c7f12947bc524dbdc528282c7e34"}, + {file = "multidict-6.7.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7a0222514e8e4c514660e182d5156a415c13ef0aabbd71682fc714e327b95e99"}, + {file = "multidict-6.7.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2397ab4daaf2698eb51a76721e98db21ce4f52339e535725de03ea962b5a3202"}, + {file = "multidict-6.7.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8891681594162635948a636c9fe0ff21746aeb3dd5463f6e25d9bea3a8a39ca1"}, + {file = "multidict-6.7.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:18706cc31dbf402a7945916dd5cddf160251b6dab8a2c5f3d6d5a55949f676b3"}, + {file = "multidict-6.7.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f844a1bbf1d207dd311a56f383f7eda2d0e134921d45751842d8235e7778965d"}, + {file = "multidict-6.7.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:d4393e3581e84e5645506923816b9cc81f5609a778c7e7534054091acc64d1c6"}, + {file = "multidict-6.7.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:fbd18dc82d7bf274b37aa48d664534330af744e03bccf696d6f4c6042e7d19e7"}, + {file = "multidict-6.7.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:b6234e14f9314731ec45c42fc4554b88133ad53a09092cc48a88e771c125dadb"}, + {file = "multidict-6.7.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:08d4379f9744d8f78d98c8673c06e202ffa88296f009c71bbafe8a6bf847d01f"}, + {file = "multidict-6.7.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:9fe04da3f79387f450fd0061d4dd2e45a72749d31bf634aecc9e27f24fdc4b3f"}, + {file = "multidict-6.7.0-cp314-cp314-win32.whl", hash = "sha256:fbafe31d191dfa7c4c51f7a6149c9fb7e914dcf9ffead27dcfd9f1ae382b3885"}, + {file = "multidict-6.7.0-cp314-cp314-win_amd64.whl", hash = "sha256:2f67396ec0310764b9222a1728ced1ab638f61aadc6226f17a71dd9324f9a99c"}, + {file = "multidict-6.7.0-cp314-cp314-win_arm64.whl", hash = "sha256:ba672b26069957ee369cfa7fc180dde1fc6f176eaf1e6beaf61fbebbd3d9c000"}, + {file = "multidict-6.7.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:c1dcc7524066fa918c6a27d61444d4ee7900ec635779058571f70d042d86ed63"}, + {file = "multidict-6.7.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:27e0b36c2d388dc7b6ced3406671b401e84ad7eb0656b8f3a2f46ed0ce483718"}, + {file = "multidict-6.7.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2a7baa46a22e77f0988e3b23d4ede5513ebec1929e34ee9495be535662c0dfe2"}, + {file = "multidict-6.7.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7bf77f54997a9166a2f5675d1201520586439424c2511723a7312bdb4bcc034e"}, + {file = "multidict-6.7.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e011555abada53f1578d63389610ac8a5400fc70ce71156b0aa30d326f1a5064"}, + {file = "multidict-6.7.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:28b37063541b897fd6a318007373930a75ca6d6ac7c940dbe14731ffdd8d498e"}, + {file = "multidict-6.7.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:05047ada7a2fde2631a0ed706f1fd68b169a681dfe5e4cf0f8e4cb6618bbc2cd"}, + {file = "multidict-6.7.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:716133f7d1d946a4e1b91b1756b23c088881e70ff180c24e864c26192ad7534a"}, + {file = "multidict-6.7.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d1bed1b467ef657f2a0ae62844a607909ef1c6889562de5e1d505f74457d0b96"}, + {file = "multidict-6.7.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:ca43bdfa5d37bd6aee89d85e1d0831fb86e25541be7e9d376ead1b28974f8e5e"}, + {file = "multidict-6.7.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:44b546bd3eb645fd26fb949e43c02a25a2e632e2ca21a35e2e132c8105dc8599"}, + {file = "multidict-6.7.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:a6ef16328011d3f468e7ebc326f24c1445f001ca1dec335b2f8e66bed3006394"}, + {file = "multidict-6.7.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:5aa873cbc8e593d361ae65c68f85faadd755c3295ea2c12040ee146802f23b38"}, + {file = "multidict-6.7.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:3d7b6ccce016e29df4b7ca819659f516f0bc7a4b3efa3bb2012ba06431b044f9"}, + {file = "multidict-6.7.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:171b73bd4ee683d307599b66793ac80981b06f069b62eea1c9e29c9241aa66b0"}, + {file = "multidict-6.7.0-cp314-cp314t-win32.whl", hash = "sha256:b2d7f80c4e1fd010b07cb26820aae86b7e73b681ee4889684fb8d2d4537aab13"}, + {file = "multidict-6.7.0-cp314-cp314t-win_amd64.whl", hash = "sha256:09929cab6fcb68122776d575e03c6cc64ee0b8fca48d17e135474b042ce515cd"}, + {file = "multidict-6.7.0-cp314-cp314t-win_arm64.whl", hash = "sha256:cc41db090ed742f32bd2d2c721861725e6109681eddf835d0a82bd3a5c382827"}, + {file = "multidict-6.7.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:363eb68a0a59bd2303216d2346e6c441ba10d36d1f9969fcb6f1ba700de7bb5c"}, + {file = "multidict-6.7.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d874eb056410ca05fed180b6642e680373688efafc7f077b2a2f61811e873a40"}, + {file = "multidict-6.7.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8b55d5497b51afdfde55925e04a022f1de14d4f4f25cdfd4f5d9b0aa96166851"}, + {file = "multidict-6.7.0-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f8e5c0031b90ca9ce555e2e8fd5c3b02a25f14989cbc310701823832c99eb687"}, + {file = "multidict-6.7.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9cf41880c991716f3c7cec48e2f19ae4045fc9db5fc9cff27347ada24d710bb5"}, + {file = "multidict-6.7.0-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8cfc12a8630a29d601f48d47787bd7eb730e475e83edb5d6c5084317463373eb"}, + {file = "multidict-6.7.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3996b50c3237c4aec17459217c1e7bbdead9a22a0fcd3c365564fbd16439dde6"}, + {file = "multidict-6.7.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7f5170993a0dd3ab871c74f45c0a21a4e2c37a2f2b01b5f722a2ad9c6650469e"}, + {file = "multidict-6.7.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ec81878ddf0e98817def1e77d4f50dae5ef5b0e4fe796fae3bd674304172416e"}, + {file = "multidict-6.7.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:9281bf5b34f59afbc6b1e477a372e9526b66ca446f4bf62592839c195a718b32"}, + {file = "multidict-6.7.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:68af405971779d8b37198726f2b6fe3955db846fee42db7a4286fc542203934c"}, + {file = "multidict-6.7.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3ba3ef510467abb0667421a286dc906e30eb08569365f5cdb131d7aff7c2dd84"}, + {file = "multidict-6.7.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:b61189b29081a20c7e4e0b49b44d5d44bb0dc92be3c6d06a11cc043f81bf9329"}, + {file = "multidict-6.7.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:fb287618b9c7aa3bf8d825f02d9201b2f13078a5ed3b293c8f4d953917d84d5e"}, + {file = "multidict-6.7.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:521f33e377ff64b96c4c556b81c55d0cfffb96a11c194fd0c3f1e56f3d8dd5a4"}, + {file = "multidict-6.7.0-cp39-cp39-win32.whl", hash = "sha256:ce8fdc2dca699f8dbf055a61d73eaa10482569ad20ee3c36ef9641f69afa8c91"}, + {file = "multidict-6.7.0-cp39-cp39-win_amd64.whl", hash = "sha256:7e73299c99939f089dd9b2120a04a516b95cdf8c1cd2b18c53ebf0de80b1f18f"}, + {file = "multidict-6.7.0-cp39-cp39-win_arm64.whl", hash = "sha256:6bdce131e14b04fd34a809b6380dbfd826065c3e2fe8a50dbae659fa0c390546"}, + {file = "multidict-6.7.0-py3-none-any.whl", hash = "sha256:394fc5c42a333c9ffc3e421a4c85e08580d990e08b99f6bf35b4132114c5dcb3"}, + {file = "multidict-6.7.0.tar.gz", hash = "sha256:c6e99d9a65ca282e578dfea819cfa9c0a62b2499d8677392e09feaf305e9e6f5"}, +] + +[[package]] +name = "nexus-rpc" +version = "1.1.0" +description = "Nexus Python SDK" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "nexus_rpc-1.1.0-py3-none-any.whl", hash = "sha256:d1b007af2aba186a27e736f8eaae39c03aed05b488084ff6c3d1785c9ba2ad38"}, + {file = "nexus_rpc-1.1.0.tar.gz", hash = "sha256:d65ad6a2f54f14e53ebe39ee30555eaeb894102437125733fb13034a04a44553"}, +] + +[package.dependencies] +typing-extensions = ">=4.12.2" + +[[package]] +name = "openai" +version = "2.3.0" +description = "The official Python library for the openai API" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "openai-2.3.0-py3-none-any.whl", hash = "sha256:a7aa83be6f7b0ab2e4d4d7bcaf36e3d790874c0167380c5d0afd0ed99a86bd7b"}, + {file = "openai-2.3.0.tar.gz", hash = "sha256:8d213ee5aaf91737faea2d7fc1cd608657a5367a18966372a3756ceaabfbd812"}, +] + +[package.dependencies] +anyio = ">=3.5.0,<5" +distro = ">=1.7.0,<2" +httpx = ">=0.23.0,<1" +jiter = ">=0.10.0,<1" +pydantic = ">=1.9.0,<3" +sniffio = "*" +tqdm = ">4" +typing-extensions = ">=4.11,<5" + +[package.extras] +aiohttp = ["aiohttp", "httpx-aiohttp (>=0.1.8)"] +datalib = ["numpy (>=1)", "pandas (>=1.2.3)", "pandas-stubs (>=1.1.0.11)"] +realtime = ["websockets (>=13,<16)"] +voice-helpers = ["numpy (>=2.0.2)", "sounddevice (>=0.5.1)"] + +[[package]] +name = "openinference-instrumentation" +version = "0.1.40" +description = "OpenInference instrumentation utilities" +optional = false +python-versions = "<3.15,>=3.9" +groups = ["main"] +files = [ + {file = "openinference_instrumentation-0.1.40-py3-none-any.whl", hash = "sha256:d2e894f25addb1dfba563789213139876c5a01fca0a1fa8aa52a455a988a11d4"}, + {file = "openinference_instrumentation-0.1.40.tar.gz", hash = "sha256:3080785479793a56023806c71dccbc39418925947407667794c651f992f700a2"}, +] + +[package.dependencies] +openinference-semantic-conventions = ">=0.1.17" +opentelemetry-api = "*" +opentelemetry-sdk = "*" +wrapt = ">=1.14.0" + +[package.extras] +test = ["jsonschema", "openai", "pydantic (>=2.0.0)", "pytest-asyncio", "pytest-recording", "types-jsonschema"] + +[[package]] +name = "openinference-instrumentation-pydantic-ai" +version = "0.1.8" +description = "OpenInference PydanticAI Instrumentation" +optional = false +python-versions = "<3.15,>=3.9" +groups = ["main"] +files = [ + {file = "openinference_instrumentation_pydantic_ai-0.1.8-py3-none-any.whl", hash = "sha256:c5bdb7071ed455341858a8b7692bc589cf19c109dd4b6bd836d256f148f5bce6"}, + {file = "openinference_instrumentation_pydantic_ai-0.1.8.tar.gz", hash = "sha256:e1489bb32b2fc88b46d2d87c73d06c11676a8262df4c3e0a01a1d217a60e70b4"}, +] + +[package.dependencies] +openinference-instrumentation = ">=0.1.27" +openinference-semantic-conventions = ">=0.1.17" +opentelemetry-api = "*" +opentelemetry-instrumentation = "*" +opentelemetry-semantic-conventions = ">=0.54b1" +typing-extensions = "*" +wrapt = "*" + +[package.extras] +instruments = ["pydantic-ai (>=0.2.0)"] +test = ["opentelemetry-exporter-otlp-proto-http", "opentelemetry-sdk (>=1.20.0)", "pydantic-ai (>=0.2.0)", "pytest (>=7.4.0)", "pytest-cov (>=4.1.0)", "pytest-vcr"] + +[[package]] +name = "openinference-semantic-conventions" +version = "0.1.24" +description = "OpenInference Semantic Conventions" +optional = false +python-versions = "<3.15,>=3.9" +groups = ["main"] +files = [ + {file = "openinference_semantic_conventions-0.1.24-py3-none-any.whl", hash = "sha256:b2d650ca7e39c5fb02bf908b8049d6ece2a2657757448e1925a38b59548a80b3"}, + {file = "openinference_semantic_conventions-0.1.24.tar.gz", hash = "sha256:3223b8c3958525457a369d58ebf0c56230a1f00567ae1e99f1c2049a8ac2cacd"}, +] + +[[package]] +name = "opentelemetry-api" +version = "1.36.0" +description = "OpenTelemetry Python API" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "opentelemetry_api-1.36.0-py3-none-any.whl", hash = "sha256:02f20bcacf666e1333b6b1f04e647dc1d5111f86b8e510238fcc56d7762cda8c"}, + {file = "opentelemetry_api-1.36.0.tar.gz", hash = "sha256:9a72572b9c416d004d492cbc6e61962c0501eaf945ece9b5a0f56597d8348aa0"}, +] + +[package.dependencies] +importlib-metadata = ">=6.0,<8.8.0" +typing-extensions = ">=4.5.0" + +[[package]] +name = "opentelemetry-exporter-otlp-proto-common" +version = "1.36.0" +description = "OpenTelemetry Protobuf encoding" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "opentelemetry_exporter_otlp_proto_common-1.36.0-py3-none-any.whl", hash = "sha256:0fc002a6ed63eac235ada9aa7056e5492e9a71728214a61745f6ad04b923f840"}, + {file = "opentelemetry_exporter_otlp_proto_common-1.36.0.tar.gz", hash = "sha256:6c496ccbcbe26b04653cecadd92f73659b814c6e3579af157d8716e5f9f25cbf"}, +] + +[package.dependencies] +opentelemetry-proto = "1.36.0" + +[[package]] +name = "opentelemetry-exporter-otlp-proto-grpc" +version = "1.36.0" +description = "OpenTelemetry Collector Protobuf over gRPC Exporter" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "opentelemetry_exporter_otlp_proto_grpc-1.36.0-py3-none-any.whl", hash = "sha256:734e841fc6a5d6f30e7be4d8053adb703c70ca80c562ae24e8083a28fadef211"}, + {file = "opentelemetry_exporter_otlp_proto_grpc-1.36.0.tar.gz", hash = "sha256:b281afbf7036b325b3588b5b6c8bb175069e3978d1bd24071f4a59d04c1e5bbf"}, +] + +[package.dependencies] +googleapis-common-protos = ">=1.57,<2.0" +grpcio = [ + {version = ">=1.63.2,<2.0.0", markers = "python_version < \"3.13\""}, + {version = ">=1.66.2,<2.0.0", markers = "python_version >= \"3.13\""}, +] +opentelemetry-api = ">=1.15,<2.0" +opentelemetry-exporter-otlp-proto-common = "1.36.0" +opentelemetry-proto = "1.36.0" +opentelemetry-sdk = ">=1.36.0,<1.37.0" +typing-extensions = ">=4.6.0" + +[[package]] +name = "opentelemetry-exporter-otlp-proto-http" +version = "1.36.0" +description = "OpenTelemetry Collector Protobuf over HTTP Exporter" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "opentelemetry_exporter_otlp_proto_http-1.36.0-py3-none-any.whl", hash = "sha256:3d769f68e2267e7abe4527f70deb6f598f40be3ea34c6adc35789bea94a32902"}, + {file = "opentelemetry_exporter_otlp_proto_http-1.36.0.tar.gz", hash = "sha256:dd3637f72f774b9fc9608ab1ac479f8b44d09b6fb5b2f3df68a24ad1da7d356e"}, +] + +[package.dependencies] +googleapis-common-protos = ">=1.52,<2.0" +opentelemetry-api = ">=1.15,<2.0" +opentelemetry-exporter-otlp-proto-common = "1.36.0" +opentelemetry-proto = "1.36.0" +opentelemetry-sdk = ">=1.36.0,<1.37.0" +requests = ">=2.7,<3.0" +typing-extensions = ">=4.5.0" + +[[package]] +name = "opentelemetry-instrumentation" +version = "0.57b0" +description = "Instrumentation Tools & Auto Instrumentation for OpenTelemetry Python" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "opentelemetry_instrumentation-0.57b0-py3-none-any.whl", hash = "sha256:9109280f44882e07cec2850db28210b90600ae9110b42824d196de357cbddf7e"}, + {file = "opentelemetry_instrumentation-0.57b0.tar.gz", hash = "sha256:f2a30135ba77cdea2b0e1df272f4163c154e978f57214795d72f40befd4fcf05"}, +] + +[package.dependencies] +opentelemetry-api = ">=1.4,<2.0" +opentelemetry-semantic-conventions = "0.57b0" +packaging = ">=18.0" +wrapt = ">=1.0.0,<2.0.0" + +[[package]] +name = "opentelemetry-instrumentation-asgi" +version = "0.57b0" +description = "ASGI instrumentation for OpenTelemetry" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "opentelemetry_instrumentation_asgi-0.57b0-py3-none-any.whl", hash = "sha256:47debbde6af066a7e8e911f7193730d5e40d62effc1ac2e1119908347790a3ea"}, + {file = "opentelemetry_instrumentation_asgi-0.57b0.tar.gz", hash = "sha256:a6f880b5d1838f65688fc992c65fbb1d3571f319d370990c32e759d3160e510b"}, +] + +[package.dependencies] +asgiref = ">=3.0,<4.0" +opentelemetry-api = ">=1.12,<2.0" +opentelemetry-instrumentation = "0.57b0" +opentelemetry-semantic-conventions = "0.57b0" +opentelemetry-util-http = "0.57b0" + +[package.extras] +instruments = ["asgiref (>=3.0,<4.0)"] + +[[package]] +name = "opentelemetry-instrumentation-fastapi" +version = "0.57b0" +description = "OpenTelemetry FastAPI Instrumentation" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "opentelemetry_instrumentation_fastapi-0.57b0-py3-none-any.whl", hash = "sha256:61e6402749ffe0bfec582e58155e0d81dd38723cd9bc4562bca1acca80334006"}, + {file = "opentelemetry_instrumentation_fastapi-0.57b0.tar.gz", hash = "sha256:73ac22f3c472a8f9cb21d1fbe5a4bf2797690c295fff4a1c040e9b1b1688a105"}, +] + +[package.dependencies] +opentelemetry-api = ">=1.12,<2.0" +opentelemetry-instrumentation = "0.57b0" +opentelemetry-instrumentation-asgi = "0.57b0" +opentelemetry-semantic-conventions = "0.57b0" +opentelemetry-util-http = "0.57b0" + +[package.extras] +instruments = ["fastapi (>=0.92,<1.0)"] + +[[package]] +name = "opentelemetry-proto" +version = "1.36.0" +description = "OpenTelemetry Python Proto" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "opentelemetry_proto-1.36.0-py3-none-any.whl", hash = "sha256:151b3bf73a09f94afc658497cf77d45a565606f62ce0c17acb08cd9937ca206e"}, + {file = "opentelemetry_proto-1.36.0.tar.gz", hash = "sha256:0f10b3c72f74c91e0764a5ec88fd8f1c368ea5d9c64639fb455e2854ef87dd2f"}, +] + +[package.dependencies] +protobuf = ">=5.0,<7.0" + +[[package]] +name = "opentelemetry-sdk" +version = "1.36.0" +description = "OpenTelemetry Python SDK" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "opentelemetry_sdk-1.36.0-py3-none-any.whl", hash = "sha256:19fe048b42e98c5c1ffe85b569b7073576ad4ce0bcb6e9b4c6a39e890a6c45fb"}, + {file = "opentelemetry_sdk-1.36.0.tar.gz", hash = "sha256:19c8c81599f51b71670661ff7495c905d8fdf6976e41622d5245b791b06fa581"}, +] + +[package.dependencies] +opentelemetry-api = "1.36.0" +opentelemetry-semantic-conventions = "0.57b0" +typing-extensions = ">=4.5.0" + +[[package]] +name = "opentelemetry-semantic-conventions" +version = "0.57b0" +description = "OpenTelemetry Semantic Conventions" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "opentelemetry_semantic_conventions-0.57b0-py3-none-any.whl", hash = "sha256:757f7e76293294f124c827e514c2a3144f191ef175b069ce8d1211e1e38e9e78"}, + {file = "opentelemetry_semantic_conventions-0.57b0.tar.gz", hash = "sha256:609a4a79c7891b4620d64c7aac6898f872d790d75f22019913a660756f27ff32"}, +] + +[package.dependencies] +opentelemetry-api = "1.36.0" +typing-extensions = ">=4.5.0" + +[[package]] +name = "opentelemetry-util-http" +version = "0.57b0" +description = "Web util for OpenTelemetry" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "opentelemetry_util_http-0.57b0-py3-none-any.whl", hash = "sha256:e54c0df5543951e471c3d694f85474977cd5765a3b7654398c83bab3d2ffb8e9"}, + {file = "opentelemetry_util_http-0.57b0.tar.gz", hash = "sha256:f7417595ead0eb42ed1863ec9b2f839fc740368cd7bbbfc1d0a47bc1ab0aba11"}, +] + +[[package]] +name = "packaging" +version = "25.0" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.8" +groups = ["main", "dev"] +files = [ + {file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"}, + {file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"}, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746"}, + {file = "pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["coverage", "pytest", "pytest-benchmark"] + +[[package]] +name = "prompt-toolkit" +version = "3.0.52" +description = "Library for building powerful interactive command lines in Python" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "prompt_toolkit-3.0.52-py3-none-any.whl", hash = "sha256:9aac639a3bbd33284347de5ad8d68ecc044b91a762dc39b7c21095fcd6a19955"}, + {file = "prompt_toolkit-3.0.52.tar.gz", hash = "sha256:28cde192929c8e7321de85de1ddbe736f1375148b02f2e17edd840042b1be855"}, +] + +[package.dependencies] +wcwidth = "*" + +[[package]] +name = "propcache" +version = "0.4.1" +description = "Accelerated property cache" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "propcache-0.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c2d1fa3201efaf55d730400d945b5b3ab6e672e100ba0f9a409d950ab25d7db"}, + {file = "propcache-0.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1eb2994229cc8ce7fe9b3db88f5465f5fd8651672840b2e426b88cdb1a30aac8"}, + {file = "propcache-0.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:66c1f011f45a3b33d7bcb22daed4b29c0c9e2224758b6be00686731e1b46f925"}, + {file = "propcache-0.4.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9a52009f2adffe195d0b605c25ec929d26b36ef986ba85244891dee3b294df21"}, + {file = "propcache-0.4.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5d4e2366a9c7b837555cf02fb9be2e3167d333aff716332ef1b7c3a142ec40c5"}, + {file = "propcache-0.4.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:9d2b6caef873b4f09e26ea7e33d65f42b944837563a47a94719cc3544319a0db"}, + {file = "propcache-0.4.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2b16ec437a8c8a965ecf95739448dd938b5c7f56e67ea009f4300d8df05f32b7"}, + {file = "propcache-0.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:296f4c8ed03ca7476813fe666c9ea97869a8d7aec972618671b33a38a5182ef4"}, + {file = "propcache-0.4.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:1f0978529a418ebd1f49dad413a2b68af33f85d5c5ca5c6ca2a3bed375a7ac60"}, + {file = "propcache-0.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fd138803047fb4c062b1c1dd95462f5209456bfab55c734458f15d11da288f8f"}, + {file = "propcache-0.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8c9b3cbe4584636d72ff556d9036e0c9317fa27b3ac1f0f558e7e84d1c9c5900"}, + {file = "propcache-0.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f93243fdc5657247533273ac4f86ae106cc6445a0efacb9a1bfe982fcfefd90c"}, + {file = "propcache-0.4.1-cp310-cp310-win32.whl", hash = "sha256:a0ee98db9c5f80785b266eb805016e36058ac72c51a064040f2bc43b61101cdb"}, + {file = "propcache-0.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:1cdb7988c4e5ac7f6d175a28a9aa0c94cb6f2ebe52756a3c0cda98d2809a9e37"}, + {file = "propcache-0.4.1-cp310-cp310-win_arm64.whl", hash = "sha256:d82ad62b19645419fe79dd63b3f9253e15b30e955c0170e5cebc350c1844e581"}, + {file = "propcache-0.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:60a8fda9644b7dfd5dece8c61d8a85e271cb958075bfc4e01083c148b61a7caf"}, + {file = "propcache-0.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c30b53e7e6bda1d547cabb47c825f3843a0a1a42b0496087bb58d8fedf9f41b5"}, + {file = "propcache-0.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6918ecbd897443087a3b7cd978d56546a812517dcaaca51b49526720571fa93e"}, + {file = "propcache-0.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3d902a36df4e5989763425a8ab9e98cd8ad5c52c823b34ee7ef307fd50582566"}, + {file = "propcache-0.4.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a9695397f85973bb40427dedddf70d8dc4a44b22f1650dd4af9eedf443d45165"}, + {file = "propcache-0.4.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2bb07ffd7eaad486576430c89f9b215f9e4be68c4866a96e97db9e97fead85dc"}, + {file = "propcache-0.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fd6f30fdcf9ae2a70abd34da54f18da086160e4d7d9251f81f3da0ff84fc5a48"}, + {file = "propcache-0.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fc38cba02d1acba4e2869eef1a57a43dfbd3d49a59bf90dda7444ec2be6a5570"}, + {file = "propcache-0.4.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:67fad6162281e80e882fb3ec355398cf72864a54069d060321f6cd0ade95fe85"}, + {file = "propcache-0.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f10207adf04d08bec185bae14d9606a1444715bc99180f9331c9c02093e1959e"}, + {file = "propcache-0.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e9b0d8d0845bbc4cfcdcbcdbf5086886bc8157aa963c31c777ceff7846c77757"}, + {file = "propcache-0.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:981333cb2f4c1896a12f4ab92a9cc8f09ea664e9b7dbdc4eff74627af3a11c0f"}, + {file = "propcache-0.4.1-cp311-cp311-win32.whl", hash = "sha256:f1d2f90aeec838a52f1c1a32fe9a619fefd5e411721a9117fbf82aea638fe8a1"}, + {file = "propcache-0.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:364426a62660f3f699949ac8c621aad6977be7126c5807ce48c0aeb8e7333ea6"}, + {file = "propcache-0.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:e53f3a38d3510c11953f3e6a33f205c6d1b001129f972805ca9b42fc308bc239"}, + {file = "propcache-0.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e153e9cd40cc8945138822807139367f256f89c6810c2634a4f6902b52d3b4e2"}, + {file = "propcache-0.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cd547953428f7abb73c5ad82cbb32109566204260d98e41e5dfdc682eb7f8403"}, + {file = "propcache-0.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f048da1b4f243fc44f205dfd320933a951b8d89e0afd4c7cacc762a8b9165207"}, + {file = "propcache-0.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ec17c65562a827bba85e3872ead335f95405ea1674860d96483a02f5c698fa72"}, + {file = "propcache-0.4.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:405aac25c6394ef275dee4c709be43745d36674b223ba4eb7144bf4d691b7367"}, + {file = "propcache-0.4.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0013cb6f8dde4b2a2f66903b8ba740bdfe378c943c4377a200551ceb27f379e4"}, + {file = "propcache-0.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:15932ab57837c3368b024473a525e25d316d8353016e7cc0e5ba9eb343fbb1cf"}, + {file = "propcache-0.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:031dce78b9dc099f4c29785d9cf5577a3faf9ebf74ecbd3c856a7b92768c3df3"}, + {file = "propcache-0.4.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ab08df6c9a035bee56e31af99be621526bd237bea9f32def431c656b29e41778"}, + {file = "propcache-0.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4d7af63f9f93fe593afbf104c21b3b15868efb2c21d07d8732c0c4287e66b6a6"}, + {file = "propcache-0.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:cfc27c945f422e8b5071b6e93169679e4eb5bf73bbcbf1ba3ae3a83d2f78ebd9"}, + {file = "propcache-0.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:35c3277624a080cc6ec6f847cbbbb5b49affa3598c4535a0a4682a697aaa5c75"}, + {file = "propcache-0.4.1-cp312-cp312-win32.whl", hash = "sha256:671538c2262dadb5ba6395e26c1731e1d52534bfe9ae56d0b5573ce539266aa8"}, + {file = "propcache-0.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:cb2d222e72399fcf5890d1d5cc1060857b9b236adff2792ff48ca2dfd46c81db"}, + {file = "propcache-0.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:204483131fb222bdaaeeea9f9e6c6ed0cac32731f75dfc1d4a567fc1926477c1"}, + {file = "propcache-0.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:43eedf29202c08550aac1d14e0ee619b0430aaef78f85864c1a892294fbc28cf"}, + {file = "propcache-0.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d62cdfcfd89ccb8de04e0eda998535c406bf5e060ffd56be6c586cbcc05b3311"}, + {file = "propcache-0.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cae65ad55793da34db5f54e4029b89d3b9b9490d8abe1b4c7ab5d4b8ec7ebf74"}, + {file = "propcache-0.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:333ddb9031d2704a301ee3e506dc46b1fe5f294ec198ed6435ad5b6a085facfe"}, + {file = "propcache-0.4.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:fd0858c20f078a32cf55f7e81473d96dcf3b93fd2ccdb3d40fdf54b8573df3af"}, + {file = "propcache-0.4.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:678ae89ebc632c5c204c794f8dab2837c5f159aeb59e6ed0539500400577298c"}, + {file = "propcache-0.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d472aeb4fbf9865e0c6d622d7f4d54a4e101a89715d8904282bb5f9a2f476c3f"}, + {file = "propcache-0.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4d3df5fa7e36b3225954fba85589da77a0fe6a53e3976de39caf04a0db4c36f1"}, + {file = "propcache-0.4.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:ee17f18d2498f2673e432faaa71698032b0127ebf23ae5974eeaf806c279df24"}, + {file = "propcache-0.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:580e97762b950f993ae618e167e7be9256b8353c2dcd8b99ec100eb50f5286aa"}, + {file = "propcache-0.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:501d20b891688eb8e7aa903021f0b72d5a55db40ffaab27edefd1027caaafa61"}, + {file = "propcache-0.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a0bd56e5b100aef69bd8562b74b46254e7c8812918d3baa700c8a8009b0af66"}, + {file = "propcache-0.4.1-cp313-cp313-win32.whl", hash = "sha256:bcc9aaa5d80322bc2fb24bb7accb4a30f81e90ab8d6ba187aec0744bc302ad81"}, + {file = "propcache-0.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:381914df18634f5494334d201e98245c0596067504b9372d8cf93f4bb23e025e"}, + {file = "propcache-0.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:8873eb4460fd55333ea49b7d189749ecf6e55bf85080f11b1c4530ed3034cba1"}, + {file = "propcache-0.4.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:92d1935ee1f8d7442da9c0c4fa7ac20d07e94064184811b685f5c4fada64553b"}, + {file = "propcache-0.4.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:473c61b39e1460d386479b9b2f337da492042447c9b685f28be4f74d3529e566"}, + {file = "propcache-0.4.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:c0ef0aaafc66fbd87842a3fe3902fd889825646bc21149eafe47be6072725835"}, + {file = "propcache-0.4.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f95393b4d66bfae908c3ca8d169d5f79cd65636ae15b5e7a4f6e67af675adb0e"}, + {file = "propcache-0.4.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c07fda85708bc48578467e85099645167a955ba093be0a2dcba962195676e859"}, + {file = "propcache-0.4.1-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:af223b406d6d000830c6f65f1e6431783fc3f713ba3e6cc8c024d5ee96170a4b"}, + {file = "propcache-0.4.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a78372c932c90ee474559c5ddfffd718238e8673c340dc21fe45c5b8b54559a0"}, + {file = "propcache-0.4.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:564d9f0d4d9509e1a870c920a89b2fec951b44bf5ba7d537a9e7c1ccec2c18af"}, + {file = "propcache-0.4.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:17612831fda0138059cc5546f4d12a2aacfb9e47068c06af35c400ba58ba7393"}, + {file = "propcache-0.4.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:41a89040cb10bd345b3c1a873b2bf36413d48da1def52f268a055f7398514874"}, + {file = "propcache-0.4.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e35b88984e7fa64aacecea39236cee32dd9bd8c55f57ba8a75cf2399553f9bd7"}, + {file = "propcache-0.4.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6f8b465489f927b0df505cbe26ffbeed4d6d8a2bbc61ce90eb074ff129ef0ab1"}, + {file = "propcache-0.4.1-cp313-cp313t-win32.whl", hash = "sha256:2ad890caa1d928c7c2965b48f3a3815c853180831d0e5503d35cf00c472f4717"}, + {file = "propcache-0.4.1-cp313-cp313t-win_amd64.whl", hash = "sha256:f7ee0e597f495cf415bcbd3da3caa3bd7e816b74d0d52b8145954c5e6fd3ff37"}, + {file = "propcache-0.4.1-cp313-cp313t-win_arm64.whl", hash = "sha256:929d7cbe1f01bb7baffb33dc14eb5691c95831450a26354cd210a8155170c93a"}, + {file = "propcache-0.4.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3f7124c9d820ba5548d431afb4632301acf965db49e666aa21c305cbe8c6de12"}, + {file = "propcache-0.4.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:c0d4b719b7da33599dfe3b22d3db1ef789210a0597bc650b7cee9c77c2be8c5c"}, + {file = "propcache-0.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9f302f4783709a78240ebc311b793f123328716a60911d667e0c036bc5dcbded"}, + {file = "propcache-0.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c80ee5802e3fb9ea37938e7eecc307fb984837091d5fd262bb37238b1ae97641"}, + {file = "propcache-0.4.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ed5a841e8bb29a55fb8159ed526b26adc5bdd7e8bd7bf793ce647cb08656cdf4"}, + {file = "propcache-0.4.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:55c72fd6ea2da4c318e74ffdf93c4fe4e926051133657459131a95c846d16d44"}, + {file = "propcache-0.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8326e144341460402713f91df60ade3c999d601e7eb5ff8f6f7862d54de0610d"}, + {file = "propcache-0.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:060b16ae65bc098da7f6d25bf359f1f31f688384858204fe5d652979e0015e5b"}, + {file = "propcache-0.4.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:89eb3fa9524f7bec9de6e83cf3faed9d79bffa560672c118a96a171a6f55831e"}, + {file = "propcache-0.4.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:dee69d7015dc235f526fe80a9c90d65eb0039103fe565776250881731f06349f"}, + {file = "propcache-0.4.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:5558992a00dfd54ccbc64a32726a3357ec93825a418a401f5cc67df0ac5d9e49"}, + {file = "propcache-0.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c9b822a577f560fbd9554812526831712c1436d2c046cedee4c3796d3543b144"}, + {file = "propcache-0.4.1-cp314-cp314-win32.whl", hash = "sha256:ab4c29b49d560fe48b696cdcb127dd36e0bc2472548f3bf56cc5cb3da2b2984f"}, + {file = "propcache-0.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:5a103c3eb905fcea0ab98be99c3a9a5ab2de60228aa5aceedc614c0281cf6153"}, + {file = "propcache-0.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:74c1fb26515153e482e00177a1ad654721bf9207da8a494a0c05e797ad27b992"}, + {file = "propcache-0.4.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:824e908bce90fb2743bd6b59db36eb4f45cd350a39637c9f73b1c1ea66f5b75f"}, + {file = "propcache-0.4.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:c2b5e7db5328427c57c8e8831abda175421b709672f6cfc3d630c3b7e2146393"}, + {file = "propcache-0.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6f6ff873ed40292cd4969ef5310179afd5db59fdf055897e282485043fc80ad0"}, + {file = "propcache-0.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:49a2dc67c154db2c1463013594c458881a069fcf98940e61a0569016a583020a"}, + {file = "propcache-0.4.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:005f08e6a0529984491e37d8dbc3dd86f84bd78a8ceb5fa9a021f4c48d4984be"}, + {file = "propcache-0.4.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5c3310452e0d31390da9035c348633b43d7e7feb2e37be252be6da45abd1abcc"}, + {file = "propcache-0.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c3c70630930447f9ef1caac7728c8ad1c56bc5015338b20fed0d08ea2480b3a"}, + {file = "propcache-0.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8e57061305815dfc910a3634dcf584f08168a8836e6999983569f51a8544cd89"}, + {file = "propcache-0.4.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:521a463429ef54143092c11a77e04056dd00636f72e8c45b70aaa3140d639726"}, + {file = "propcache-0.4.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:120c964da3fdc75e3731aa392527136d4ad35868cc556fd09bb6d09172d9a367"}, + {file = "propcache-0.4.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:d8f353eb14ee3441ee844ade4277d560cdd68288838673273b978e3d6d2c8f36"}, + {file = "propcache-0.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ab2943be7c652f09638800905ee1bab2c544e537edb57d527997a24c13dc1455"}, + {file = "propcache-0.4.1-cp314-cp314t-win32.whl", hash = "sha256:05674a162469f31358c30bcaa8883cb7829fa3110bf9c0991fe27d7896c42d85"}, + {file = "propcache-0.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:990f6b3e2a27d683cb7602ed6c86f15ee6b43b1194736f9baaeb93d0016633b1"}, + {file = "propcache-0.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:ecef2343af4cc68e05131e45024ba34f6095821988a9d0a02aa7c73fcc448aa9"}, + {file = "propcache-0.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3d233076ccf9e450c8b3bc6720af226b898ef5d051a2d145f7d765e6e9f9bcff"}, + {file = "propcache-0.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:357f5bb5c377a82e105e44bd3d52ba22b616f7b9773714bff93573988ef0a5fb"}, + {file = "propcache-0.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cbc3b6dfc728105b2a57c06791eb07a94229202ea75c59db644d7d496b698cac"}, + {file = "propcache-0.4.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:182b51b421f0501952d938dc0b0eb45246a5b5153c50d42b495ad5fb7517c888"}, + {file = "propcache-0.4.1-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4b536b39c5199b96fc6245eb5fb796c497381d3942f169e44e8e392b29c9ebcc"}, + {file = "propcache-0.4.1-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:db65d2af507bbfbdcedb254a11149f894169d90488dd3e7190f7cdcb2d6cd57a"}, + {file = "propcache-0.4.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fd2dbc472da1f772a4dae4fa24be938a6c544671a912e30529984dd80400cd88"}, + {file = "propcache-0.4.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:daede9cd44e0f8bdd9e6cc9a607fc81feb80fae7a5fc6cecaff0e0bb32e42d00"}, + {file = "propcache-0.4.1-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:71b749281b816793678ae7f3d0d84bd36e694953822eaad408d682efc5ca18e0"}, + {file = "propcache-0.4.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:0002004213ee1f36cfb3f9a42b5066100c44276b9b72b4e1504cddd3d692e86e"}, + {file = "propcache-0.4.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:fe49d0a85038f36ba9e3ffafa1103e61170b28e95b16622e11be0a0ea07c6781"}, + {file = "propcache-0.4.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:99d43339c83aaf4d32bda60928231848eee470c6bda8d02599cc4cebe872d183"}, + {file = "propcache-0.4.1-cp39-cp39-win32.whl", hash = "sha256:a129e76735bc792794d5177069691c3217898b9f5cee2b2661471e52ffe13f19"}, + {file = "propcache-0.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:948dab269721ae9a87fd16c514a0a2c2a1bdb23a9a61b969b0f9d9ee2968546f"}, + {file = "propcache-0.4.1-cp39-cp39-win_arm64.whl", hash = "sha256:5fd37c406dd6dc85aa743e214cef35dc54bbdd1419baac4f6ae5e5b1a2976938"}, + {file = "propcache-0.4.1-py3-none-any.whl", hash = "sha256:af2a6052aeb6cf17d3e46ee169099044fd8224cbaf75c76a2ef596e8163e2237"}, + {file = "propcache-0.4.1.tar.gz", hash = "sha256:f48107a8c637e80362555f37ecf49abe20370e557cc4ab374f04ec4423c97c3d"}, +] + +[[package]] +name = "protobuf" +version = "6.31.1" +description = "" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "protobuf-6.31.1-cp310-abi3-win32.whl", hash = "sha256:7fa17d5a29c2e04b7d90e5e32388b8bfd0e7107cd8e616feef7ed3fa6bdab5c9"}, + {file = "protobuf-6.31.1-cp310-abi3-win_amd64.whl", hash = "sha256:426f59d2964864a1a366254fa703b8632dcec0790d8862d30034d8245e1cd447"}, + {file = "protobuf-6.31.1-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:6f1227473dc43d44ed644425268eb7c2e488ae245d51c6866d19fe158e207402"}, + {file = "protobuf-6.31.1-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:a40fc12b84c154884d7d4c4ebd675d5b3b5283e155f324049ae396b95ddebc39"}, + {file = "protobuf-6.31.1-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:4ee898bf66f7a8b0bd21bce523814e6fbd8c6add948045ce958b73af7e8878c6"}, + {file = "protobuf-6.31.1-cp39-cp39-win32.whl", hash = "sha256:0414e3aa5a5f3ff423828e1e6a6e907d6c65c1d5b7e6e975793d5590bdeecc16"}, + {file = "protobuf-6.31.1-cp39-cp39-win_amd64.whl", hash = "sha256:8764cf4587791e7564051b35524b72844f845ad0bb011704c3736cce762d8fe9"}, + {file = "protobuf-6.31.1-py3-none-any.whl", hash = "sha256:720a6c7e6b77288b85063569baae8536671b39f15cc22037ec7045658d80489e"}, + {file = "protobuf-6.31.1.tar.gz", hash = "sha256:d8cac4c982f0b957a4dc73a80e2ea24fab08e679c0de9deb835f4a12d69aca9a"}, +] + +[[package]] +name = "psycopg" +version = "3.2.10" +description = "PostgreSQL database adapter for Python" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "psycopg-3.2.10-py3-none-any.whl", hash = "sha256:ab5caf09a9ec42e314a21f5216dbcceac528e0e05142e42eea83a3b28b320ac3"}, + {file = "psycopg-3.2.10.tar.gz", hash = "sha256:0bce99269d16ed18401683a8569b2c5abd94f72f8364856d56c0389bcd50972a"}, +] + +[package.dependencies] +typing-extensions = {version = ">=4.6", markers = "python_version < \"3.13\""} +tzdata = {version = "*", markers = "sys_platform == \"win32\""} + +[package.extras] +binary = ["psycopg-binary (==3.2.10)"] +c = ["psycopg-c (==3.2.10)"] +dev = ["ast-comments (>=1.1.2)", "black (>=24.1.0)", "codespell (>=2.2)", "dnspython (>=2.1)", "flake8 (>=4.0)", "isort-psycopg", "isort[colors] (>=6.0)", "mypy (>=1.14)", "pre-commit (>=4.0.1)", "types-setuptools (>=57.4)", "types-shapely (>=2.0)", "wheel (>=0.37)"] +docs = ["Sphinx (>=5.0)", "furo (==2022.6.21)", "sphinx-autobuild (>=2021.3.14)", "sphinx-autodoc-typehints (>=1.12)"] +pool = ["psycopg-pool"] +test = ["anyio (>=4.0)", "mypy (>=1.14)", "pproxy (>=2.7)", "pytest (>=6.2.5)", "pytest-cov (>=3.0)", "pytest-randomly (>=3.5)"] + +[[package]] +name = "pyasn1" +version = "0.6.1" +description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629"}, + {file = "pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034"}, +] + +[[package]] +name = "pyasn1-modules" +version = "0.4.2" +description = "A collection of ASN.1-based protocols modules" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "pyasn1_modules-0.4.2-py3-none-any.whl", hash = "sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a"}, + {file = "pyasn1_modules-0.4.2.tar.gz", hash = "sha256:677091de870a80aae844b1ca6134f54652fa2c8c5a52aa396440ac3106e941e6"}, +] + +[package.dependencies] +pyasn1 = ">=0.6.1,<0.7.0" + +[[package]] +name = "pycparser" +version = "2.23" +description = "C parser in Python" +optional = false +python-versions = ">=3.8" +groups = ["main"] +markers = "implementation_name != \"PyPy\"" +files = [ + {file = "pycparser-2.23-py3-none-any.whl", hash = "sha256:e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934"}, + {file = "pycparser-2.23.tar.gz", hash = "sha256:78816d4f24add8f10a06d6f05b4d424ad9e96cfebf68a4ddc99c65c0720d00c2"}, +] + +[[package]] +name = "pycryptodome" +version = "3.23.0" +description = "Cryptographic library for Python" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +groups = ["main"] +files = [ + {file = "pycryptodome-3.23.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a176b79c49af27d7f6c12e4b178b0824626f40a7b9fed08f712291b6d54bf566"}, + {file = "pycryptodome-3.23.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:573a0b3017e06f2cffd27d92ef22e46aa3be87a2d317a5abf7cc0e84e321bd75"}, + {file = "pycryptodome-3.23.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:63dad881b99ca653302b2c7191998dd677226222a3f2ea79999aa51ce695f720"}, + {file = "pycryptodome-3.23.0-cp27-cp27m-win32.whl", hash = "sha256:b34e8e11d97889df57166eda1e1ddd7676da5fcd4d71a0062a760e75060514b4"}, + {file = "pycryptodome-3.23.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:7ac1080a8da569bde76c0a104589c4f414b8ba296c0b3738cf39a466a9fb1818"}, + {file = "pycryptodome-3.23.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:6fe8258e2039eceb74dfec66b3672552b6b7d2c235b2dfecc05d16b8921649a8"}, + {file = "pycryptodome-3.23.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:0011f7f00cdb74879142011f95133274741778abba114ceca229adbf8e62c3e4"}, + {file = "pycryptodome-3.23.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:90460fc9e088ce095f9ee8356722d4f10f86e5be06e2354230a9880b9c549aae"}, + {file = "pycryptodome-3.23.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4764e64b269fc83b00f682c47443c2e6e85b18273712b98aa43bcb77f8570477"}, + {file = "pycryptodome-3.23.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb8f24adb74984aa0e5d07a2368ad95276cf38051fe2dc6605cbcf482e04f2a7"}, + {file = "pycryptodome-3.23.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d97618c9c6684a97ef7637ba43bdf6663a2e2e77efe0f863cce97a76af396446"}, + {file = "pycryptodome-3.23.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9a53a4fe5cb075075d515797d6ce2f56772ea7e6a1e5e4b96cf78a14bac3d265"}, + {file = "pycryptodome-3.23.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:763d1d74f56f031788e5d307029caef067febf890cd1f8bf61183ae142f1a77b"}, + {file = "pycryptodome-3.23.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:954af0e2bd7cea83ce72243b14e4fb518b18f0c1649b576d114973e2073b273d"}, + {file = "pycryptodome-3.23.0-cp313-cp313t-win32.whl", hash = "sha256:257bb3572c63ad8ba40b89f6fc9d63a2a628e9f9708d31ee26560925ebe0210a"}, + {file = "pycryptodome-3.23.0-cp313-cp313t-win_amd64.whl", hash = "sha256:6501790c5b62a29fcb227bd6b62012181d886a767ce9ed03b303d1f22eb5c625"}, + {file = "pycryptodome-3.23.0-cp313-cp313t-win_arm64.whl", hash = "sha256:9a77627a330ab23ca43b48b130e202582e91cc69619947840ea4d2d1be21eb39"}, + {file = "pycryptodome-3.23.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:187058ab80b3281b1de11c2e6842a357a1f71b42cb1e15bce373f3d238135c27"}, + {file = "pycryptodome-3.23.0-cp37-abi3-macosx_10_9_x86_64.whl", hash = "sha256:cfb5cd445280c5b0a4e6187a7ce8de5a07b5f3f897f235caa11f1f435f182843"}, + {file = "pycryptodome-3.23.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67bd81fcbe34f43ad9422ee8fd4843c8e7198dd88dd3d40e6de42ee65fbe1490"}, + {file = "pycryptodome-3.23.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8987bd3307a39bc03df5c8e0e3d8be0c4c3518b7f044b0f4c15d1aa78f52575"}, + {file = "pycryptodome-3.23.0-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aa0698f65e5b570426fc31b8162ed4603b0c2841cbb9088e2b01641e3065915b"}, + {file = "pycryptodome-3.23.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:53ecbafc2b55353edcebd64bf5da94a2a2cdf5090a6915bcca6eca6cc452585a"}, + {file = "pycryptodome-3.23.0-cp37-abi3-musllinux_1_2_i686.whl", hash = "sha256:156df9667ad9f2ad26255926524e1c136d6664b741547deb0a86a9acf5ea631f"}, + {file = "pycryptodome-3.23.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:dea827b4d55ee390dc89b2afe5927d4308a8b538ae91d9c6f7a5090f397af1aa"}, + {file = "pycryptodome-3.23.0-cp37-abi3-win32.whl", hash = "sha256:507dbead45474b62b2bbe318eb1c4c8ee641077532067fec9c1aa82c31f84886"}, + {file = "pycryptodome-3.23.0-cp37-abi3-win_amd64.whl", hash = "sha256:c75b52aacc6c0c260f204cbdd834f76edc9fb0d8e0da9fbf8352ef58202564e2"}, + {file = "pycryptodome-3.23.0-cp37-abi3-win_arm64.whl", hash = "sha256:11eeeb6917903876f134b56ba11abe95c0b0fd5e3330def218083c7d98bbcb3c"}, + {file = "pycryptodome-3.23.0-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:350ebc1eba1da729b35ab7627a833a1a355ee4e852d8ba0447fafe7b14504d56"}, + {file = "pycryptodome-3.23.0-pp27-pypy_73-win32.whl", hash = "sha256:93837e379a3e5fd2bb00302a47aee9fdf7940d83595be3915752c74033d17ca7"}, + {file = "pycryptodome-3.23.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ddb95b49df036ddd264a0ad246d1be5b672000f12d6961ea2c267083a5e19379"}, + {file = "pycryptodome-3.23.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e95564beb8782abfd9e431c974e14563a794a4944c29d6d3b7b5ea042110b4"}, + {file = "pycryptodome-3.23.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14e15c081e912c4b0d75632acd8382dfce45b258667aa3c67caf7a4d4c13f630"}, + {file = "pycryptodome-3.23.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7fc76bf273353dc7e5207d172b83f569540fc9a28d63171061c42e361d22353"}, + {file = "pycryptodome-3.23.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:45c69ad715ca1a94f778215a11e66b7ff989d792a4d63b68dc586a1da1392ff5"}, + {file = "pycryptodome-3.23.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:865d83c906b0fc6a59b510deceee656b6bc1c4fa0d82176e2b77e97a420a996a"}, + {file = "pycryptodome-3.23.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89d4d56153efc4d81defe8b65fd0821ef8b2d5ddf8ed19df31ba2f00872b8002"}, + {file = "pycryptodome-3.23.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3f2d0aaf8080bda0587d58fc9fe4766e012441e2eed4269a77de6aea981c8be"}, + {file = "pycryptodome-3.23.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:64093fc334c1eccfd3933c134c4457c34eaca235eeae49d69449dc4728079339"}, + {file = "pycryptodome-3.23.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:ce64e84a962b63a47a592690bdc16a7eaf709d2c2697ababf24a0def566899a6"}, + {file = "pycryptodome-3.23.0.tar.gz", hash = "sha256:447700a657182d60338bab09fdb27518f8856aecd80ae4c6bdddb67ff5da44ef"}, +] + +[[package]] +name = "pydantic" +version = "2.12.0" +description = "Data validation using Python type hints" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "pydantic-2.12.0-py3-none-any.whl", hash = "sha256:f6a1da352d42790537e95e83a8bdfb91c7efbae63ffd0b86fa823899e807116f"}, + {file = "pydantic-2.12.0.tar.gz", hash = "sha256:c1a077e6270dbfb37bfd8b498b3981e2bb18f68103720e51fa6c306a5a9af563"}, +] + +[package.dependencies] +annotated-types = ">=0.6.0" +pydantic-core = "2.41.1" +typing-extensions = ">=4.14.1" +typing-inspection = ">=0.4.2" + +[package.extras] +email = ["email-validator (>=2.0.0)"] +timezone = ["tzdata"] + +[[package]] +name = "pydantic-ai" +version = "0.7.2" +description = "Agent Framework / shim to use Pydantic with LLMs" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "pydantic_ai-0.7.2-py3-none-any.whl", hash = "sha256:a6e5d0994aa87385a05fdfdad7fda1fd14576f623635e4000883c4c7856eba13"}, + {file = "pydantic_ai-0.7.2.tar.gz", hash = "sha256:d215c323741d47ff13c6b48aa75aedfb8b6b5f9da553af709675c3078a4be4fc"}, +] + +[package.dependencies] +pydantic-ai-slim = {version = "0.7.2", extras = ["ag-ui", "anthropic", "bedrock", "cli", "cohere", "evals", "google", "groq", "huggingface", "mcp", "mistral", "openai", "retries", "temporal", "vertexai"]} + +[package.extras] +a2a = ["fasta2a (>=0.4.1)"] +examples = ["pydantic-ai-examples (==0.7.2)"] +logfire = ["logfire (>=3.14.1)"] + +[[package]] +name = "pydantic-ai-slim" +version = "0.7.2" +description = "Agent Framework / shim to use Pydantic with LLMs, slim package" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "pydantic_ai_slim-0.7.2-py3-none-any.whl", hash = "sha256:f5749d63bf4c2deac45371874df30d1d76a1572ce9467f6505926ecb835da583"}, + {file = "pydantic_ai_slim-0.7.2.tar.gz", hash = "sha256:636ca32c8928048ba1173963aab6b7eb33b71174bbc371ad3f2096fee4c48dfe"}, +] + +[package.dependencies] +ag-ui-protocol = {version = ">=0.1.8", optional = true, markers = "extra == \"ag-ui\""} +anthropic = {version = ">=0.61.0", optional = true, markers = "extra == \"anthropic\""} +argcomplete = {version = ">=3.5.0", optional = true, markers = "extra == \"cli\""} +boto3 = {version = ">=1.39.0", optional = true, markers = "extra == \"bedrock\""} +cohere = {version = ">=5.16.0", optional = true, markers = "platform_system != \"Emscripten\" and extra == \"cohere\""} +eval-type-backport = ">=0.2.0" +google-auth = {version = ">=2.36.0", optional = true, markers = "extra == \"vertexai\""} +google-genai = {version = ">=1.28.0", optional = true, markers = "extra == \"google\""} +griffe = ">=1.3.2" +groq = {version = ">=0.25.0", optional = true, markers = "extra == \"groq\""} +httpx = ">=0.27" +huggingface-hub = {version = ">=0.33.5", extras = ["inference"], optional = true, markers = "extra == \"huggingface\""} +mcp = {version = ">=1.10.0", optional = true, markers = "python_version >= \"3.10\" and extra == \"mcp\""} +mistralai = {version = ">=1.9.2", optional = true, markers = "extra == \"mistral\""} +openai = {version = ">=1.99.9", optional = true, markers = "extra == \"openai\""} +opentelemetry-api = ">=1.28.0" +prompt-toolkit = {version = ">=3", optional = true, markers = "extra == \"cli\""} +pydantic = ">=2.10" +pydantic-evals = {version = "0.7.2", optional = true, markers = "extra == \"evals\""} +pydantic-graph = "0.7.2" +requests = {version = ">=2.32.2", optional = true, markers = "extra == \"vertexai\""} +rich = {version = ">=13", optional = true, markers = "extra == \"cli\""} +starlette = {version = ">=0.45.3", optional = true, markers = "extra == \"ag-ui\""} +temporalio = {version = ">=1.15.0", optional = true, markers = "extra == \"temporal\""} +tenacity = {version = ">=8.2.3", optional = true, markers = "extra == \"retries\""} +typing-inspection = ">=0.4.0" + +[package.extras] +a2a = ["fasta2a (>=0.4.1)"] +ag-ui = ["ag-ui-protocol (>=0.1.8)", "starlette (>=0.45.3)"] +anthropic = ["anthropic (>=0.61.0)"] +bedrock = ["boto3 (>=1.39.0)"] +cli = ["argcomplete (>=3.5.0)", "prompt-toolkit (>=3)", "rich (>=13)"] +cohere = ["cohere (>=5.16.0)"] +duckduckgo = ["ddgs (>=9.0.0)"] +evals = ["pydantic-evals (==0.7.2)"] +google = ["google-genai (>=1.28.0)"] +groq = ["groq (>=0.25.0)"] +huggingface = ["huggingface-hub[inference] (>=0.33.5)"] +logfire = ["logfire (>=3.14.1)"] +mcp = ["mcp (>=1.10.0)"] +mistral = ["mistralai (>=1.9.2)"] +openai = ["openai (>=1.99.9)"] +retries = ["tenacity (>=8.2.3)"] +tavily = ["tavily-python (>=0.5.0)"] +temporal = ["temporalio (>=1.15.0)"] +vertexai = ["google-auth (>=2.36.0)", "requests (>=2.32.2)"] + +[[package]] +name = "pydantic-core" +version = "2.41.1" +description = "Core functionality for Pydantic validation and serialization" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "pydantic_core-2.41.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:e63036298322e9aea1c8b7c0a6c1204d615dbf6ec0668ce5b83ff27f07404a61"}, + {file = "pydantic_core-2.41.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:241299ca91fc77ef64f11ed909d2d9220a01834e8e6f8de61275c4dd16b7c936"}, + {file = "pydantic_core-2.41.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ab7e594a2a5c24ab8013a7dc8cfe5f2260e80e490685814122081705c2cf2b0"}, + {file = "pydantic_core-2.41.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b054ef1a78519cb934b58e9c90c09e93b837c935dcd907b891f2b265b129eb6e"}, + {file = "pydantic_core-2.41.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f2ab7d10d0ab2ed6da54c757233eb0f48ebfb4f86e9b88ccecb3f92bbd61a538"}, + {file = "pydantic_core-2.41.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2757606b7948bb853a27e4040820306eaa0ccb9e8f9f8a0fa40cb674e170f350"}, + {file = "pydantic_core-2.41.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cec0e75eb61f606bad0a32f2be87507087514e26e8c73db6cbdb8371ccd27917"}, + {file = "pydantic_core-2.41.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0234236514f44a5bf552105cfe2543a12f48203397d9d0f866affa569345a5b5"}, + {file = "pydantic_core-2.41.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:1b974e41adfbb4ebb0f65fc4ca951347b17463d60893ba7d5f7b9bb087c83897"}, + {file = "pydantic_core-2.41.1-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:248dafb3204136113c383e91a4d815269f51562b6659b756cf3df14eefc7d0bb"}, + {file = "pydantic_core-2.41.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:678f9d76a91d6bcedd7568bbf6beb77ae8447f85d1aeebaab7e2f0829cfc3a13"}, + {file = "pydantic_core-2.41.1-cp310-cp310-win32.whl", hash = "sha256:dff5bee1d21ee58277900692a641925d2dddfde65182c972569b1a276d2ac8fb"}, + {file = "pydantic_core-2.41.1-cp310-cp310-win_amd64.whl", hash = "sha256:5042da12e5d97d215f91567110fdfa2e2595a25f17c19b9ff024f31c34f9b53e"}, + {file = "pydantic_core-2.41.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4f276a6134fe1fc1daa692642a3eaa2b7b858599c49a7610816388f5e37566a1"}, + {file = "pydantic_core-2.41.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:07588570a805296ece009c59d9a679dc08fab72fb337365afb4f3a14cfbfc176"}, + {file = "pydantic_core-2.41.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28527e4b53400cd60ffbd9812ccb2b5135d042129716d71afd7e45bf42b855c0"}, + {file = "pydantic_core-2.41.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:46a1c935c9228bad738c8a41de06478770927baedf581d172494ab36a6b96575"}, + {file = "pydantic_core-2.41.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:447ddf56e2b7d28d200d3e9eafa936fe40485744b5a824b67039937580b3cb20"}, + {file = "pydantic_core-2.41.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:63892ead40c1160ac860b5debcc95c95c5a0035e543a8b5a4eac70dd22e995f4"}, + {file = "pydantic_core-2.41.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f4a9543ca355e6df8fbe9c83e9faab707701e9103ae857ecb40f1c0cf8b0e94d"}, + {file = "pydantic_core-2.41.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f2611bdb694116c31e551ed82e20e39a90bea9b7ad9e54aaf2d045ad621aa7a1"}, + {file = "pydantic_core-2.41.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fecc130893a9b5f7bfe230be1bb8c61fe66a19db8ab704f808cb25a82aad0bc9"}, + {file = "pydantic_core-2.41.1-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:1e2df5f8344c99b6ea5219f00fdc8950b8e6f2c422fbc1cc122ec8641fac85a1"}, + {file = "pydantic_core-2.41.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:35291331e9d8ed94c257bab6be1cb3a380b5eee570a2784bffc055e18040a2ea"}, + {file = "pydantic_core-2.41.1-cp311-cp311-win32.whl", hash = "sha256:2876a095292668d753f1a868c4a57c4ac9f6acbd8edda8debe4218d5848cf42f"}, + {file = "pydantic_core-2.41.1-cp311-cp311-win_amd64.whl", hash = "sha256:b92d6c628e9a338846a28dfe3fcdc1a3279388624597898b105e078cdfc59298"}, + {file = "pydantic_core-2.41.1-cp311-cp311-win_arm64.whl", hash = "sha256:7d82ae99409eb69d507a89835488fb657faa03ff9968a9379567b0d2e2e56bc5"}, + {file = "pydantic_core-2.41.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:db2f82c0ccbce8f021ad304ce35cbe02aa2f95f215cac388eed542b03b4d5eb4"}, + {file = "pydantic_core-2.41.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:47694a31c710ced9205d5f1e7e8af3ca57cbb8a503d98cb9e33e27c97a501601"}, + {file = "pydantic_core-2.41.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:93e9decce94daf47baf9e9d392f5f2557e783085f7c5e522011545d9d6858e00"}, + {file = "pydantic_core-2.41.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ab0adafdf2b89c8b84f847780a119437a0931eca469f7b44d356f2b426dd9741"}, + {file = "pydantic_core-2.41.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5da98cc81873f39fd56882e1569c4677940fbc12bce6213fad1ead784192d7c8"}, + {file = "pydantic_core-2.41.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:209910e88afb01fd0fd403947b809ba8dba0e08a095e1f703294fda0a8fdca51"}, + {file = "pydantic_core-2.41.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:365109d1165d78d98e33c5bfd815a9b5d7d070f578caefaabcc5771825b4ecb5"}, + {file = "pydantic_core-2.41.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:706abf21e60a2857acdb09502bc853ee5bce732955e7b723b10311114f033115"}, + {file = "pydantic_core-2.41.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:bf0bd5417acf7f6a7ec3b53f2109f587be176cb35f9cf016da87e6017437a72d"}, + {file = "pydantic_core-2.41.1-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:2e71b1c6ceb9c78424ae9f63a07292fb769fb890a4e7efca5554c47f33a60ea5"}, + {file = "pydantic_core-2.41.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:80745b9770b4a38c25015b517451c817799bfb9d6499b0d13d8227ec941cb513"}, + {file = "pydantic_core-2.41.1-cp312-cp312-win32.whl", hash = "sha256:83b64d70520e7890453f1aa21d66fda44e7b35f1cfea95adf7b4289a51e2b479"}, + {file = "pydantic_core-2.41.1-cp312-cp312-win_amd64.whl", hash = "sha256:377defd66ee2003748ee93c52bcef2d14fde48fe28a0b156f88c3dbf9bc49a50"}, + {file = "pydantic_core-2.41.1-cp312-cp312-win_arm64.whl", hash = "sha256:c95caff279d49c1d6cdfe2996e6c2ad712571d3b9caaa209a404426c326c4bde"}, + {file = "pydantic_core-2.41.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:70e790fce5f05204ef4403159857bfcd587779da78627b0babb3654f75361ebf"}, + {file = "pydantic_core-2.41.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9cebf1ca35f10930612d60bd0f78adfacee824c30a880e3534ba02c207cceceb"}, + {file = "pydantic_core-2.41.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:170406a37a5bc82c22c3274616bf6f17cc7df9c4a0a0a50449e559cb755db669"}, + {file = "pydantic_core-2.41.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:12d4257fc9187a0ccd41b8b327d6a4e57281ab75e11dda66a9148ef2e1fb712f"}, + {file = "pydantic_core-2.41.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a75a33b4db105dd1c8d57839e17ee12db8d5ad18209e792fa325dbb4baeb00f4"}, + {file = "pydantic_core-2.41.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:08a589f850803a74e0fcb16a72081cafb0d72a3cdda500106942b07e76b7bf62"}, + {file = "pydantic_core-2.41.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a97939d6ea44763c456bd8a617ceada2c9b96bb5b8ab3dfa0d0827df7619014"}, + {file = "pydantic_core-2.41.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d2ae423c65c556f09569524b80ffd11babff61f33055ef9773d7c9fabc11ed8d"}, + {file = "pydantic_core-2.41.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:4dc703015fbf8764d6a8001c327a87f1823b7328d40b47ce6000c65918ad2b4f"}, + {file = "pydantic_core-2.41.1-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:968e4ffdfd35698a5fe659e5e44c508b53664870a8e61c8f9d24d3d145d30257"}, + {file = "pydantic_core-2.41.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:fff2b76c8e172d34771cd4d4f0ade08072385310f214f823b5a6ad4006890d32"}, + {file = "pydantic_core-2.41.1-cp313-cp313-win32.whl", hash = "sha256:a38a5263185407ceb599f2f035faf4589d57e73c7146d64f10577f6449e8171d"}, + {file = "pydantic_core-2.41.1-cp313-cp313-win_amd64.whl", hash = "sha256:b42ae7fd6760782c975897e1fdc810f483b021b32245b0105d40f6e7a3803e4b"}, + {file = "pydantic_core-2.41.1-cp313-cp313-win_arm64.whl", hash = "sha256:ad4111acc63b7384e205c27a2f15e23ac0ee21a9d77ad6f2e9cb516ec90965fb"}, + {file = "pydantic_core-2.41.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:440d0df7415b50084a4ba9d870480c16c5f67c0d1d4d5119e3f70925533a0edc"}, + {file = "pydantic_core-2.41.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:71eaa38d342099405dae6484216dcf1e8e4b0bebd9b44a4e08c9b43db6a2ab67"}, + {file = "pydantic_core-2.41.1-cp313-cp313t-win_amd64.whl", hash = "sha256:555ecf7e50f1161d3f693bc49f23c82cf6cdeafc71fa37a06120772a09a38795"}, + {file = "pydantic_core-2.41.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:05226894a26f6f27e1deb735d7308f74ef5fa3a6de3e0135bb66cdcaee88f64b"}, + {file = "pydantic_core-2.41.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:85ff7911c6c3e2fd8d3779c50925f6406d770ea58ea6dde9c230d35b52b16b4a"}, + {file = "pydantic_core-2.41.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47f1f642a205687d59b52dc1a9a607f45e588f5a2e9eeae05edd80c7a8c47674"}, + {file = "pydantic_core-2.41.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:df11c24e138876ace5ec6043e5cae925e34cf38af1a1b3d63589e8f7b5f5cdc4"}, + {file = "pydantic_core-2.41.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7f0bf7f5c8f7bf345c527e8a0d72d6b26eda99c1227b0c34e7e59e181260de31"}, + {file = "pydantic_core-2.41.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:82b887a711d341c2c47352375d73b029418f55b20bd7815446d175a70effa706"}, + {file = "pydantic_core-2.41.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b5f1d5d6bbba484bdf220c72d8ecd0be460f4bd4c5e534a541bb2cd57589fb8b"}, + {file = "pydantic_core-2.41.1-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2bf1917385ebe0f968dc5c6ab1375886d56992b93ddfe6bf52bff575d03662be"}, + {file = "pydantic_core-2.41.1-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:4f94f3ab188f44b9a73f7295663f3ecb8f2e2dd03a69c8f2ead50d37785ecb04"}, + {file = "pydantic_core-2.41.1-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:3925446673641d37c30bd84a9d597e49f72eacee8b43322c8999fa17d5ae5bc4"}, + {file = "pydantic_core-2.41.1-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:49bd51cc27adb980c7b97357ae036ce9b3c4d0bb406e84fbe16fb2d368b602a8"}, + {file = "pydantic_core-2.41.1-cp314-cp314-win32.whl", hash = "sha256:a31ca0cd0e4d12ea0df0077df2d487fc3eb9d7f96bbb13c3c5b88dcc21d05159"}, + {file = "pydantic_core-2.41.1-cp314-cp314-win_amd64.whl", hash = "sha256:1b5c4374a152e10a22175d7790e644fbd8ff58418890e07e2073ff9d4414efae"}, + {file = "pydantic_core-2.41.1-cp314-cp314-win_arm64.whl", hash = "sha256:4fee76d757639b493eb600fba668f1e17475af34c17dd61db7a47e824d464ca9"}, + {file = "pydantic_core-2.41.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f9b9c968cfe5cd576fdd7361f47f27adeb120517e637d1b189eea1c3ece573f4"}, + {file = "pydantic_core-2.41.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1ebc7ab67b856384aba09ed74e3e977dded40e693de18a4f197c67d0d4e6d8e"}, + {file = "pydantic_core-2.41.1-cp314-cp314t-win_amd64.whl", hash = "sha256:8ae0dc57b62a762985bc7fbf636be3412394acc0ddb4ade07fe104230f1b9762"}, + {file = "pydantic_core-2.41.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:10ce489cf09a4956a1549af839b983edc59b0f60e1b068c21b10154e58f54f80"}, + {file = "pydantic_core-2.41.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ff548c908caffd9455fd1342366bcf8a1ec8a3fca42f35c7fc60883d6a901074"}, + {file = "pydantic_core-2.41.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d43bf082025082bda13be89a5f876cc2386b7727c7b322be2d2b706a45cea8e"}, + {file = "pydantic_core-2.41.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:666aee751faf1c6864b2db795775dd67b61fdcf646abefa309ed1da039a97209"}, + {file = "pydantic_core-2.41.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b83aaeff0d7bde852c32e856f3ee410842ebc08bc55c510771d87dcd1c01e1ed"}, + {file = "pydantic_core-2.41.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:055c7931b0329cb8acde20cdde6d9c2cbc2a02a0a8e54a792cddd91e2ea92c65"}, + {file = "pydantic_core-2.41.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:530bbb1347e3e5ca13a91ac087c4971d7da09630ef8febd27a20a10800c2d06d"}, + {file = "pydantic_core-2.41.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:65a0ea16cfea7bfa9e43604c8bd726e63a3788b61c384c37664b55209fcb1d74"}, + {file = "pydantic_core-2.41.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:8fa93fadff794c6d15c345c560513b160197342275c6d104cc879f932b978afc"}, + {file = "pydantic_core-2.41.1-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:c8a1af9ac51969a494c6a82b563abae6859dc082d3b999e8fa7ba5ee1b05e8e8"}, + {file = "pydantic_core-2.41.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:30edab28829703f876897c9471a857e43d847b8799c3c9e2fbce644724b50aa4"}, + {file = "pydantic_core-2.41.1-cp39-cp39-win32.whl", hash = "sha256:84d0ff869f98be2e93efdf1ae31e5a15f0926d22af8677d51676e373abbfe57a"}, + {file = "pydantic_core-2.41.1-cp39-cp39-win_amd64.whl", hash = "sha256:b5674314987cdde5a5511b029fa5fb1556b3d147a367e01dd583b19cfa8e35df"}, + {file = "pydantic_core-2.41.1-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:68f2251559b8efa99041bb63571ec7cdd2d715ba74cc82b3bc9eff824ebc8bf0"}, + {file = "pydantic_core-2.41.1-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:c7bc140c596097cb53b30546ca257dbe3f19282283190b1b5142928e5d5d3a20"}, + {file = "pydantic_core-2.41.1-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2896510fce8f4725ec518f8b9d7f015a00db249d2fd40788f442af303480063d"}, + {file = "pydantic_core-2.41.1-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ced20e62cfa0f496ba68fa5d6c7ee71114ea67e2a5da3114d6450d7f4683572a"}, + {file = "pydantic_core-2.41.1-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:b04fa9ed049461a7398138c604b00550bc89e3e1151d84b81ad6dc93e39c4c06"}, + {file = "pydantic_core-2.41.1-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:b3b7d9cfbfdc43c80a16638c6dc2768e3956e73031fca64e8e1a3ae744d1faeb"}, + {file = "pydantic_core-2.41.1-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eec83fc6abef04c7f9bec616e2d76ee9a6a4ae2a359b10c21d0f680e24a247ca"}, + {file = "pydantic_core-2.41.1-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6771a2d9f83c4038dfad5970a3eef215940682b2175e32bcc817bdc639019b28"}, + {file = "pydantic_core-2.41.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:fabcbdb12de6eada8d6e9a759097adb3c15440fafc675b3e94ae5c9cb8d678a0"}, + {file = "pydantic_core-2.41.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:80e97ccfaf0aaf67d55de5085b0ed0d994f57747d9d03f2de5cc9847ca737b08"}, + {file = "pydantic_core-2.41.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34df1fe8fea5d332484a763702e8b6a54048a9d4fe6ccf41e34a128238e01f52"}, + {file = "pydantic_core-2.41.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:421b5595f845842fc093f7250e24ee395f54ca62d494fdde96f43ecf9228ae01"}, + {file = "pydantic_core-2.41.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:dce8b22663c134583aaad24827863306a933f576c79da450be3984924e2031d1"}, + {file = "pydantic_core-2.41.1-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:300a9c162fea9906cc5c103893ca2602afd84f0ec90d3be36f4cc360125d22e1"}, + {file = "pydantic_core-2.41.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e019167628f6e6161ae7ab9fb70f6d076a0bf0d55aa9b20833f86a320c70dd65"}, + {file = "pydantic_core-2.41.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:13ab9cc2de6f9d4ab645a050ae5aee61a2424ac4d3a16ba23d4c2027705e0301"}, + {file = "pydantic_core-2.41.1-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:af2385d3f98243fb733862f806c5bb9122e5fba05b373e3af40e3c82d711cef1"}, + {file = "pydantic_core-2.41.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:6550617a0c2115be56f90c31a5370261d8ce9dbf051c3ed53b51172dd34da696"}, + {file = "pydantic_core-2.41.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc17b6ecf4983d298686014c92ebc955a9f9baf9f57dad4065e7906e7bee6222"}, + {file = "pydantic_core-2.41.1-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:42ae9352cf211f08b04ea110563d6b1e415878eea5b4c70f6bdb17dca3b932d2"}, + {file = "pydantic_core-2.41.1-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:e82947de92068b0a21681a13dd2102387197092fbe7defcfb8453e0913866506"}, + {file = "pydantic_core-2.41.1-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:e244c37d5471c9acdcd282890c6c4c83747b77238bfa19429b8473586c907656"}, + {file = "pydantic_core-2.41.1-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:1e798b4b304a995110d41ec93653e57975620ccb2842ba9420037985e7d7284e"}, + {file = "pydantic_core-2.41.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:f1fc716c0eb1663c59699b024428ad5ec2bcc6b928527b8fe28de6cb89f47efb"}, + {file = "pydantic_core-2.41.1.tar.gz", hash = "sha256:1ad375859a6d8c356b7704ec0f547a58e82ee80bb41baa811ad710e124bc8f2f"}, +] + +[package.dependencies] +typing-extensions = ">=4.14.1" + +[[package]] +name = "pydantic-evals" +version = "0.7.2" +description = "Framework for evaluating stochastic code execution, especially code making use of LLMs" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "pydantic_evals-0.7.2-py3-none-any.whl", hash = "sha256:c7497d89659c35fbcaefbeb6f457ae09d62e36e161c4b25a462808178b7cfa92"}, + {file = "pydantic_evals-0.7.2.tar.gz", hash = "sha256:0cf7adee67b8a12ea0b41e5162c7256ae0f6a237acb1eea161a74ed6cf61615a"}, +] + +[package.dependencies] +anyio = ">=0" +logfire-api = ">=3.14.1" +pydantic = ">=2.10" +pydantic-ai-slim = "0.7.2" +pyyaml = ">=6.0.2" +rich = ">=13.9.4" + +[package.extras] +logfire = ["logfire (>=3.14.1)"] + +[[package]] +name = "pydantic-graph" +version = "0.7.2" +description = "Graph and state machine library" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "pydantic_graph-0.7.2-py3-none-any.whl", hash = "sha256:b6189500a465ce1bce4bbc65ac5871149af8e0f81a15d54540d3dfc0cc9b2502"}, + {file = "pydantic_graph-0.7.2.tar.gz", hash = "sha256:f90e4ec6f02b899bf6f88cc026dafa119ea5041ab4c62ba81497717c003a946e"}, +] + +[package.dependencies] +httpx = ">=0.27" +logfire-api = ">=3.14.1" +pydantic = ">=2.10" +typing-inspection = ">=0.4.0" + +[[package]] +name = "pydantic-settings" +version = "2.11.0" +description = "Settings management using Pydantic" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "pydantic_settings-2.11.0-py3-none-any.whl", hash = "sha256:fe2cea3413b9530d10f3a5875adffb17ada5c1e1bab0b2885546d7310415207c"}, + {file = "pydantic_settings-2.11.0.tar.gz", hash = "sha256:d0e87a1c7d33593beb7194adb8470fc426e95ba02af83a0f23474a04c9a08180"}, +] + +[package.dependencies] +pydantic = ">=2.7.0" +python-dotenv = ">=0.21.0" +typing-inspection = ">=0.4.0" + +[package.extras] +aws-secrets-manager = ["boto3 (>=1.35.0)", "boto3-stubs[secretsmanager]"] +azure-key-vault = ["azure-identity (>=1.16.0)", "azure-keyvault-secrets (>=4.8.0)"] +gcp-secret-manager = ["google-cloud-secret-manager (>=2.23.1)"] +toml = ["tomli (>=2.0.1)"] +yaml = ["pyyaml (>=6.0.1)"] + +[[package]] +name = "pygments" +version = "2.19.2" +description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=3.8" +groups = ["main", "dev"] +files = [ + {file = "pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b"}, + {file = "pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887"}, +] + +[package.extras] +windows-terminal = ["colorama (>=0.4.6)"] + +[[package]] +name = "pytest" +version = "8.4.2" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79"}, + {file = "pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01"}, +] + +[package.dependencies] +colorama = {version = ">=0.4", markers = "sys_platform == \"win32\""} +iniconfig = ">=1" +packaging = ">=20" +pluggy = ">=1.5,<2" +pygments = ">=2.7.2" + +[package.extras] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "pytest-asyncio" +version = "1.2.0" +description = "Pytest support for asyncio" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "pytest_asyncio-1.2.0-py3-none-any.whl", hash = "sha256:8e17ae5e46d8e7efe51ab6494dd2010f4ca8dae51652aa3c8d55acf50bfb2e99"}, + {file = "pytest_asyncio-1.2.0.tar.gz", hash = "sha256:c609a64a2a8768462d0c99811ddb8bd2583c33fd33cf7f21af1c142e824ffb57"}, +] + +[package.dependencies] +pytest = ">=8.2,<9" +typing-extensions = {version = ">=4.12", markers = "python_version < \"3.13\""} + +[package.extras] +docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1)"] +testing = ["coverage (>=6.2)", "hypothesis (>=5.7.1)"] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +description = "Extensions to the standard Python datetime module" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +groups = ["main"] +files = [ + {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, + {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, +] + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "python-dotenv" +version = "1.1.1" +description = "Read key-value pairs from a .env file and set them as environment variables" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "python_dotenv-1.1.1-py3-none-any.whl", hash = "sha256:31f23644fe2602f88ff55e1f5c79ba497e01224ee7737937930c448e4d0e24dc"}, + {file = "python_dotenv-1.1.1.tar.gz", hash = "sha256:a8a6399716257f45be6a007360200409fce5cda2661e3dec71d23dc15f6189ab"}, +] + +[package.extras] +cli = ["click (>=5.0)"] + +[[package]] +name = "python-multipart" +version = "0.0.20" +description = "A streaming multipart parser for Python" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "python_multipart-0.0.20-py3-none-any.whl", hash = "sha256:8a62d3a8335e06589fe01f2a3e178cdcc632f3fbe0d492ad9ee0ec35aab1f104"}, + {file = "python_multipart-0.0.20.tar.gz", hash = "sha256:8dd0cab45b8e23064ae09147625994d090fa46f5b0d1e13af944c331a7fa9d13"}, +] + +[[package]] +name = "pythonnet" +version = "3.0.5" +description = ".NET and Mono integration for Python" +optional = false +python-versions = "<3.14,>=3.7" +groups = ["main"] +files = [ + {file = "pythonnet-3.0.5-py3-none-any.whl", hash = "sha256:f6702d694d5d5b163c9f3f5cc34e0bed8d6857150237fae411fefb883a656d20"}, + {file = "pythonnet-3.0.5.tar.gz", hash = "sha256:48e43ca463941b3608b32b4e236db92d8d40db4c58a75ace902985f76dac21cf"}, +] + +[package.dependencies] +clr_loader = ">=0.2.7,<0.3.0" + +[[package]] +name = "pywin32" +version = "311" +description = "Python for Window Extensions" +optional = false +python-versions = "*" +groups = ["main"] +markers = "sys_platform == \"win32\"" +files = [ + {file = "pywin32-311-cp310-cp310-win32.whl", hash = "sha256:d03ff496d2a0cd4a5893504789d4a15399133fe82517455e78bad62efbb7f0a3"}, + {file = "pywin32-311-cp310-cp310-win_amd64.whl", hash = "sha256:797c2772017851984b97180b0bebe4b620bb86328e8a884bb626156295a63b3b"}, + {file = "pywin32-311-cp310-cp310-win_arm64.whl", hash = "sha256:0502d1facf1fed4839a9a51ccbcc63d952cf318f78ffc00a7e78528ac27d7a2b"}, + {file = "pywin32-311-cp311-cp311-win32.whl", hash = "sha256:184eb5e436dea364dcd3d2316d577d625c0351bf237c4e9a5fabbcfa5a58b151"}, + {file = "pywin32-311-cp311-cp311-win_amd64.whl", hash = "sha256:3ce80b34b22b17ccbd937a6e78e7225d80c52f5ab9940fe0506a1a16f3dab503"}, + {file = "pywin32-311-cp311-cp311-win_arm64.whl", hash = "sha256:a733f1388e1a842abb67ffa8e7aad0e70ac519e09b0f6a784e65a136ec7cefd2"}, + {file = "pywin32-311-cp312-cp312-win32.whl", hash = "sha256:750ec6e621af2b948540032557b10a2d43b0cee2ae9758c54154d711cc852d31"}, + {file = "pywin32-311-cp312-cp312-win_amd64.whl", hash = "sha256:b8c095edad5c211ff31c05223658e71bf7116daa0ecf3ad85f3201ea3190d067"}, + {file = "pywin32-311-cp312-cp312-win_arm64.whl", hash = "sha256:e286f46a9a39c4a18b319c28f59b61de793654af2f395c102b4f819e584b5852"}, + {file = "pywin32-311-cp313-cp313-win32.whl", hash = "sha256:f95ba5a847cba10dd8c4d8fefa9f2a6cf283b8b88ed6178fa8a6c1ab16054d0d"}, + {file = "pywin32-311-cp313-cp313-win_amd64.whl", hash = "sha256:718a38f7e5b058e76aee1c56ddd06908116d35147e133427e59a3983f703a20d"}, + {file = "pywin32-311-cp313-cp313-win_arm64.whl", hash = "sha256:7b4075d959648406202d92a2310cb990fea19b535c7f4a78d3f5e10b926eeb8a"}, + {file = "pywin32-311-cp314-cp314-win32.whl", hash = "sha256:b7a2c10b93f8986666d0c803ee19b5990885872a7de910fc460f9b0c2fbf92ee"}, + {file = "pywin32-311-cp314-cp314-win_amd64.whl", hash = "sha256:3aca44c046bd2ed8c90de9cb8427f581c479e594e99b5c0bb19b29c10fd6cb87"}, + {file = "pywin32-311-cp314-cp314-win_arm64.whl", hash = "sha256:a508e2d9025764a8270f93111a970e1d0fbfc33f4153b388bb649b7eec4f9b42"}, + {file = "pywin32-311-cp38-cp38-win32.whl", hash = "sha256:6c6f2969607b5023b0d9ce2541f8d2cbb01c4f46bc87456017cf63b73f1e2d8c"}, + {file = "pywin32-311-cp38-cp38-win_amd64.whl", hash = "sha256:c8015b09fb9a5e188f83b7b04de91ddca4658cee2ae6f3bc483f0b21a77ef6cd"}, + {file = "pywin32-311-cp39-cp39-win32.whl", hash = "sha256:aba8f82d551a942cb20d4a83413ccbac30790b50efb89a75e4f586ac0bb8056b"}, + {file = "pywin32-311-cp39-cp39-win_amd64.whl", hash = "sha256:e0c4cfb0621281fe40387df582097fd796e80430597cb9944f0ae70447bacd91"}, + {file = "pywin32-311-cp39-cp39-win_arm64.whl", hash = "sha256:62ea666235135fee79bb154e695f3ff67370afefd71bd7fea7512fc70ef31e3d"}, +] + +[[package]] +name = "pyyaml" +version = "6.0.3" +description = "YAML parser and emitter for Python" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "PyYAML-6.0.3-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:c2514fceb77bc5e7a2f7adfaa1feb2fb311607c9cb518dbc378688ec73d8292f"}, + {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c57bb8c96f6d1808c030b1687b9b5fb476abaa47f0db9c0101f5e9f394e97f4"}, + {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:efd7b85f94a6f21e4932043973a7ba2613b059c4a000551892ac9f1d11f5baf3"}, + {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22ba7cfcad58ef3ecddc7ed1db3409af68d023b7f940da23c6c2a1890976eda6"}, + {file = "PyYAML-6.0.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:6344df0d5755a2c9a276d4473ae6b90647e216ab4757f8426893b5dd2ac3f369"}, + {file = "PyYAML-6.0.3-cp38-cp38-win32.whl", hash = "sha256:3ff07ec89bae51176c0549bc4c63aa6202991da2d9a6129d7aef7f1407d3f295"}, + {file = "PyYAML-6.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:5cf4e27da7e3fbed4d6c3d8e797387aaad68102272f8f9752883bc32d61cb87b"}, + {file = "pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b"}, + {file = "pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956"}, + {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8"}, + {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198"}, + {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b"}, + {file = "pyyaml-6.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0"}, + {file = "pyyaml-6.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69"}, + {file = "pyyaml-6.0.3-cp310-cp310-win32.whl", hash = "sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e"}, + {file = "pyyaml-6.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c"}, + {file = "pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e"}, + {file = "pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824"}, + {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c"}, + {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00"}, + {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d"}, + {file = "pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a"}, + {file = "pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4"}, + {file = "pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b"}, + {file = "pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf"}, + {file = "pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196"}, + {file = "pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0"}, + {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28"}, + {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c"}, + {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc"}, + {file = "pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e"}, + {file = "pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea"}, + {file = "pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5"}, + {file = "pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b"}, + {file = "pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd"}, + {file = "pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8"}, + {file = "pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1"}, + {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c"}, + {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5"}, + {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6"}, + {file = "pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6"}, + {file = "pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be"}, + {file = "pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26"}, + {file = "pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c"}, + {file = "pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb"}, + {file = "pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac"}, + {file = "pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310"}, + {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7"}, + {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788"}, + {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5"}, + {file = "pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764"}, + {file = "pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35"}, + {file = "pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac"}, + {file = "pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3"}, + {file = "pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3"}, + {file = "pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba"}, + {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c"}, + {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702"}, + {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c"}, + {file = "pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065"}, + {file = "pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65"}, + {file = "pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9"}, + {file = "pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b"}, + {file = "pyyaml-6.0.3-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:b865addae83924361678b652338317d1bd7e79b1f4596f96b96c77a5a34b34da"}, + {file = "pyyaml-6.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c3355370a2c156cffb25e876646f149d5d68f5e0a3ce86a5084dd0b64a994917"}, + {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3c5677e12444c15717b902a5798264fa7909e41153cdf9ef7ad571b704a63dd9"}, + {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5ed875a24292240029e4483f9d4a4b8a1ae08843b9c54f43fcc11e404532a8a5"}, + {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0150219816b6a1fa26fb4699fb7daa9caf09eb1999f3b70fb6e786805e80375a"}, + {file = "pyyaml-6.0.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:fa160448684b4e94d80416c0fa4aac48967a969efe22931448d853ada8baf926"}, + {file = "pyyaml-6.0.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:27c0abcb4a5dac13684a37f76e701e054692a9b2d3064b70f5e4eb54810553d7"}, + {file = "pyyaml-6.0.3-cp39-cp39-win32.whl", hash = "sha256:1ebe39cb5fc479422b83de611d14e2c0d3bb2a18bbcb01f229ab3cfbd8fee7a0"}, + {file = "pyyaml-6.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:2e71d11abed7344e42a8849600193d15b6def118602c4c176f748e4583246007"}, + {file = "pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f"}, +] + +[[package]] +name = "referencing" +version = "0.36.2" +description = "JSON Referencing + Python" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "referencing-0.36.2-py3-none-any.whl", hash = "sha256:e8699adbbf8b5c7de96d8ffa0eb5c158b3beafce084968e2ea8bb08c6794dcd0"}, + {file = "referencing-0.36.2.tar.gz", hash = "sha256:df2e89862cd09deabbdba16944cc3f10feb6b3e6f18e902f7cc25609a34775aa"}, +] + +[package.dependencies] +attrs = ">=22.2.0" +rpds-py = ">=0.7.0" +typing-extensions = {version = ">=4.4.0", markers = "python_version < \"3.13\""} + +[[package]] +name = "requests" +version = "2.32.5" +description = "Python HTTP for Humans." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6"}, + {file = "requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf"}, +] + +[package.dependencies] +certifi = ">=2017.4.17" +charset_normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<3" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "requests-toolbelt" +version = "1.0.0" +description = "A utility belt for advanced users of python-requests" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +groups = ["main"] +files = [ + {file = "requests-toolbelt-1.0.0.tar.gz", hash = "sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6"}, + {file = "requests_toolbelt-1.0.0-py2.py3-none-any.whl", hash = "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06"}, +] + +[package.dependencies] +requests = ">=2.0.1,<3.0.0" + +[[package]] +name = "rich" +version = "14.2.0" +description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" +optional = false +python-versions = ">=3.8.0" +groups = ["main"] +files = [ + {file = "rich-14.2.0-py3-none-any.whl", hash = "sha256:76bc51fe2e57d2b1be1f96c524b890b816e334ab4c1e45888799bfaab0021edd"}, + {file = "rich-14.2.0.tar.gz", hash = "sha256:73ff50c7c0c1c77c8243079283f4edb376f0f6442433aecb8ce7e6d0b92d1fe4"}, +] + +[package.dependencies] +markdown-it-py = ">=2.2.0" +pygments = ">=2.13.0,<3.0.0" + +[package.extras] +jupyter = ["ipywidgets (>=7.5.1,<9)"] + +[[package]] +name = "rpds-py" +version = "0.27.1" +description = "Python bindings to Rust's persistent data structures (rpds)" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "rpds_py-0.27.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:68afeec26d42ab3b47e541b272166a0b4400313946871cba3ed3a4fc0cab1cef"}, + {file = "rpds_py-0.27.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:74e5b2f7bb6fa38b1b10546d27acbacf2a022a8b5543efb06cfebc72a59c85be"}, + {file = "rpds_py-0.27.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9024de74731df54546fab0bfbcdb49fae19159ecaecfc8f37c18d2c7e2c0bd61"}, + {file = "rpds_py-0.27.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:31d3ebadefcd73b73928ed0b2fd696f7fefda8629229f81929ac9c1854d0cffb"}, + {file = "rpds_py-0.27.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b2e7f8f169d775dd9092a1743768d771f1d1300453ddfe6325ae3ab5332b4657"}, + {file = "rpds_py-0.27.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d905d16f77eb6ab2e324e09bfa277b4c8e5e6b8a78a3e7ff8f3cdf773b4c013"}, + {file = "rpds_py-0.27.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50c946f048209e6362e22576baea09193809f87687a95a8db24e5fbdb307b93a"}, + {file = "rpds_py-0.27.1-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:3deab27804d65cd8289eb814c2c0e807c4b9d9916c9225e363cb0cf875eb67c1"}, + {file = "rpds_py-0.27.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8b61097f7488de4be8244c89915da8ed212832ccf1e7c7753a25a394bf9b1f10"}, + {file = "rpds_py-0.27.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8a3f29aba6e2d7d90528d3c792555a93497fe6538aa65eb675b44505be747808"}, + {file = "rpds_py-0.27.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:dd6cd0485b7d347304067153a6dc1d73f7d4fd995a396ef32a24d24b8ac63ac8"}, + {file = "rpds_py-0.27.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:6f4461bf931108c9fa226ffb0e257c1b18dc2d44cd72b125bec50ee0ab1248a9"}, + {file = "rpds_py-0.27.1-cp310-cp310-win32.whl", hash = "sha256:ee5422d7fb21f6a00c1901bf6559c49fee13a5159d0288320737bbf6585bd3e4"}, + {file = "rpds_py-0.27.1-cp310-cp310-win_amd64.whl", hash = "sha256:3e039aabf6d5f83c745d5f9a0a381d031e9ed871967c0a5c38d201aca41f3ba1"}, + {file = "rpds_py-0.27.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:be898f271f851f68b318872ce6ebebbc62f303b654e43bf72683dbdc25b7c881"}, + {file = "rpds_py-0.27.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:62ac3d4e3e07b58ee0ddecd71d6ce3b1637de2d373501412df395a0ec5f9beb5"}, + {file = "rpds_py-0.27.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4708c5c0ceb2d034f9991623631d3d23cb16e65c83736ea020cdbe28d57c0a0e"}, + {file = "rpds_py-0.27.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:abfa1171a9952d2e0002aba2ad3780820b00cc3d9c98c6630f2e93271501f66c"}, + {file = "rpds_py-0.27.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4b507d19f817ebaca79574b16eb2ae412e5c0835542c93fe9983f1e432aca195"}, + {file = "rpds_py-0.27.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:168b025f8fd8d8d10957405f3fdcef3dc20f5982d398f90851f4abc58c566c52"}, + {file = "rpds_py-0.27.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb56c6210ef77caa58e16e8c17d35c63fe3f5b60fd9ba9d424470c3400bcf9ed"}, + {file = "rpds_py-0.27.1-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:d252f2d8ca0195faa707f8eb9368955760880b2b42a8ee16d382bf5dd807f89a"}, + {file = "rpds_py-0.27.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6e5e54da1e74b91dbc7996b56640f79b195d5925c2b78efaa8c5d53e1d88edde"}, + {file = "rpds_py-0.27.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ffce0481cc6e95e5b3f0a47ee17ffbd234399e6d532f394c8dce320c3b089c21"}, + {file = "rpds_py-0.27.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:a205fdfe55c90c2cd8e540ca9ceba65cbe6629b443bc05db1f590a3db8189ff9"}, + {file = "rpds_py-0.27.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:689fb5200a749db0415b092972e8eba85847c23885c8543a8b0f5c009b1a5948"}, + {file = "rpds_py-0.27.1-cp311-cp311-win32.whl", hash = "sha256:3182af66048c00a075010bc7f4860f33913528a4b6fc09094a6e7598e462fe39"}, + {file = "rpds_py-0.27.1-cp311-cp311-win_amd64.whl", hash = "sha256:b4938466c6b257b2f5c4ff98acd8128ec36b5059e5c8f8372d79316b1c36bb15"}, + {file = "rpds_py-0.27.1-cp311-cp311-win_arm64.whl", hash = "sha256:2f57af9b4d0793e53266ee4325535a31ba48e2f875da81a9177c9926dfa60746"}, + {file = "rpds_py-0.27.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:ae2775c1973e3c30316892737b91f9283f9908e3cc7625b9331271eaaed7dc90"}, + {file = "rpds_py-0.27.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2643400120f55c8a96f7c9d858f7be0c88d383cd4653ae2cf0d0c88f668073e5"}, + {file = "rpds_py-0.27.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:16323f674c089b0360674a4abd28d5042947d54ba620f72514d69be4ff64845e"}, + {file = "rpds_py-0.27.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9a1f4814b65eacac94a00fc9a526e3fdafd78e439469644032032d0d63de4881"}, + {file = "rpds_py-0.27.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ba32c16b064267b22f1850a34051121d423b6f7338a12b9459550eb2096e7ec"}, + {file = "rpds_py-0.27.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5c20f33fd10485b80f65e800bbe5f6785af510b9f4056c5a3c612ebc83ba6cb"}, + {file = "rpds_py-0.27.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:466bfe65bd932da36ff279ddd92de56b042f2266d752719beb97b08526268ec5"}, + {file = "rpds_py-0.27.1-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:41e532bbdcb57c92ba3be62c42e9f096431b4cf478da9bc3bc6ce5c38ab7ba7a"}, + {file = "rpds_py-0.27.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f149826d742b406579466283769a8ea448eed82a789af0ed17b0cd5770433444"}, + {file = "rpds_py-0.27.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:80c60cfb5310677bd67cb1e85a1e8eb52e12529545441b43e6f14d90b878775a"}, + {file = "rpds_py-0.27.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:7ee6521b9baf06085f62ba9c7a3e5becffbc32480d2f1b351559c001c38ce4c1"}, + {file = "rpds_py-0.27.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a512c8263249a9d68cac08b05dd59d2b3f2061d99b322813cbcc14c3c7421998"}, + {file = "rpds_py-0.27.1-cp312-cp312-win32.whl", hash = "sha256:819064fa048ba01b6dadc5116f3ac48610435ac9a0058bbde98e569f9e785c39"}, + {file = "rpds_py-0.27.1-cp312-cp312-win_amd64.whl", hash = "sha256:d9199717881f13c32c4046a15f024971a3b78ad4ea029e8da6b86e5aa9cf4594"}, + {file = "rpds_py-0.27.1-cp312-cp312-win_arm64.whl", hash = "sha256:33aa65b97826a0e885ef6e278fbd934e98cdcfed80b63946025f01e2f5b29502"}, + {file = "rpds_py-0.27.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:e4b9fcfbc021633863a37e92571d6f91851fa656f0180246e84cbd8b3f6b329b"}, + {file = "rpds_py-0.27.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1441811a96eadca93c517d08df75de45e5ffe68aa3089924f963c782c4b898cf"}, + {file = "rpds_py-0.27.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55266dafa22e672f5a4f65019015f90336ed31c6383bd53f5e7826d21a0e0b83"}, + {file = "rpds_py-0.27.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d78827d7ac08627ea2c8e02c9e5b41180ea5ea1f747e9db0915e3adf36b62dcf"}, + {file = "rpds_py-0.27.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae92443798a40a92dc5f0b01d8a7c93adde0c4dc965310a29ae7c64d72b9fad2"}, + {file = "rpds_py-0.27.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c46c9dd2403b66a2a3b9720ec4b74d4ab49d4fabf9f03dfdce2d42af913fe8d0"}, + {file = "rpds_py-0.27.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2efe4eb1d01b7f5f1939f4ef30ecea6c6b3521eec451fb93191bf84b2a522418"}, + {file = "rpds_py-0.27.1-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:15d3b4d83582d10c601f481eca29c3f138d44c92187d197aff663a269197c02d"}, + {file = "rpds_py-0.27.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4ed2e16abbc982a169d30d1a420274a709949e2cbdef119fe2ec9d870b42f274"}, + {file = "rpds_py-0.27.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a75f305c9b013289121ec0f1181931975df78738cdf650093e6b86d74aa7d8dd"}, + {file = "rpds_py-0.27.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:67ce7620704745881a3d4b0ada80ab4d99df390838839921f99e63c474f82cf2"}, + {file = "rpds_py-0.27.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9d992ac10eb86d9b6f369647b6a3f412fc0075cfd5d799530e84d335e440a002"}, + {file = "rpds_py-0.27.1-cp313-cp313-win32.whl", hash = "sha256:4f75e4bd8ab8db624e02c8e2fc4063021b58becdbe6df793a8111d9343aec1e3"}, + {file = "rpds_py-0.27.1-cp313-cp313-win_amd64.whl", hash = "sha256:f9025faafc62ed0b75a53e541895ca272815bec18abe2249ff6501c8f2e12b83"}, + {file = "rpds_py-0.27.1-cp313-cp313-win_arm64.whl", hash = "sha256:ed10dc32829e7d222b7d3b93136d25a406ba9788f6a7ebf6809092da1f4d279d"}, + {file = "rpds_py-0.27.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:92022bbbad0d4426e616815b16bc4127f83c9a74940e1ccf3cfe0b387aba0228"}, + {file = "rpds_py-0.27.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:47162fdab9407ec3f160805ac3e154df042e577dd53341745fc7fb3f625e6d92"}, + {file = "rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb89bec23fddc489e5d78b550a7b773557c9ab58b7946154a10a6f7a214a48b2"}, + {file = "rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e48af21883ded2b3e9eb48cb7880ad8598b31ab752ff3be6457001d78f416723"}, + {file = "rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6f5b7bd8e219ed50299e58551a410b64daafb5017d54bbe822e003856f06a802"}, + {file = "rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:08f1e20bccf73b08d12d804d6e1c22ca5530e71659e6673bce31a6bb71c1e73f"}, + {file = "rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0dc5dceeaefcc96dc192e3a80bbe1d6c410c469e97bdd47494a7d930987f18b2"}, + {file = "rpds_py-0.27.1-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:d76f9cc8665acdc0c9177043746775aa7babbf479b5520b78ae4002d889f5c21"}, + {file = "rpds_py-0.27.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:134fae0e36022edad8290a6661edf40c023562964efea0cc0ec7f5d392d2aaef"}, + {file = "rpds_py-0.27.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:eb11a4f1b2b63337cfd3b4d110af778a59aae51c81d195768e353d8b52f88081"}, + {file = "rpds_py-0.27.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:13e608ac9f50a0ed4faec0e90ece76ae33b34c0e8656e3dceb9a7db994c692cd"}, + {file = "rpds_py-0.27.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dd2135527aa40f061350c3f8f89da2644de26cd73e4de458e79606384f4f68e7"}, + {file = "rpds_py-0.27.1-cp313-cp313t-win32.whl", hash = "sha256:3020724ade63fe320a972e2ffd93b5623227e684315adce194941167fee02688"}, + {file = "rpds_py-0.27.1-cp313-cp313t-win_amd64.whl", hash = "sha256:8ee50c3e41739886606388ba3ab3ee2aae9f35fb23f833091833255a31740797"}, + {file = "rpds_py-0.27.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:acb9aafccaae278f449d9c713b64a9e68662e7799dbd5859e2c6b3c67b56d334"}, + {file = "rpds_py-0.27.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:b7fb801aa7f845ddf601c49630deeeccde7ce10065561d92729bfe81bd21fb33"}, + {file = "rpds_py-0.27.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fe0dd05afb46597b9a2e11c351e5e4283c741237e7f617ffb3252780cca9336a"}, + {file = "rpds_py-0.27.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b6dfb0e058adb12d8b1d1b25f686e94ffa65d9995a5157afe99743bf7369d62b"}, + {file = "rpds_py-0.27.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ed090ccd235f6fa8bb5861684567f0a83e04f52dfc2e5c05f2e4b1309fcf85e7"}, + {file = "rpds_py-0.27.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bf876e79763eecf3e7356f157540d6a093cef395b65514f17a356f62af6cc136"}, + {file = "rpds_py-0.27.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:12ed005216a51b1d6e2b02a7bd31885fe317e45897de81d86dcce7d74618ffff"}, + {file = "rpds_py-0.27.1-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:ee4308f409a40e50593c7e3bb8cbe0b4d4c66d1674a316324f0c2f5383b486f9"}, + {file = "rpds_py-0.27.1-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0b08d152555acf1f455154d498ca855618c1378ec810646fcd7c76416ac6dc60"}, + {file = "rpds_py-0.27.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:dce51c828941973a5684d458214d3a36fcd28da3e1875d659388f4f9f12cc33e"}, + {file = "rpds_py-0.27.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:c1476d6f29eb81aa4151c9a31219b03f1f798dc43d8af1250a870735516a1212"}, + {file = "rpds_py-0.27.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:3ce0cac322b0d69b63c9cdb895ee1b65805ec9ffad37639f291dd79467bee675"}, + {file = "rpds_py-0.27.1-cp314-cp314-win32.whl", hash = "sha256:dfbfac137d2a3d0725758cd141f878bf4329ba25e34979797c89474a89a8a3a3"}, + {file = "rpds_py-0.27.1-cp314-cp314-win_amd64.whl", hash = "sha256:a6e57b0abfe7cc513450fcf529eb486b6e4d3f8aee83e92eb5f1ef848218d456"}, + {file = "rpds_py-0.27.1-cp314-cp314-win_arm64.whl", hash = "sha256:faf8d146f3d476abfee026c4ae3bdd9ca14236ae4e4c310cbd1cf75ba33d24a3"}, + {file = "rpds_py-0.27.1-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:ba81d2b56b6d4911ce735aad0a1d4495e808b8ee4dc58715998741a26874e7c2"}, + {file = "rpds_py-0.27.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:84f7d509870098de0e864cad0102711c1e24e9b1a50ee713b65928adb22269e4"}, + {file = "rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9e960fc78fecd1100539f14132425e1d5fe44ecb9239f8f27f079962021523e"}, + {file = "rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:62f85b665cedab1a503747617393573995dac4600ff51869d69ad2f39eb5e817"}, + {file = "rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fed467af29776f6556250c9ed85ea5a4dd121ab56a5f8b206e3e7a4c551e48ec"}, + {file = "rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2729615f9d430af0ae6b36cf042cb55c0936408d543fb691e1a9e36648fd35a"}, + {file = "rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1b207d881a9aef7ba753d69c123a35d96ca7cb808056998f6b9e8747321f03b8"}, + {file = "rpds_py-0.27.1-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:639fd5efec029f99b79ae47e5d7e00ad8a773da899b6309f6786ecaf22948c48"}, + {file = "rpds_py-0.27.1-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fecc80cb2a90e28af8a9b366edacf33d7a91cbfe4c2c4544ea1246e949cfebeb"}, + {file = "rpds_py-0.27.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:42a89282d711711d0a62d6f57d81aa43a1368686c45bc1c46b7f079d55692734"}, + {file = "rpds_py-0.27.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:cf9931f14223de59551ab9d38ed18d92f14f055a5f78c1d8ad6493f735021bbb"}, + {file = "rpds_py-0.27.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f39f58a27cc6e59f432b568ed8429c7e1641324fbe38131de852cd77b2d534b0"}, + {file = "rpds_py-0.27.1-cp314-cp314t-win32.whl", hash = "sha256:d5fa0ee122dc09e23607a28e6d7b150da16c662e66409bbe85230e4c85bb528a"}, + {file = "rpds_py-0.27.1-cp314-cp314t-win_amd64.whl", hash = "sha256:6567d2bb951e21232c2f660c24cf3470bb96de56cdcb3f071a83feeaff8a2772"}, + {file = "rpds_py-0.27.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:c918c65ec2e42c2a78d19f18c553d77319119bf43aa9e2edf7fb78d624355527"}, + {file = "rpds_py-0.27.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1fea2b1a922c47c51fd07d656324531adc787e415c8b116530a1d29c0516c62d"}, + {file = "rpds_py-0.27.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbf94c58e8e0cd6b6f38d8de67acae41b3a515c26169366ab58bdca4a6883bb8"}, + {file = "rpds_py-0.27.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c2a8fed130ce946d5c585eddc7c8eeef0051f58ac80a8ee43bd17835c144c2cc"}, + {file = "rpds_py-0.27.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:037a2361db72ee98d829bc2c5b7cc55598ae0a5e0ec1823a56ea99374cfd73c1"}, + {file = "rpds_py-0.27.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5281ed1cc1d49882f9997981c88df1a22e140ab41df19071222f7e5fc4e72125"}, + {file = "rpds_py-0.27.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fd50659a069c15eef8aa3d64bbef0d69fd27bb4a50c9ab4f17f83a16cbf8905"}, + {file = "rpds_py-0.27.1-cp39-cp39-manylinux_2_31_riscv64.whl", hash = "sha256:c4b676c4ae3921649a15d28ed10025548e9b561ded473aa413af749503c6737e"}, + {file = "rpds_py-0.27.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:079bc583a26db831a985c5257797b2b5d3affb0386e7ff886256762f82113b5e"}, + {file = "rpds_py-0.27.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:4e44099bd522cba71a2c6b97f68e19f40e7d85399de899d66cdb67b32d7cb786"}, + {file = "rpds_py-0.27.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e202e6d4188e53c6661af813b46c37ca2c45e497fc558bacc1a7630ec2695aec"}, + {file = "rpds_py-0.27.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:f41f814b8eaa48768d1bb551591f6ba45f87ac76899453e8ccd41dba1289b04b"}, + {file = "rpds_py-0.27.1-cp39-cp39-win32.whl", hash = "sha256:9e71f5a087ead99563c11fdaceee83ee982fd39cf67601f4fd66cb386336ee52"}, + {file = "rpds_py-0.27.1-cp39-cp39-win_amd64.whl", hash = "sha256:71108900c9c3c8590697244b9519017a400d9ba26a36c48381b3f64743a44aab"}, + {file = "rpds_py-0.27.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:7ba22cb9693df986033b91ae1d7a979bc399237d45fccf875b76f62bb9e52ddf"}, + {file = "rpds_py-0.27.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:5b640501be9288c77738b5492b3fd3abc4ba95c50c2e41273c8a1459f08298d3"}, + {file = "rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb08b65b93e0c6dd70aac7f7890a9c0938d5ec71d5cb32d45cf844fb8ae47636"}, + {file = "rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d7ff07d696a7a38152ebdb8212ca9e5baab56656749f3d6004b34ab726b550b8"}, + {file = "rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fb7c72262deae25366e3b6c0c0ba46007967aea15d1eea746e44ddba8ec58dcc"}, + {file = "rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7b002cab05d6339716b03a4a3a2ce26737f6231d7b523f339fa061d53368c9d8"}, + {file = "rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23f6b69d1c26c4704fec01311963a41d7de3ee0570a84ebde4d544e5a1859ffc"}, + {file = "rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:530064db9146b247351f2a0250b8f00b289accea4596a033e94be2389977de71"}, + {file = "rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7b90b0496570bd6b0321724a330d8b545827c4df2034b6ddfc5f5275f55da2ad"}, + {file = "rpds_py-0.27.1-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:879b0e14a2da6a1102a3fc8af580fc1ead37e6d6692a781bd8c83da37429b5ab"}, + {file = "rpds_py-0.27.1-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:0d807710df3b5faa66c731afa162ea29717ab3be17bdc15f90f2d9f183da4059"}, + {file = "rpds_py-0.27.1-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:3adc388fc3afb6540aec081fa59e6e0d3908722771aa1e37ffe22b220a436f0b"}, + {file = "rpds_py-0.27.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c796c0c1cc68cb08b0284db4229f5af76168172670c74908fdbd4b7d7f515819"}, + {file = "rpds_py-0.27.1-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:cdfe4bb2f9fe7458b7453ad3c33e726d6d1c7c0a72960bcc23800d77384e42df"}, + {file = "rpds_py-0.27.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:8fabb8fd848a5f75a2324e4a84501ee3a5e3c78d8603f83475441866e60b94a3"}, + {file = "rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eda8719d598f2f7f3e0f885cba8646644b55a187762bec091fa14a2b819746a9"}, + {file = "rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3c64d07e95606ec402a0a1c511fe003873fa6af630bda59bac77fac8b4318ebc"}, + {file = "rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:93a2ed40de81bcff59aabebb626562d48332f3d028ca2036f1d23cbb52750be4"}, + {file = "rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:387ce8c44ae94e0ec50532d9cb0edce17311024c9794eb196b90e1058aadeb66"}, + {file = "rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aaf94f812c95b5e60ebaf8bfb1898a7d7cb9c1af5744d4a67fa47796e0465d4e"}, + {file = "rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:4848ca84d6ded9b58e474dfdbad4b8bfb450344c0551ddc8d958bf4b36aa837c"}, + {file = "rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2bde09cbcf2248b73c7c323be49b280180ff39fadcfe04e7b6f54a678d02a7cf"}, + {file = "rpds_py-0.27.1-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:94c44ee01fd21c9058f124d2d4f0c9dc7634bec93cd4b38eefc385dabe71acbf"}, + {file = "rpds_py-0.27.1-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:df8b74962e35c9249425d90144e721eed198e6555a0e22a563d29fe4486b51f6"}, + {file = "rpds_py-0.27.1-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:dc23e6820e3b40847e2f4a7726462ba0cf53089512abe9ee16318c366494c17a"}, + {file = "rpds_py-0.27.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:aa8933159edc50be265ed22b401125c9eebff3171f570258854dbce3ecd55475"}, + {file = "rpds_py-0.27.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:a50431bf02583e21bf273c71b89d710e7a710ad5e39c725b14e685610555926f"}, + {file = "rpds_py-0.27.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78af06ddc7fe5cc0e967085a9115accee665fb912c22a3f54bad70cc65b05fe6"}, + {file = "rpds_py-0.27.1-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:70d0738ef8fee13c003b100c2fbd667ec4f133468109b3472d249231108283a3"}, + {file = "rpds_py-0.27.1-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e2f6fd8a1cea5bbe599b6e78a6e5ee08db434fc8ffea51ff201c8765679698b3"}, + {file = "rpds_py-0.27.1-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8177002868d1426305bb5de1e138161c2ec9eb2d939be38291d7c431c4712df8"}, + {file = "rpds_py-0.27.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:008b839781d6c9bf3b6a8984d1d8e56f0ec46dc56df61fd669c49b58ae800400"}, + {file = "rpds_py-0.27.1-pp39-pypy39_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:a55b9132bb1ade6c734ddd2759c8dc132aa63687d259e725221f106b83a0e485"}, + {file = "rpds_py-0.27.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a46fdec0083a26415f11d5f236b79fa1291c32aaa4a17684d82f7017a1f818b1"}, + {file = "rpds_py-0.27.1-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:8a63b640a7845f2bdd232eb0d0a4a2dd939bcdd6c57e6bb134526487f3160ec5"}, + {file = "rpds_py-0.27.1-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:7e32721e5d4922deaaf963469d795d5bde6093207c52fec719bd22e5d1bedbc4"}, + {file = "rpds_py-0.27.1-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:2c426b99a068601b5f4623573df7a7c3d72e87533a2dd2253353a03e7502566c"}, + {file = "rpds_py-0.27.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:4fc9b7fe29478824361ead6e14e4f5aed570d477e06088826537e202d25fe859"}, + {file = "rpds_py-0.27.1.tar.gz", hash = "sha256:26a1c73171d10b7acccbded82bf6a586ab8203601e565badc74bbbf8bc5a10f8"}, +] + +[[package]] +name = "rsa" +version = "4.9.1" +description = "Pure-Python RSA implementation" +optional = false +python-versions = "<4,>=3.6" +groups = ["main"] +files = [ + {file = "rsa-4.9.1-py3-none-any.whl", hash = "sha256:68635866661c6836b8d39430f97a996acbd61bfa49406748ea243539fe239762"}, + {file = "rsa-4.9.1.tar.gz", hash = "sha256:e7bdbfdb5497da4c07dfd35530e1a902659db6ff241e39d9953cad06ebd0ae75"}, +] + +[package.dependencies] +pyasn1 = ">=0.1.3" + +[[package]] +name = "ruff" +version = "0.9.10" +description = "An extremely fast Python linter and code formatter, written in Rust." +optional = false +python-versions = ">=3.7" +groups = ["dev"] +files = [ + {file = "ruff-0.9.10-py3-none-linux_armv6l.whl", hash = "sha256:eb4d25532cfd9fe461acc83498361ec2e2252795b4f40b17e80692814329e42d"}, + {file = "ruff-0.9.10-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:188a6638dab1aa9bb6228a7302387b2c9954e455fb25d6b4470cb0641d16759d"}, + {file = "ruff-0.9.10-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5284dcac6b9dbc2fcb71fdfc26a217b2ca4ede6ccd57476f52a587451ebe450d"}, + {file = "ruff-0.9.10-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47678f39fa2a3da62724851107f438c8229a3470f533894b5568a39b40029c0c"}, + {file = "ruff-0.9.10-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:99713a6e2766b7a17147b309e8c915b32b07a25c9efd12ada79f217c9c778b3e"}, + {file = "ruff-0.9.10-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:524ee184d92f7c7304aa568e2db20f50c32d1d0caa235d8ddf10497566ea1a12"}, + {file = "ruff-0.9.10-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:df92aeac30af821f9acf819fc01b4afc3dfb829d2782884f8739fb52a8119a16"}, + {file = "ruff-0.9.10-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de42e4edc296f520bb84954eb992a07a0ec5a02fecb834498415908469854a52"}, + {file = "ruff-0.9.10-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d257f95b65806104b6b1ffca0ea53f4ef98454036df65b1eda3693534813ecd1"}, + {file = "ruff-0.9.10-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b60dec7201c0b10d6d11be00e8f2dbb6f40ef1828ee75ed739923799513db24c"}, + {file = "ruff-0.9.10-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:d838b60007da7a39c046fcdd317293d10b845001f38bcb55ba766c3875b01e43"}, + {file = "ruff-0.9.10-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:ccaf903108b899beb8e09a63ffae5869057ab649c1e9231c05ae354ebc62066c"}, + {file = "ruff-0.9.10-py3-none-musllinux_1_2_i686.whl", hash = "sha256:f9567d135265d46e59d62dc60c0bfad10e9a6822e231f5b24032dba5a55be6b5"}, + {file = "ruff-0.9.10-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5f202f0d93738c28a89f8ed9eaba01b7be339e5d8d642c994347eaa81c6d75b8"}, + {file = "ruff-0.9.10-py3-none-win32.whl", hash = "sha256:bfb834e87c916521ce46b1788fbb8484966e5113c02df216680102e9eb960029"}, + {file = "ruff-0.9.10-py3-none-win_amd64.whl", hash = "sha256:f2160eeef3031bf4b17df74e307d4c5fb689a6f3a26a2de3f7ef4044e3c484f1"}, + {file = "ruff-0.9.10-py3-none-win_arm64.whl", hash = "sha256:5fd804c0327a5e5ea26615550e706942f348b197d5475ff34c19733aee4b2e69"}, + {file = "ruff-0.9.10.tar.gz", hash = "sha256:9bacb735d7bada9cfb0f2c227d3658fc443d90a727b47f206fb33f52f3c0eac7"}, +] + +[[package]] +name = "s3transfer" +version = "0.14.0" +description = "An Amazon S3 Transfer Manager" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "s3transfer-0.14.0-py3-none-any.whl", hash = "sha256:ea3b790c7077558ed1f02a3072fb3cb992bbbd253392f4b6e9e8976941c7d456"}, + {file = "s3transfer-0.14.0.tar.gz", hash = "sha256:eff12264e7c8b4985074ccce27a3b38a485bb7f7422cc8046fee9be4983e4125"}, +] + +[package.dependencies] +botocore = ">=1.37.4,<2.0a.0" + +[package.extras] +crt = ["botocore[crt] (>=1.37.4,<2.0a.0)"] + +[[package]] +name = "six" +version = "1.17.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +groups = ["main"] +files = [ + {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, + {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, +] + +[[package]] +name = "sniffio" +version = "1.3.1" +description = "Sniff out which async library your code is running under" +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, + {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, +] + +[[package]] +name = "sse-starlette" +version = "3.0.2" +description = "SSE plugin for Starlette" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "sse_starlette-3.0.2-py3-none-any.whl", hash = "sha256:16b7cbfddbcd4eaca11f7b586f3b8a080f1afe952c15813455b162edea619e5a"}, + {file = "sse_starlette-3.0.2.tar.gz", hash = "sha256:ccd60b5765ebb3584d0de2d7a6e4f745672581de4f5005ab31c3a25d10b52b3a"}, +] + +[package.dependencies] +anyio = ">=4.7.0" + +[package.extras] +daphne = ["daphne (>=4.2.0)"] +examples = ["aiosqlite (>=0.21.0)", "fastapi (>=0.115.12)", "sqlalchemy[asyncio] (>=2.0.41)", "starlette (>=0.41.3)", "uvicorn (>=0.34.0)"] +granian = ["granian (>=2.3.1)"] +uvicorn = ["uvicorn (>=0.34.0)"] + +[[package]] +name = "starlette" +version = "0.46.2" +description = "The little ASGI library that shines." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "starlette-0.46.2-py3-none-any.whl", hash = "sha256:595633ce89f8ffa71a015caed34a5b2dc1c0cdb3f0f1fbd1e69339cf2abeec35"}, + {file = "starlette-0.46.2.tar.gz", hash = "sha256:7f7361f34eed179294600af672f565727419830b54b7b084efe44bb82d2fccd5"}, +] + +[package.dependencies] +anyio = ">=3.6.2,<5" + +[package.extras] +full = ["httpx (>=0.27.0,<0.29.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.18)", "pyyaml"] + +[[package]] +name = "structlog" +version = "25.4.0" +description = "Structured Logging for Python" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "structlog-25.4.0-py3-none-any.whl", hash = "sha256:fe809ff5c27e557d14e613f45ca441aabda051d119ee5a0102aaba6ce40eed2c"}, + {file = "structlog-25.4.0.tar.gz", hash = "sha256:186cd1b0a8ae762e29417095664adf1d6a31702160a46dacb7796ea82f7409e4"}, +] + +[[package]] +name = "temporalio" +version = "1.18.1" +description = "Temporal.io Python SDK" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "temporalio-1.18.1-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:748c0ec9f48aa1ab612a58fe516d9be28c1dd98194f560fd28a2ab09c6e2ca5e"}, + {file = "temporalio-1.18.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:5a789e7c483582d6d7dd49e7d2d2730d82dc94d9342fe71be76fa67afa4e6865"}, + {file = "temporalio-1.18.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9f5cf75c4b887476a2b39d022a9c44c495f5eb1668087a022bd9258d3adddf9"}, + {file = "temporalio-1.18.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f28a69394bf18b4a1c22a6a784d348e93482858c505d054570b278f0f5e13e9c"}, + {file = "temporalio-1.18.1-cp39-abi3-win_amd64.whl", hash = "sha256:552b360f9ccdac8d5fc5d19c6578c2f6f634399ccc37439c4794aa58487f7fd5"}, + {file = "temporalio-1.18.1.tar.gz", hash = "sha256:46394498f8822e61b3ce70d6735de7618f5af0501fb90f3f90f4b4f9e7816d77"}, +] + +[package.dependencies] +nexus-rpc = "1.1.0" +protobuf = ">=3.20,<7.0.0" +types-protobuf = ">=3.20" +typing-extensions = ">=4.2.0,<5" + +[package.extras] +grpc = ["grpcio (>=1.48.2,<2)"] +openai-agents = ["eval-type-backport (>=0.2.2)", "mcp (>=1.9.4,<2)", "openai-agents (>=0.3,<0.4)"] +opentelemetry = ["opentelemetry-api (>=1.11.1,<2)", "opentelemetry-sdk (>=1.11.1,<2)"] +pydantic = ["pydantic (>=2.0.0,<3)"] + +[[package]] +name = "tenacity" +version = "9.1.2" +description = "Retry code until it succeeds" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "tenacity-9.1.2-py3-none-any.whl", hash = "sha256:f77bf36710d8b73a50b2dd155c97b870017ad21afe6ab300326b0371b3b05138"}, + {file = "tenacity-9.1.2.tar.gz", hash = "sha256:1169d376c297e7de388d18b4481760d478b0e99a777cad3a9c86e556f4b697cb"}, +] + +[package.extras] +doc = ["reno", "sphinx"] +test = ["pytest", "tornado (>=4.5)", "typeguard"] + +[[package]] +name = "tokenizers" +version = "0.22.1" +description = "" +optional = false +python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system != \"Emscripten\"" +files = [ + {file = "tokenizers-0.22.1-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:59fdb013df17455e5f950b4b834a7b3ee2e0271e6378ccb33aa74d178b513c73"}, + {file = "tokenizers-0.22.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:8d4e484f7b0827021ac5f9f71d4794aaef62b979ab7608593da22b1d2e3c4edc"}, + {file = "tokenizers-0.22.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:19d2962dd28bc67c1f205ab180578a78eef89ac60ca7ef7cbe9635a46a56422a"}, + {file = "tokenizers-0.22.1-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:38201f15cdb1f8a6843e6563e6e79f4abd053394992b9bbdf5213ea3469b4ae7"}, + {file = "tokenizers-0.22.1-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d1cbe5454c9a15df1b3443c726063d930c16f047a3cc724b9e6e1a91140e5a21"}, + {file = "tokenizers-0.22.1-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e7d094ae6312d69cc2a872b54b91b309f4f6fbce871ef28eb27b52a98e4d0214"}, + {file = "tokenizers-0.22.1-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:afd7594a56656ace95cdd6df4cca2e4059d294c5cfb1679c57824b605556cb2f"}, + {file = "tokenizers-0.22.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2ef6063d7a84994129732b47e7915e8710f27f99f3a3260b8a38fc7ccd083f4"}, + {file = "tokenizers-0.22.1-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ba0a64f450b9ef412c98f6bcd2a50c6df6e2443b560024a09fa6a03189726879"}, + {file = "tokenizers-0.22.1-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:331d6d149fa9c7d632cde4490fb8bbb12337fa3a0232e77892be656464f4b446"}, + {file = "tokenizers-0.22.1-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:607989f2ea68a46cb1dfbaf3e3aabdf3f21d8748312dbeb6263d1b3b66c5010a"}, + {file = "tokenizers-0.22.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a0f307d490295717726598ef6fa4f24af9d484809223bbc253b201c740a06390"}, + {file = "tokenizers-0.22.1-cp39-abi3-win32.whl", hash = "sha256:b5120eed1442765cd90b903bb6cfef781fd8fe64e34ccaecbae4c619b7b12a82"}, + {file = "tokenizers-0.22.1-cp39-abi3-win_amd64.whl", hash = "sha256:65fd6e3fb11ca1e78a6a93602490f134d1fdeb13bcef99389d5102ea318ed138"}, + {file = "tokenizers-0.22.1.tar.gz", hash = "sha256:61de6522785310a309b3407bac22d99c4db5dba349935e99e4d15ea2226af2d9"}, +] + +[package.dependencies] +huggingface-hub = ">=0.16.4,<2.0" + +[package.extras] +dev = ["tokenizers[testing]"] +docs = ["setuptools-rust", "sphinx", "sphinx-rtd-theme"] +testing = ["black (==22.3)", "datasets", "numpy", "pytest", "pytest-asyncio", "requests", "ruff"] + +[[package]] +name = "tqdm" +version = "4.67.1" +description = "Fast, Extensible Progress Meter" +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2"}, + {file = "tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[package.extras] +dev = ["nbval", "pytest (>=6)", "pytest-asyncio (>=0.24)", "pytest-cov", "pytest-timeout"] +discord = ["requests"] +notebook = ["ipywidgets (>=6)"] +slack = ["slack-sdk"] +telegram = ["requests"] + +[[package]] +name = "types-protobuf" +version = "6.32.1.20250918" +description = "Typing stubs for protobuf" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "types_protobuf-6.32.1.20250918-py3-none-any.whl", hash = "sha256:22ba6133d142d11cc34d3788ad6dead2732368ebb0406eaa7790ea6ae46c8d0b"}, + {file = "types_protobuf-6.32.1.20250918.tar.gz", hash = "sha256:44ce0ae98475909ca72379946ab61a4435eec2a41090821e713c17e8faf5b88f"}, +] + +[[package]] +name = "types-requests" +version = "2.32.4.20250913" +description = "Typing stubs for requests" +optional = false +python-versions = ">=3.9" +groups = ["main"] +markers = "platform_system != \"Emscripten\"" +files = [ + {file = "types_requests-2.32.4.20250913-py3-none-any.whl", hash = "sha256:78c9c1fffebbe0fa487a418e0fa5252017e9c60d1a2da394077f1780f655d7e1"}, + {file = "types_requests-2.32.4.20250913.tar.gz", hash = "sha256:abd6d4f9ce3a9383f269775a9835a4c24e5cd6b9f647d64f88aa4613c33def5d"}, +] + +[package.dependencies] +urllib3 = ">=2" + +[[package]] +name = "typing-extensions" +version = "4.15.0" +description = "Backported and Experimental Type Hints for Python 3.9+" +optional = false +python-versions = ">=3.9" +groups = ["main", "dev"] +files = [ + {file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"}, + {file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"}, +] + +[[package]] +name = "typing-inspection" +version = "0.4.2" +description = "Runtime typing introspection tools" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7"}, + {file = "typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464"}, +] + +[package.dependencies] +typing-extensions = ">=4.12.0" + +[[package]] +name = "tzdata" +version = "2025.2" +description = "Provider of IANA time zone data" +optional = false +python-versions = ">=2" +groups = ["main"] +markers = "sys_platform == \"win32\"" +files = [ + {file = "tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8"}, + {file = "tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9"}, +] + +[[package]] +name = "urllib3" +version = "2.5.0" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc"}, + {file = "urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +h2 = ["h2 (>=4,<5)"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] + +[[package]] +name = "uvicorn" +version = "0.34.3" +description = "The lightning-fast ASGI server." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "uvicorn-0.34.3-py3-none-any.whl", hash = "sha256:16246631db62bdfbf069b0645177d6e8a77ba950cfedbfd093acef9444e4d885"}, + {file = "uvicorn-0.34.3.tar.gz", hash = "sha256:35919a9a979d7a59334b6b10e05d77c1d0d574c50e0fc98b8b1a0f165708b55a"}, +] + +[package.dependencies] +click = ">=7.0" +h11 = ">=0.8" + +[package.extras] +standard = ["colorama (>=0.4)", "httptools (>=0.6.3)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"] + +[[package]] +name = "wcwidth" +version = "0.2.14" +description = "Measures the displayed width of unicode strings in a terminal" +optional = false +python-versions = ">=3.6" +groups = ["main"] +files = [ + {file = "wcwidth-0.2.14-py2.py3-none-any.whl", hash = "sha256:a7bb560c8aee30f9957e5f9895805edd20602f2d7f720186dfd906e82b4982e1"}, + {file = "wcwidth-0.2.14.tar.gz", hash = "sha256:4d478375d31bc5395a3c55c40ccdf3354688364cd61c4f6adacaa9215d0b3605"}, +] + +[[package]] +name = "websockets" +version = "14.2" +description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "websockets-14.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e8179f95323b9ab1c11723e5d91a89403903f7b001828161b480a7810b334885"}, + {file = "websockets-14.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0d8c3e2cdb38f31d8bd7d9d28908005f6fa9def3324edb9bf336d7e4266fd397"}, + {file = "websockets-14.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:714a9b682deb4339d39ffa674f7b674230227d981a37d5d174a4a83e3978a610"}, + {file = "websockets-14.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2e53c72052f2596fb792a7acd9704cbc549bf70fcde8a99e899311455974ca3"}, + {file = "websockets-14.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e3fbd68850c837e57373d95c8fe352203a512b6e49eaae4c2f4088ef8cf21980"}, + {file = "websockets-14.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b27ece32f63150c268593d5fdb82819584831a83a3f5809b7521df0685cd5d8"}, + {file = "websockets-14.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4daa0faea5424d8713142b33825fff03c736f781690d90652d2c8b053345b0e7"}, + {file = "websockets-14.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:bc63cee8596a6ec84d9753fd0fcfa0452ee12f317afe4beae6b157f0070c6c7f"}, + {file = "websockets-14.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7a570862c325af2111343cc9b0257b7119b904823c675b22d4ac547163088d0d"}, + {file = "websockets-14.2-cp310-cp310-win32.whl", hash = "sha256:75862126b3d2d505e895893e3deac0a9339ce750bd27b4ba515f008b5acf832d"}, + {file = "websockets-14.2-cp310-cp310-win_amd64.whl", hash = "sha256:cc45afb9c9b2dc0852d5c8b5321759cf825f82a31bfaf506b65bf4668c96f8b2"}, + {file = "websockets-14.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3bdc8c692c866ce5fefcaf07d2b55c91d6922ac397e031ef9b774e5b9ea42166"}, + {file = "websockets-14.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c93215fac5dadc63e51bcc6dceca72e72267c11def401d6668622b47675b097f"}, + {file = "websockets-14.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1c9b6535c0e2cf8a6bf938064fb754aaceb1e6a4a51a80d884cd5db569886910"}, + {file = "websockets-14.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a52a6d7cf6938e04e9dceb949d35fbdf58ac14deea26e685ab6368e73744e4c"}, + {file = "websockets-14.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9f05702e93203a6ff5226e21d9b40c037761b2cfb637187c9802c10f58e40473"}, + {file = "websockets-14.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22441c81a6748a53bfcb98951d58d1af0661ab47a536af08920d129b4d1c3473"}, + {file = "websockets-14.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:efd9b868d78b194790e6236d9cbc46d68aba4b75b22497eb4ab64fa640c3af56"}, + {file = "websockets-14.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1a5a20d5843886d34ff8c57424cc65a1deda4375729cbca4cb6b3353f3ce4142"}, + {file = "websockets-14.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:34277a29f5303d54ec6468fb525d99c99938607bc96b8d72d675dee2b9f5bf1d"}, + {file = "websockets-14.2-cp311-cp311-win32.whl", hash = "sha256:02687db35dbc7d25fd541a602b5f8e451a238ffa033030b172ff86a93cb5dc2a"}, + {file = "websockets-14.2-cp311-cp311-win_amd64.whl", hash = "sha256:862e9967b46c07d4dcd2532e9e8e3c2825e004ffbf91a5ef9dde519ee2effb0b"}, + {file = "websockets-14.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1f20522e624d7ffbdbe259c6b6a65d73c895045f76a93719aa10cd93b3de100c"}, + {file = "websockets-14.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:647b573f7d3ada919fd60e64d533409a79dcf1ea21daeb4542d1d996519ca967"}, + {file = "websockets-14.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6af99a38e49f66be5a64b1e890208ad026cda49355661549c507152113049990"}, + {file = "websockets-14.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:091ab63dfc8cea748cc22c1db2814eadb77ccbf82829bac6b2fbe3401d548eda"}, + {file = "websockets-14.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b374e8953ad477d17e4851cdc66d83fdc2db88d9e73abf755c94510ebddceb95"}, + {file = "websockets-14.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a39d7eceeea35db85b85e1169011bb4321c32e673920ae9c1b6e0978590012a3"}, + {file = "websockets-14.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0a6f3efd47ffd0d12080594f434faf1cd2549b31e54870b8470b28cc1d3817d9"}, + {file = "websockets-14.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:065ce275e7c4ffb42cb738dd6b20726ac26ac9ad0a2a48e33ca632351a737267"}, + {file = "websockets-14.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e9d0e53530ba7b8b5e389c02282f9d2aa47581514bd6049d3a7cffe1385cf5fe"}, + {file = "websockets-14.2-cp312-cp312-win32.whl", hash = "sha256:20e6dd0984d7ca3037afcb4494e48c74ffb51e8013cac71cf607fffe11df7205"}, + {file = "websockets-14.2-cp312-cp312-win_amd64.whl", hash = "sha256:44bba1a956c2c9d268bdcdf234d5e5ff4c9b6dc3e300545cbe99af59dda9dcce"}, + {file = "websockets-14.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6f1372e511c7409a542291bce92d6c83320e02c9cf392223272287ce55bc224e"}, + {file = "websockets-14.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4da98b72009836179bb596a92297b1a61bb5a830c0e483a7d0766d45070a08ad"}, + {file = "websockets-14.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8a86a269759026d2bde227652b87be79f8a734e582debf64c9d302faa1e9f03"}, + {file = "websockets-14.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:86cf1aaeca909bf6815ea714d5c5736c8d6dd3a13770e885aafe062ecbd04f1f"}, + {file = "websockets-14.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9b0f6c3ba3b1240f602ebb3971d45b02cc12bd1845466dd783496b3b05783a5"}, + {file = "websockets-14.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:669c3e101c246aa85bc8534e495952e2ca208bd87994650b90a23d745902db9a"}, + {file = "websockets-14.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:eabdb28b972f3729348e632ab08f2a7b616c7e53d5414c12108c29972e655b20"}, + {file = "websockets-14.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2066dc4cbcc19f32c12a5a0e8cc1b7ac734e5b64ac0a325ff8353451c4b15ef2"}, + {file = "websockets-14.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ab95d357cd471df61873dadf66dd05dd4709cae001dd6342edafc8dc6382f307"}, + {file = "websockets-14.2-cp313-cp313-win32.whl", hash = "sha256:a9e72fb63e5f3feacdcf5b4ff53199ec8c18d66e325c34ee4c551ca748623bbc"}, + {file = "websockets-14.2-cp313-cp313-win_amd64.whl", hash = "sha256:b439ea828c4ba99bb3176dc8d9b933392a2413c0f6b149fdcba48393f573377f"}, + {file = "websockets-14.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7cd5706caec1686c5d233bc76243ff64b1c0dc445339bd538f30547e787c11fe"}, + {file = "websockets-14.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ec607328ce95a2f12b595f7ae4c5d71bf502212bddcea528290b35c286932b12"}, + {file = "websockets-14.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:da85651270c6bfb630136423037dd4975199e5d4114cae6d3066641adcc9d1c7"}, + {file = "websockets-14.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c3ecadc7ce90accf39903815697917643f5b7cfb73c96702318a096c00aa71f5"}, + {file = "websockets-14.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1979bee04af6a78608024bad6dfcc0cc930ce819f9e10342a29a05b5320355d0"}, + {file = "websockets-14.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2dddacad58e2614a24938a50b85969d56f88e620e3f897b7d80ac0d8a5800258"}, + {file = "websockets-14.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:89a71173caaf75fa71a09a5f614f450ba3ec84ad9fca47cb2422a860676716f0"}, + {file = "websockets-14.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:6af6a4b26eea4fc06c6818a6b962a952441e0e39548b44773502761ded8cc1d4"}, + {file = "websockets-14.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:80c8efa38957f20bba0117b48737993643204645e9ec45512579132508477cfc"}, + {file = "websockets-14.2-cp39-cp39-win32.whl", hash = "sha256:2e20c5f517e2163d76e2729104abc42639c41cf91f7b1839295be43302713661"}, + {file = "websockets-14.2-cp39-cp39-win_amd64.whl", hash = "sha256:b4c8cef610e8d7c70dea92e62b6814a8cd24fbd01d7103cc89308d2bfe1659ef"}, + {file = "websockets-14.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:d7d9cafbccba46e768be8a8ad4635fa3eae1ffac4c6e7cb4eb276ba41297ed29"}, + {file = "websockets-14.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:c76193c1c044bd1e9b3316dcc34b174bbf9664598791e6fb606d8d29000e070c"}, + {file = "websockets-14.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fd475a974d5352390baf865309fe37dec6831aafc3014ffac1eea99e84e83fc2"}, + {file = "websockets-14.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2c6c0097a41968b2e2b54ed3424739aab0b762ca92af2379f152c1aef0187e1c"}, + {file = "websockets-14.2-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d7ff794c8b36bc402f2e07c0b2ceb4a2424147ed4785ff03e2a7af03711d60a"}, + {file = "websockets-14.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:dec254fcabc7bd488dab64846f588fc5b6fe0d78f641180030f8ea27b76d72c3"}, + {file = "websockets-14.2-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:bbe03eb853e17fd5b15448328b4ec7fb2407d45fb0245036d06a3af251f8e48f"}, + {file = "websockets-14.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:a3c4aa3428b904d5404a0ed85f3644d37e2cb25996b7f096d77caeb0e96a3b42"}, + {file = "websockets-14.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:577a4cebf1ceaf0b65ffc42c54856214165fb8ceeba3935852fc33f6b0c55e7f"}, + {file = "websockets-14.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ad1c1d02357b7665e700eca43a31d52814ad9ad9b89b58118bdabc365454b574"}, + {file = "websockets-14.2-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f390024a47d904613577df83ba700bd189eedc09c57af0a904e5c39624621270"}, + {file = "websockets-14.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:3c1426c021c38cf92b453cdf371228d3430acd775edee6bac5a4d577efc72365"}, + {file = "websockets-14.2-py3-none-any.whl", hash = "sha256:7a6ceec4ea84469f15cf15807a747e9efe57e369c384fa86e022b3bea679b79b"}, + {file = "websockets-14.2.tar.gz", hash = "sha256:5059ed9c54945efb321f097084b4c7e52c246f2c869815876a69d1efc4ad6eb5"}, +] + +[[package]] +name = "wrapt" +version = "1.17.3" +description = "Module for decorators, wrappers and monkey patching." +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "wrapt-1.17.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:88bbae4d40d5a46142e70d58bf664a89b6b4befaea7b2ecc14e03cedb8e06c04"}, + {file = "wrapt-1.17.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6b13af258d6a9ad602d57d889f83b9d5543acd471eee12eb51f5b01f8eb1bc2"}, + {file = "wrapt-1.17.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd341868a4b6714a5962c1af0bd44f7c404ef78720c7de4892901e540417111c"}, + {file = "wrapt-1.17.3-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f9b2601381be482f70e5d1051a5965c25fb3625455a2bf520b5a077b22afb775"}, + {file = "wrapt-1.17.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:343e44b2a8e60e06a7e0d29c1671a0d9951f59174f3709962b5143f60a2a98bd"}, + {file = "wrapt-1.17.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:33486899acd2d7d3066156b03465b949da3fd41a5da6e394ec49d271baefcf05"}, + {file = "wrapt-1.17.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e6f40a8aa5a92f150bdb3e1c44b7e98fb7113955b2e5394122fa5532fec4b418"}, + {file = "wrapt-1.17.3-cp310-cp310-win32.whl", hash = "sha256:a36692b8491d30a8c75f1dfee65bef119d6f39ea84ee04d9f9311f83c5ad9390"}, + {file = "wrapt-1.17.3-cp310-cp310-win_amd64.whl", hash = "sha256:afd964fd43b10c12213574db492cb8f73b2f0826c8df07a68288f8f19af2ebe6"}, + {file = "wrapt-1.17.3-cp310-cp310-win_arm64.whl", hash = "sha256:af338aa93554be859173c39c85243970dc6a289fa907402289eeae7543e1ae18"}, + {file = "wrapt-1.17.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:273a736c4645e63ac582c60a56b0acb529ef07f78e08dc6bfadf6a46b19c0da7"}, + {file = "wrapt-1.17.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5531d911795e3f935a9c23eb1c8c03c211661a5060aab167065896bbf62a5f85"}, + {file = "wrapt-1.17.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0610b46293c59a3adbae3dee552b648b984176f8562ee0dba099a56cfbe4df1f"}, + {file = "wrapt-1.17.3-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b32888aad8b6e68f83a8fdccbf3165f5469702a7544472bdf41f582970ed3311"}, + {file = "wrapt-1.17.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8cccf4f81371f257440c88faed6b74f1053eef90807b77e31ca057b2db74edb1"}, + {file = "wrapt-1.17.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8a210b158a34164de8bb68b0e7780041a903d7b00c87e906fb69928bf7890d5"}, + {file = "wrapt-1.17.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:79573c24a46ce11aab457b472efd8d125e5a51da2d1d24387666cd85f54c05b2"}, + {file = "wrapt-1.17.3-cp311-cp311-win32.whl", hash = "sha256:c31eebe420a9a5d2887b13000b043ff6ca27c452a9a22fa71f35f118e8d4bf89"}, + {file = "wrapt-1.17.3-cp311-cp311-win_amd64.whl", hash = "sha256:0b1831115c97f0663cb77aa27d381237e73ad4f721391a9bfb2fe8bc25fa6e77"}, + {file = "wrapt-1.17.3-cp311-cp311-win_arm64.whl", hash = "sha256:5a7b3c1ee8265eb4c8f1b7d29943f195c00673f5ab60c192eba2d4a7eae5f46a"}, + {file = "wrapt-1.17.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ab232e7fdb44cdfbf55fc3afa31bcdb0d8980b9b95c38b6405df2acb672af0e0"}, + {file = "wrapt-1.17.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:9baa544e6acc91130e926e8c802a17f3b16fbea0fd441b5a60f5cf2cc5c3deba"}, + {file = "wrapt-1.17.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6b538e31eca1a7ea4605e44f81a48aa24c4632a277431a6ed3f328835901f4fd"}, + {file = "wrapt-1.17.3-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:042ec3bb8f319c147b1301f2393bc19dba6e176b7da446853406d041c36c7828"}, + {file = "wrapt-1.17.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3af60380ba0b7b5aeb329bc4e402acd25bd877e98b3727b0135cb5c2efdaefe9"}, + {file = "wrapt-1.17.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0b02e424deef65c9f7326d8c19220a2c9040c51dc165cddb732f16198c168396"}, + {file = "wrapt-1.17.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:74afa28374a3c3a11b3b5e5fca0ae03bef8450d6aa3ab3a1e2c30e3a75d023dc"}, + {file = "wrapt-1.17.3-cp312-cp312-win32.whl", hash = "sha256:4da9f45279fff3543c371d5ababc57a0384f70be244de7759c85a7f989cb4ebe"}, + {file = "wrapt-1.17.3-cp312-cp312-win_amd64.whl", hash = "sha256:e71d5c6ebac14875668a1e90baf2ea0ef5b7ac7918355850c0908ae82bcb297c"}, + {file = "wrapt-1.17.3-cp312-cp312-win_arm64.whl", hash = "sha256:604d076c55e2fdd4c1c03d06dc1a31b95130010517b5019db15365ec4a405fc6"}, + {file = "wrapt-1.17.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a47681378a0439215912ef542c45a783484d4dd82bac412b71e59cf9c0e1cea0"}, + {file = "wrapt-1.17.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:54a30837587c6ee3cd1a4d1c2ec5d24e77984d44e2f34547e2323ddb4e22eb77"}, + {file = "wrapt-1.17.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:16ecf15d6af39246fe33e507105d67e4b81d8f8d2c6598ff7e3ca1b8a37213f7"}, + {file = "wrapt-1.17.3-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6fd1ad24dc235e4ab88cda009e19bf347aabb975e44fd5c2fb22a3f6e4141277"}, + {file = "wrapt-1.17.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ed61b7c2d49cee3c027372df5809a59d60cf1b6c2f81ee980a091f3afed6a2d"}, + {file = "wrapt-1.17.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:423ed5420ad5f5529db9ce89eac09c8a2f97da18eb1c870237e84c5a5c2d60aa"}, + {file = "wrapt-1.17.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e01375f275f010fcbf7f643b4279896d04e571889b8a5b3f848423d91bf07050"}, + {file = "wrapt-1.17.3-cp313-cp313-win32.whl", hash = "sha256:53e5e39ff71b3fc484df8a522c933ea2b7cdd0d5d15ae82e5b23fde87d44cbd8"}, + {file = "wrapt-1.17.3-cp313-cp313-win_amd64.whl", hash = "sha256:1f0b2f40cf341ee8cc1a97d51ff50dddb9fcc73241b9143ec74b30fc4f44f6cb"}, + {file = "wrapt-1.17.3-cp313-cp313-win_arm64.whl", hash = "sha256:7425ac3c54430f5fc5e7b6f41d41e704db073309acfc09305816bc6a0b26bb16"}, + {file = "wrapt-1.17.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cf30f6e3c077c8e6a9a7809c94551203c8843e74ba0c960f4a98cd80d4665d39"}, + {file = "wrapt-1.17.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:e228514a06843cae89621384cfe3a80418f3c04aadf8a3b14e46a7be704e4235"}, + {file = "wrapt-1.17.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:5ea5eb3c0c071862997d6f3e02af1d055f381b1d25b286b9d6644b79db77657c"}, + {file = "wrapt-1.17.3-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:281262213373b6d5e4bb4353bc36d1ba4084e6d6b5d242863721ef2bf2c2930b"}, + {file = "wrapt-1.17.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dc4a8d2b25efb6681ecacad42fca8859f88092d8732b170de6a5dddd80a1c8fa"}, + {file = "wrapt-1.17.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:373342dd05b1d07d752cecbec0c41817231f29f3a89aa8b8843f7b95992ed0c7"}, + {file = "wrapt-1.17.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d40770d7c0fd5cbed9d84b2c3f2e156431a12c9a37dc6284060fb4bec0b7ffd4"}, + {file = "wrapt-1.17.3-cp314-cp314-win32.whl", hash = "sha256:fbd3c8319de8e1dc79d346929cd71d523622da527cca14e0c1d257e31c2b8b10"}, + {file = "wrapt-1.17.3-cp314-cp314-win_amd64.whl", hash = "sha256:e1a4120ae5705f673727d3253de3ed0e016f7cd78dc463db1b31e2463e1f3cf6"}, + {file = "wrapt-1.17.3-cp314-cp314-win_arm64.whl", hash = "sha256:507553480670cab08a800b9463bdb881b2edeed77dc677b0a5915e6106e91a58"}, + {file = "wrapt-1.17.3-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:ed7c635ae45cfbc1a7371f708727bf74690daedc49b4dba310590ca0bd28aa8a"}, + {file = "wrapt-1.17.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:249f88ed15503f6492a71f01442abddd73856a0032ae860de6d75ca62eed8067"}, + {file = "wrapt-1.17.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5a03a38adec8066d5a37bea22f2ba6bbf39fcdefbe2d91419ab864c3fb515454"}, + {file = "wrapt-1.17.3-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:5d4478d72eb61c36e5b446e375bbc49ed002430d17cdec3cecb36993398e1a9e"}, + {file = "wrapt-1.17.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:223db574bb38637e8230eb14b185565023ab624474df94d2af18f1cdb625216f"}, + {file = "wrapt-1.17.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e405adefb53a435f01efa7ccdec012c016b5a1d3f35459990afc39b6be4d5056"}, + {file = "wrapt-1.17.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:88547535b787a6c9ce4086917b6e1d291aa8ed914fdd3a838b3539dc95c12804"}, + {file = "wrapt-1.17.3-cp314-cp314t-win32.whl", hash = "sha256:41b1d2bc74c2cac6f9074df52b2efbef2b30bdfe5f40cb78f8ca22963bc62977"}, + {file = "wrapt-1.17.3-cp314-cp314t-win_amd64.whl", hash = "sha256:73d496de46cd2cdbdbcce4ae4bcdb4afb6a11234a1df9c085249d55166b95116"}, + {file = "wrapt-1.17.3-cp314-cp314t-win_arm64.whl", hash = "sha256:f38e60678850c42461d4202739f9bf1e3a737c7ad283638251e79cc49effb6b6"}, + {file = "wrapt-1.17.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:70d86fa5197b8947a2fa70260b48e400bf2ccacdcab97bb7de47e3d1e6312225"}, + {file = "wrapt-1.17.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:df7d30371a2accfe4013e90445f6388c570f103d61019b6b7c57e0265250072a"}, + {file = "wrapt-1.17.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:caea3e9c79d5f0d2c6d9ab96111601797ea5da8e6d0723f77eabb0d4068d2b2f"}, + {file = "wrapt-1.17.3-cp38-cp38-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:758895b01d546812d1f42204bd443b8c433c44d090248bf22689df673ccafe00"}, + {file = "wrapt-1.17.3-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:02b551d101f31694fc785e58e0720ef7d9a10c4e62c1c9358ce6f63f23e30a56"}, + {file = "wrapt-1.17.3-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:656873859b3b50eeebe6db8b1455e99d90c26ab058db8e427046dbc35c3140a5"}, + {file = "wrapt-1.17.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:a9a2203361a6e6404f80b99234fe7fb37d1fc73487b5a78dc1aa5b97201e0f22"}, + {file = "wrapt-1.17.3-cp38-cp38-win32.whl", hash = "sha256:55cbbc356c2842f39bcc553cf695932e8b30e30e797f961860afb308e6b1bb7c"}, + {file = "wrapt-1.17.3-cp38-cp38-win_amd64.whl", hash = "sha256:ad85e269fe54d506b240d2d7b9f5f2057c2aa9a2ea5b32c66f8902f768117ed2"}, + {file = "wrapt-1.17.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:30ce38e66630599e1193798285706903110d4f057aab3168a34b7fdc85569afc"}, + {file = "wrapt-1.17.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:65d1d00fbfb3ea5f20add88bbc0f815150dbbde3b026e6c24759466c8b5a9ef9"}, + {file = "wrapt-1.17.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a7c06742645f914f26c7f1fa47b8bc4c91d222f76ee20116c43d5ef0912bba2d"}, + {file = "wrapt-1.17.3-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7e18f01b0c3e4a07fe6dfdb00e29049ba17eadbc5e7609a2a3a4af83ab7d710a"}, + {file = "wrapt-1.17.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f5f51a6466667a5a356e6381d362d259125b57f059103dd9fdc8c0cf1d14139"}, + {file = "wrapt-1.17.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:59923aa12d0157f6b82d686c3fd8e1166fa8cdfb3e17b42ce3b6147ff81528df"}, + {file = "wrapt-1.17.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:46acc57b331e0b3bcb3e1ca3b421d65637915cfcd65eb783cb2f78a511193f9b"}, + {file = "wrapt-1.17.3-cp39-cp39-win32.whl", hash = "sha256:3e62d15d3cfa26e3d0788094de7b64efa75f3a53875cdbccdf78547aed547a81"}, + {file = "wrapt-1.17.3-cp39-cp39-win_amd64.whl", hash = "sha256:1f23fa283f51c890eda8e34e4937079114c74b4c81d2b2f1f1d94948f5cc3d7f"}, + {file = "wrapt-1.17.3-cp39-cp39-win_arm64.whl", hash = "sha256:24c2ed34dc222ed754247a2702b1e1e89fdbaa4016f324b4b8f1a802d4ffe87f"}, + {file = "wrapt-1.17.3-py3-none-any.whl", hash = "sha256:7171ae35d2c33d326ac19dd8facb1e82e5fd04ef8c6c0e394d7af55a55051c22"}, + {file = "wrapt-1.17.3.tar.gz", hash = "sha256:f66eb08feaa410fe4eebd17f2a2c8e2e46d3476e9f8c783daa8e09e0faa666d0"}, +] + +[[package]] +name = "yarl" +version = "1.22.0" +description = "Yet another URL library" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "yarl-1.22.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c7bd6683587567e5a49ee6e336e0612bec8329be1b7d4c8af5687dcdeb67ee1e"}, + {file = "yarl-1.22.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5cdac20da754f3a723cceea5b3448e1a2074866406adeb4ef35b469d089adb8f"}, + {file = "yarl-1.22.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:07a524d84df0c10f41e3ee918846e1974aba4ec017f990dc735aad487a0bdfdf"}, + {file = "yarl-1.22.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e1b329cb8146d7b736677a2440e422eadd775d1806a81db2d4cded80a48efc1a"}, + {file = "yarl-1.22.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:75976c6945d85dbb9ee6308cd7ff7b1fb9409380c82d6119bd778d8fcfe2931c"}, + {file = "yarl-1.22.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:80ddf7a5f8c86cb3eb4bc9028b07bbbf1f08a96c5c0bc1244be5e8fefcb94147"}, + {file = "yarl-1.22.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d332fc2e3c94dad927f2112395772a4e4fedbcf8f80efc21ed7cdfae4d574fdb"}, + {file = "yarl-1.22.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0cf71bf877efeac18b38d3930594c0948c82b64547c1cf420ba48722fe5509f6"}, + {file = "yarl-1.22.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:663e1cadaddae26be034a6ab6072449a8426ddb03d500f43daf952b74553bba0"}, + {file = "yarl-1.22.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:6dcbb0829c671f305be48a7227918cfcd11276c2d637a8033a99a02b67bf9eda"}, + {file = "yarl-1.22.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f0d97c18dfd9a9af4490631905a3f131a8e4c9e80a39353919e2cfed8f00aedc"}, + {file = "yarl-1.22.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:437840083abe022c978470b942ff832c3940b2ad3734d424b7eaffcd07f76737"}, + {file = "yarl-1.22.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a899cbd98dce6f5d8de1aad31cb712ec0a530abc0a86bd6edaa47c1090138467"}, + {file = "yarl-1.22.0-cp310-cp310-win32.whl", hash = "sha256:595697f68bd1f0c1c159fcb97b661fc9c3f5db46498043555d04805430e79bea"}, + {file = "yarl-1.22.0-cp310-cp310-win_amd64.whl", hash = "sha256:cb95a9b1adaa48e41815a55ae740cfda005758104049a640a398120bf02515ca"}, + {file = "yarl-1.22.0-cp310-cp310-win_arm64.whl", hash = "sha256:b85b982afde6df99ecc996990d4ad7ccbdbb70e2a4ba4de0aecde5922ba98a0b"}, + {file = "yarl-1.22.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1ab72135b1f2db3fed3997d7e7dc1b80573c67138023852b6efb336a5eae6511"}, + {file = "yarl-1.22.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:669930400e375570189492dc8d8341301578e8493aec04aebc20d4717f899dd6"}, + {file = "yarl-1.22.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:792a2af6d58177ef7c19cbf0097aba92ca1b9cb3ffdd9c7470e156c8f9b5e028"}, + {file = "yarl-1.22.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3ea66b1c11c9150f1372f69afb6b8116f2dd7286f38e14ea71a44eee9ec51b9d"}, + {file = "yarl-1.22.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3e2daa88dc91870215961e96a039ec73e4937da13cf77ce17f9cad0c18df3503"}, + {file = "yarl-1.22.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ba440ae430c00eee41509353628600212112cd5018d5def7e9b05ea7ac34eb65"}, + {file = "yarl-1.22.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e6438cc8f23a9c1478633d216b16104a586b9761db62bfacb6425bac0a36679e"}, + {file = "yarl-1.22.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c52a6e78aef5cf47a98ef8e934755abf53953379b7d53e68b15ff4420e6683d"}, + {file = "yarl-1.22.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3b06bcadaac49c70f4c88af4ffcfbe3dc155aab3163e75777818092478bcbbe7"}, + {file = "yarl-1.22.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:6944b2dc72c4d7f7052683487e3677456050ff77fcf5e6204e98caf785ad1967"}, + {file = "yarl-1.22.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:d5372ca1df0f91a86b047d1277c2aaf1edb32d78bbcefffc81b40ffd18f027ed"}, + {file = "yarl-1.22.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:51af598701f5299012b8416486b40fceef8c26fc87dc6d7d1f6fc30609ea0aa6"}, + {file = "yarl-1.22.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b266bd01fedeffeeac01a79ae181719ff848a5a13ce10075adbefc8f1daee70e"}, + {file = "yarl-1.22.0-cp311-cp311-win32.whl", hash = "sha256:a9b1ba5610a4e20f655258d5a1fdc7ebe3d837bb0e45b581398b99eb98b1f5ca"}, + {file = "yarl-1.22.0-cp311-cp311-win_amd64.whl", hash = "sha256:078278b9b0b11568937d9509b589ee83ef98ed6d561dfe2020e24a9fd08eaa2b"}, + {file = "yarl-1.22.0-cp311-cp311-win_arm64.whl", hash = "sha256:b6a6f620cfe13ccec221fa312139135166e47ae169f8253f72a0abc0dae94376"}, + {file = "yarl-1.22.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e340382d1afa5d32b892b3ff062436d592ec3d692aeea3bef3a5cfe11bbf8c6f"}, + {file = "yarl-1.22.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f1e09112a2c31ffe8d80be1b0988fa6a18c5d5cad92a9ffbb1c04c91bfe52ad2"}, + {file = "yarl-1.22.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:939fe60db294c786f6b7c2d2e121576628468f65453d86b0fe36cb52f987bd74"}, + {file = "yarl-1.22.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e1651bf8e0398574646744c1885a41198eba53dc8a9312b954073f845c90a8df"}, + {file = "yarl-1.22.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b8a0588521a26bf92a57a1705b77b8b59044cdceccac7151bd8d229e66b8dedb"}, + {file = "yarl-1.22.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:42188e6a615c1a75bcaa6e150c3fe8f3e8680471a6b10150c5f7e83f47cc34d2"}, + {file = "yarl-1.22.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f6d2cb59377d99718913ad9a151030d6f83ef420a2b8f521d94609ecc106ee82"}, + {file = "yarl-1.22.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50678a3b71c751d58d7908edc96d332af328839eea883bb554a43f539101277a"}, + {file = "yarl-1.22.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1e8fbaa7cec507aa24ea27a01456e8dd4b6fab829059b69844bd348f2d467124"}, + {file = "yarl-1.22.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:433885ab5431bc3d3d4f2f9bd15bfa1614c522b0f1405d62c4f926ccd69d04fa"}, + {file = "yarl-1.22.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:b790b39c7e9a4192dc2e201a282109ed2985a1ddbd5ac08dc56d0e121400a8f7"}, + {file = "yarl-1.22.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:31f0b53913220599446872d757257be5898019c85e7971599065bc55065dc99d"}, + {file = "yarl-1.22.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a49370e8f711daec68d09b821a34e1167792ee2d24d405cbc2387be4f158b520"}, + {file = "yarl-1.22.0-cp312-cp312-win32.whl", hash = "sha256:70dfd4f241c04bd9239d53b17f11e6ab672b9f1420364af63e8531198e3f5fe8"}, + {file = "yarl-1.22.0-cp312-cp312-win_amd64.whl", hash = "sha256:8884d8b332a5e9b88e23f60bb166890009429391864c685e17bd73a9eda9105c"}, + {file = "yarl-1.22.0-cp312-cp312-win_arm64.whl", hash = "sha256:ea70f61a47f3cc93bdf8b2f368ed359ef02a01ca6393916bc8ff877427181e74"}, + {file = "yarl-1.22.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8dee9c25c74997f6a750cd317b8ca63545169c098faee42c84aa5e506c819b53"}, + {file = "yarl-1.22.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:01e73b85a5434f89fc4fe27dcda2aff08ddf35e4d47bbbea3bdcd25321af538a"}, + {file = "yarl-1.22.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:22965c2af250d20c873cdbee8ff958fb809940aeb2e74ba5f20aaf6b7ac8c70c"}, + {file = "yarl-1.22.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b4f15793aa49793ec8d1c708ab7f9eded1aa72edc5174cae703651555ed1b601"}, + {file = "yarl-1.22.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5542339dcf2747135c5c85f68680353d5cb9ffd741c0f2e8d832d054d41f35a"}, + {file = "yarl-1.22.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5c401e05ad47a75869c3ab3e35137f8468b846770587e70d71e11de797d113df"}, + {file = "yarl-1.22.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:243dda95d901c733f5b59214d28b0120893d91777cb8aa043e6ef059d3cddfe2"}, + {file = "yarl-1.22.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bec03d0d388060058f5d291a813f21c011041938a441c593374da6077fe21b1b"}, + {file = "yarl-1.22.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b0748275abb8c1e1e09301ee3cf90c8a99678a4e92e4373705f2a2570d581273"}, + {file = "yarl-1.22.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:47fdb18187e2a4e18fda2c25c05d8251a9e4a521edaed757fef033e7d8498d9a"}, + {file = "yarl-1.22.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c7044802eec4524fde550afc28edda0dd5784c4c45f0be151a2d3ba017daca7d"}, + {file = "yarl-1.22.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:139718f35149ff544caba20fce6e8a2f71f1e39b92c700d8438a0b1d2a631a02"}, + {file = "yarl-1.22.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e1b51bebd221006d3d2f95fbe124b22b247136647ae5dcc8c7acafba66e5ee67"}, + {file = "yarl-1.22.0-cp313-cp313-win32.whl", hash = "sha256:d3e32536234a95f513bd374e93d717cf6b2231a791758de6c509e3653f234c95"}, + {file = "yarl-1.22.0-cp313-cp313-win_amd64.whl", hash = "sha256:47743b82b76d89a1d20b83e60d5c20314cbd5ba2befc9cda8f28300c4a08ed4d"}, + {file = "yarl-1.22.0-cp313-cp313-win_arm64.whl", hash = "sha256:5d0fcda9608875f7d052eff120c7a5da474a6796fe4d83e152e0e4d42f6d1a9b"}, + {file = "yarl-1.22.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:719ae08b6972befcba4310e49edb1161a88cdd331e3a694b84466bd938a6ab10"}, + {file = "yarl-1.22.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:47d8a5c446df1c4db9d21b49619ffdba90e77c89ec6e283f453856c74b50b9e3"}, + {file = "yarl-1.22.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:cfebc0ac8333520d2d0423cbbe43ae43c8838862ddb898f5ca68565e395516e9"}, + {file = "yarl-1.22.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4398557cbf484207df000309235979c79c4356518fd5c99158c7d38203c4da4f"}, + {file = "yarl-1.22.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2ca6fd72a8cd803be290d42f2dec5cdcd5299eeb93c2d929bf060ad9efaf5de0"}, + {file = "yarl-1.22.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ca1f59c4e1ab6e72f0a23c13fca5430f889634166be85dbf1013683e49e3278e"}, + {file = "yarl-1.22.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6c5010a52015e7c70f86eb967db0f37f3c8bd503a695a49f8d45700144667708"}, + {file = "yarl-1.22.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d7672ecf7557476642c88497c2f8d8542f8e36596e928e9bcba0e42e1e7d71f"}, + {file = "yarl-1.22.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:3b7c88eeef021579d600e50363e0b6ee4f7f6f728cd3486b9d0f3ee7b946398d"}, + {file = "yarl-1.22.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:f4afb5c34f2c6fecdcc182dfcfc6af6cccf1aa923eed4d6a12e9d96904e1a0d8"}, + {file = "yarl-1.22.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:59c189e3e99a59cf8d83cbb31d4db02d66cda5a1a4374e8a012b51255341abf5"}, + {file = "yarl-1.22.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:5a3bf7f62a289fa90f1990422dc8dff5a458469ea71d1624585ec3a4c8d6960f"}, + {file = "yarl-1.22.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:de6b9a04c606978fdfe72666fa216ffcf2d1a9f6a381058d4378f8d7b1e5de62"}, + {file = "yarl-1.22.0-cp313-cp313t-win32.whl", hash = "sha256:1834bb90991cc2999f10f97f5f01317f99b143284766d197e43cd5b45eb18d03"}, + {file = "yarl-1.22.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ff86011bd159a9d2dfc89c34cfd8aff12875980e3bd6a39ff097887520e60249"}, + {file = "yarl-1.22.0-cp313-cp313t-win_arm64.whl", hash = "sha256:7861058d0582b847bc4e3a4a4c46828a410bca738673f35a29ba3ca5db0b473b"}, + {file = "yarl-1.22.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:34b36c2c57124530884d89d50ed2c1478697ad7473efd59cfd479945c95650e4"}, + {file = "yarl-1.22.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:0dd9a702591ca2e543631c2a017e4a547e38a5c0f29eece37d9097e04a7ac683"}, + {file = "yarl-1.22.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:594fcab1032e2d2cc3321bb2e51271e7cd2b516c7d9aee780ece81b07ff8244b"}, + {file = "yarl-1.22.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f3d7a87a78d46a2e3d5b72587ac14b4c16952dd0887dbb051451eceac774411e"}, + {file = "yarl-1.22.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:852863707010316c973162e703bddabec35e8757e67fcb8ad58829de1ebc8590"}, + {file = "yarl-1.22.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:131a085a53bfe839a477c0845acf21efc77457ba2bcf5899618136d64f3303a2"}, + {file = "yarl-1.22.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:078a8aefd263f4d4f923a9677b942b445a2be970ca24548a8102689a3a8ab8da"}, + {file = "yarl-1.22.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bca03b91c323036913993ff5c738d0842fc9c60c4648e5c8d98331526df89784"}, + {file = "yarl-1.22.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:68986a61557d37bb90d3051a45b91fa3d5c516d177dfc6dd6f2f436a07ff2b6b"}, + {file = "yarl-1.22.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:4792b262d585ff0dff6bcb787f8492e40698443ec982a3568c2096433660c694"}, + {file = "yarl-1.22.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:ebd4549b108d732dba1d4ace67614b9545b21ece30937a63a65dd34efa19732d"}, + {file = "yarl-1.22.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f87ac53513d22240c7d59203f25cc3beac1e574c6cd681bbfd321987b69f95fd"}, + {file = "yarl-1.22.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:22b029f2881599e2f1b06f8f1db2ee63bd309e2293ba2d566e008ba12778b8da"}, + {file = "yarl-1.22.0-cp314-cp314-win32.whl", hash = "sha256:6a635ea45ba4ea8238463b4f7d0e721bad669f80878b7bfd1f89266e2ae63da2"}, + {file = "yarl-1.22.0-cp314-cp314-win_amd64.whl", hash = "sha256:0d6e6885777af0f110b0e5d7e5dda8b704efed3894da26220b7f3d887b839a79"}, + {file = "yarl-1.22.0-cp314-cp314-win_arm64.whl", hash = "sha256:8218f4e98d3c10d683584cb40f0424f4b9fd6e95610232dd75e13743b070ee33"}, + {file = "yarl-1.22.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:45c2842ff0e0d1b35a6bf1cd6c690939dacb617a70827f715232b2e0494d55d1"}, + {file = "yarl-1.22.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:d947071e6ebcf2e2bee8fce76e10faca8f7a14808ca36a910263acaacef08eca"}, + {file = "yarl-1.22.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:334b8721303e61b00019474cc103bdac3d7b1f65e91f0bfedeec2d56dfe74b53"}, + {file = "yarl-1.22.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e7ce67c34138a058fd092f67d07a72b8e31ff0c9236e751957465a24b28910c"}, + {file = "yarl-1.22.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d77e1b2c6d04711478cb1c4ab90db07f1609ccf06a287d5607fcd90dc9863acf"}, + {file = "yarl-1.22.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4647674b6150d2cae088fc07de2738a84b8bcedebef29802cf0b0a82ab6face"}, + {file = "yarl-1.22.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:efb07073be061c8f79d03d04139a80ba33cbd390ca8f0297aae9cce6411e4c6b"}, + {file = "yarl-1.22.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e51ac5435758ba97ad69617e13233da53908beccc6cfcd6c34bbed8dcbede486"}, + {file = "yarl-1.22.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:33e32a0dd0c8205efa8e83d04fc9f19313772b78522d1bdc7d9aed706bfd6138"}, + {file = "yarl-1.22.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:bf4a21e58b9cde0e401e683ebd00f6ed30a06d14e93f7c8fd059f8b6e8f87b6a"}, + {file = "yarl-1.22.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:e4b582bab49ac33c8deb97e058cd67c2c50dac0dd134874106d9c774fd272529"}, + {file = "yarl-1.22.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:0b5bcc1a9c4839e7e30b7b30dd47fe5e7e44fb7054ec29b5bb8d526aa1041093"}, + {file = "yarl-1.22.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:c0232bce2170103ec23c454e54a57008a9a72b5d1c3105dc2496750da8cfa47c"}, + {file = "yarl-1.22.0-cp314-cp314t-win32.whl", hash = "sha256:8009b3173bcd637be650922ac455946197d858b3630b6d8787aa9e5c4564533e"}, + {file = "yarl-1.22.0-cp314-cp314t-win_amd64.whl", hash = "sha256:9fb17ea16e972c63d25d4a97f016d235c78dd2344820eb35bc034bc32012ee27"}, + {file = "yarl-1.22.0-cp314-cp314t-win_arm64.whl", hash = "sha256:9f6d73c1436b934e3f01df1e1b21ff765cd1d28c77dfb9ace207f746d4610ee1"}, + {file = "yarl-1.22.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3aa27acb6de7a23785d81557577491f6c38a5209a254d1191519d07d8fe51748"}, + {file = "yarl-1.22.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:af74f05666a5e531289cb1cc9c883d1de2088b8e5b4de48004e5ca8a830ac859"}, + {file = "yarl-1.22.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:62441e55958977b8167b2709c164c91a6363e25da322d87ae6dd9c6019ceecf9"}, + {file = "yarl-1.22.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b580e71cac3f8113d3135888770903eaf2f507e9421e5697d6ee6d8cd1c7f054"}, + {file = "yarl-1.22.0-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e81fda2fb4a07eda1a2252b216aa0df23ebcd4d584894e9612e80999a78fd95b"}, + {file = "yarl-1.22.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:99b6fc1d55782461b78221e95fc357b47ad98b041e8e20f47c1411d0aacddc60"}, + {file = "yarl-1.22.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:088e4e08f033db4be2ccd1f34cf29fe994772fb54cfe004bbf54db320af56890"}, + {file = "yarl-1.22.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e4e1f6f0b4da23e61188676e3ed027ef0baa833a2e633c29ff8530800edccba"}, + {file = "yarl-1.22.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:84fc3ec96fce86ce5aa305eb4aa9358279d1aa644b71fab7b8ed33fe3ba1a7ca"}, + {file = "yarl-1.22.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:5dbeefd6ca588b33576a01b0ad58aa934bc1b41ef89dee505bf2932b22ddffba"}, + {file = "yarl-1.22.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:14291620375b1060613f4aab9ebf21850058b6b1b438f386cc814813d901c60b"}, + {file = "yarl-1.22.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:a4fcfc8eb2c34148c118dfa02e6427ca278bfd0f3df7c5f99e33d2c0e81eae3e"}, + {file = "yarl-1.22.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:029866bde8d7b0878b9c160e72305bbf0a7342bcd20b9999381704ae03308dc8"}, + {file = "yarl-1.22.0-cp39-cp39-win32.whl", hash = "sha256:4dcc74149ccc8bba31ce1944acee24813e93cfdee2acda3c172df844948ddf7b"}, + {file = "yarl-1.22.0-cp39-cp39-win_amd64.whl", hash = "sha256:10619d9fdee46d20edc49d3479e2f8269d0779f1b031e6f7c2aa1c76be04b7ed"}, + {file = "yarl-1.22.0-cp39-cp39-win_arm64.whl", hash = "sha256:dd7afd3f8b0bfb4e0d9fc3c31bfe8a4ec7debe124cfd90619305def3c8ca8cd2"}, + {file = "yarl-1.22.0-py3-none-any.whl", hash = "sha256:1380560bdba02b6b6c90de54133c81c9f2a453dee9912fe58c1dcced1edb7cff"}, + {file = "yarl-1.22.0.tar.gz", hash = "sha256:bebf8557577d4401ba8bd9ff33906f1376c877aa78d1fe216ad01b4d6745af71"}, +] + +[package.dependencies] +idna = ">=2.0" +multidict = ">=4.0" +propcache = ">=0.2.1" + +[[package]] +name = "zipp" +version = "3.23.0" +description = "Backport of pathlib-compatible object wrapper for zip files" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e"}, + {file = "zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166"}, +] + +[package.extras] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] +cover = ["pytest-cov"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +enabler = ["pytest-enabler (>=2.2)"] +test = ["big-O", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more_itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] +type = ["pytest-mypy"] + +[metadata] +lock-version = "2.1" +python-versions = ">=3.12,<3.14" +content-hash = "40971a0e3251dddba28ffca505774c2f0ee81a872114c89efd041543f92c073b" diff --git a/projects/agents/poetry.toml b/projects/agents/poetry.toml new file mode 100644 index 0000000..be97f1e --- /dev/null +++ b/projects/agents/poetry.toml @@ -0,0 +1,3 @@ +[virtualenvs] +in-project = true +prefer-active-python = true \ No newline at end of file diff --git a/projects/agents/pyproject.toml b/projects/agents/pyproject.toml new file mode 100644 index 0000000..71f4b12 --- /dev/null +++ b/projects/agents/pyproject.toml @@ -0,0 +1,44 @@ +[tool.poetry] +name = "agents" +version = "0.1.0" +description = "Agents for Nemesis data processing and interaction." +readme = "README.md" + +[tool.poetry.dependencies] +python = ">=3.12,<3.14" +fastapi = "^0.115.6" +uvicorn = "^0.34.0" +dapr = "1.16.0" +dapr-ext-fastapi = "1.16.0" +structlog = "^25.1.0" +pydantic = "^2.10.4" +typing-extensions = "^4.12.2" +gql = {extras = ["requests"], version = "^3.5.3"} +websockets = "^14.2" +# pydantic-ai-slim = {extras = ["retries"], version = "^0.7.1"} +common = { path = "../../libs/common", develop = true } +pydantic-ai = "^0.7.1" +dapr-ext-workflow = "1.16.0" +logfire = "^4.3.3" +colorlog = "^6.9.0" +opentelemetry-instrumentation-fastapi = "^0.57b0" +opentelemetry-api = "^1.36.0" +opentelemetry-sdk = "^1.36.0" +opentelemetry-instrumentation = "^0.57b0" +opentelemetry-exporter-otlp-proto-grpc = "^1.36.0" +opentelemetry-exporter-otlp-proto-http = "^1.36.0" +openinference-instrumentation-pydantic-ai = {version = "^0.1.4", python = ">=3.12,<3.14"} +asyncpg = "^0.30.0" +psycopg = "^3.2.9" +pythonnet = "^3.0.4" +protobuf = "6.31.1" + +[tool.poetry.group.dev.dependencies] +ruff = "^0.9.2" +pytest = "^8.4.2" +pytest-asyncio = "^1.2.0" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" + diff --git a/projects/agents/tests/test_example.py b/projects/agents/tests/test_example.py new file mode 100644 index 0000000..b497045 --- /dev/null +++ b/projects/agents/tests/test_example.py @@ -0,0 +1,3 @@ +def test_example(): + """Simple example test to verify pytest is working.""" + assert True diff --git a/projects/alerting/.vscode/settings.json b/projects/alerting/.vscode/settings.json index 34bd581..0cdb088 100644 --- a/projects/alerting/.vscode/settings.json +++ b/projects/alerting/.vscode/settings.json @@ -6,15 +6,13 @@ "editor.formatOnSave": false }, "[python]": { - "editor.formatOnSave": true, "editor.codeActionsOnSave": { - "source.fixAll": "explicit", - "source.organizeImports": "explicit" + "source.fixAll.ruff": "explicit", + "source.organizeImports.ruff": "explicit" }, "editor.defaultFormatter": "charliermarsh.ruff" }, "autoDocstring.docstringFormat": "google", - "editor.formatOnSave": true, "files.exclude": { "**/.DS_Store": true, "**/.git": true, diff --git a/projects/alerting/alerting/main.py b/projects/alerting/alerting/main.py index 0e8660b..68dbf8a 100644 --- a/projects/alerting/alerting/main.py +++ b/projects/alerting/alerting/main.py @@ -2,10 +2,9 @@ import asyncio import os import re from contextlib import asynccontextmanager -from typing import Optional import apprise -import structlog +from common.logger import get_logger from common.models import Alert, CloudEvent from dapr.clients import DaprClient from dapr.ext.fastapi import DaprApp @@ -14,7 +13,7 @@ from gql import Client, gql from gql.transport.websockets import WebsocketsTransport from pydantic import BaseModel -logger = structlog.get_logger(module=__name__) +logger = get_logger(__name__) apobj = apprise.Apprise() is_initialized = False @@ -62,7 +61,9 @@ async def lifespan(app: FastAPI): logger.info(f"[alerting] adding Apprise URL: {url} (tag: {tag})") apobj.add(f"{url}?footer=no", tag=tag) else: - logger.warning("No Apprise services were added during initialization") + # Use test endpoint as default when APPRISE_URLS is not configured + logger.info("No APPRISE_URLS configured, using test endpoint as default") + apobj.add("json://localhost:8000/test/alert?footer=no", tag="default") is_initialized = True @@ -85,10 +86,10 @@ dapr_app = DaprApp(app) class TestAlert(BaseModel): - title: Optional[str] = "Nemesis Alert" + title: str | None = "Nemesis Alert" body: str - service: Optional[str] = None - tag: Optional[str] = None + service: str | None = None + tag: str | None = None async def handle_feedback_subscription(): @@ -121,12 +122,13 @@ async def handle_feedback_subscription(): while True: try: transport = WebsocketsTransport( - url="ws://hasura:8080/v1/graphql", headers={"x-hasura-admin-secret": hasura_admin_secret} + url="ws://hasura:8080/v1/graphql", + headers={"x-hasura-admin-secret": hasura_admin_secret} ) async with Client( transport=transport, - fetch_schema_from_transport=True, + fetch_schema_from_transport=False, # Disable schema fetching to avoid large payload ) as session: async for result in session.subscribe(SUBSCRIPTION): if result is None: @@ -288,6 +290,42 @@ async def handle_alert(event: CloudEvent[Alert]): raise +@app.get("/apprise-info") +async def get_apprise_info(): + """Get information about configured Apprise URLs, specifically Slack channels.""" + apprise_urls = os.getenv("APPRISE_URLS", "") + + if not apprise_urls: + return {"channels": []} + + channels = [] + + for apprise_url in apprise_urls.split(","): + url, tag = process_apprise_url(apprise_url) + + # Only process Slack URLs + if url.startswith("slack://"): + # Extract channel name from Slack URL format: slack://TOKEN@WORKSPACE/#channel + import re + channel_match = re.search(r'#([^?]+)', url) + if channel_match: + channel_name = channel_match.group(1) + + if tag and tag != "default": + channels.append({ + "name": channel_name, + "type": "tagged", + "tag": tag + }) + else: + channels.append({ + "name": channel_name, + "type": "main" + }) + + return {"channels": channels} + + @app.api_route("/healthz", methods=["GET", "HEAD"]) async def healthcheck(): """Health check endpoint for Docker healthcheck.""" diff --git a/projects/alerting/poetry.lock b/projects/alerting/poetry.lock index 7147c49..2e01426 100644 --- a/projects/alerting/poetry.lock +++ b/projects/alerting/poetry.lock @@ -495,12 +495,12 @@ version = "0.4.6" description = "Cross-platform colored terminal text." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -groups = ["main"] -markers = "platform_system == \"Windows\"" +groups = ["main", "dev"] files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] +markers = {main = "platform_system == \"Windows\"", dev = "sys_platform == \"win32\""} [[package]] name = "common" @@ -513,7 +513,7 @@ files = [] develop = true [package.dependencies] -dapr = "^1.14.0" +dapr = "1.16.0" fastapi = "^0.115.6" minio = "^7.2.14" pydantic = "^2.10.5" @@ -525,14 +525,14 @@ url = "../../libs/common" [[package]] name = "dapr" -version = "1.15.0" +version = "1.16.0" description = "The official release of Dapr Python SDK." optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "dapr-1.15.0-py3-none-any.whl", hash = "sha256:0093bf6df5eb9a14fbab60191a619438e0b6b336f60a7994e184276bcc35d5fb"}, - {file = "dapr-1.15.0.tar.gz", hash = "sha256:6b2373084143f164cb00702758b17a14fc4442314a1f3e2be36ee008d486c47a"}, + {file = "dapr-1.16.0-py3-none-any.whl", hash = "sha256:076dd559a0b450eae24b1c2ae779c9299ed3e06a05c1f72719a6613af8d19ced"}, + {file = "dapr-1.16.0.tar.gz", hash = "sha256:c7e3d005552a598d07608d0d502b2bc432e86678f94b2beccc13a096b9198684"}, ] [package.dependencies] @@ -545,18 +545,18 @@ typing-extensions = ">=4.4.0" [[package]] name = "dapr-ext-fastapi" -version = "1.15.0" +version = "1.16.0" description = "The official release of Dapr FastAPI extension." optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "dapr-ext-fastapi-1.15.0.tar.gz", hash = "sha256:d5411d24c6dcc256041b29383caa95d6024e58ab094ad24f7e72b9396bc245b0"}, - {file = "dapr_ext_fastapi-1.15.0-py3-none-any.whl", hash = "sha256:429f15345a4b2fb89586fe691d9c8addba0001246c54cf0fc7e5c7c3a6075c97"}, + {file = "dapr-ext-fastapi-1.16.0.tar.gz", hash = "sha256:10108c3831ae2164c1589c86e6b86fe8ee146650514961841d9ab5eb783f4a76"}, + {file = "dapr_ext_fastapi-1.16.0-py3-none-any.whl", hash = "sha256:9dcc0aaceb361c5132295450a71d4f9ddec09ab5848dbfe2a8b0cf9050fd903e"}, ] [package.dependencies] -dapr = ">=1.15.0" +dapr = ">=1.16.0" fastapi = ">=0.60.1" uvicorn = ">=0.11.6" @@ -854,6 +854,18 @@ files = [ [package.extras] all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] +[[package]] +name = "iniconfig" +version = "2.1.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760"}, + {file = "iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"}, +] + [[package]] name = "markdown" version = "3.7" @@ -1008,6 +1020,34 @@ rsa = ["cryptography (>=3.0.0)"] signals = ["blinker (>=1.4.0)"] signedtoken = ["cryptography (>=3.0.0)", "pyjwt (>=2.0.0,<3)"] +[[package]] +name = "packaging" +version = "25.0" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"}, + {file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"}, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746"}, + {file = "pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["coverage", "pytest", "pytest-benchmark"] + [[package]] name = "propcache" version = "0.3.0" @@ -1323,6 +1363,63 @@ files = [ [package.dependencies] typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" +[[package]] +name = "pygments" +version = "2.19.2" +description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b"}, + {file = "pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887"}, +] + +[package.extras] +windows-terminal = ["colorama (>=0.4.6)"] + +[[package]] +name = "pytest" +version = "8.4.2" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79"}, + {file = "pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01"}, +] + +[package.dependencies] +colorama = {version = ">=0.4", markers = "sys_platform == \"win32\""} +iniconfig = ">=1" +packaging = ">=20" +pluggy = ">=1.5,<2" +pygments = ">=2.7.2" + +[package.extras] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "pytest-asyncio" +version = "1.2.0" +description = "Pytest support for asyncio" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "pytest_asyncio-1.2.0-py3-none-any.whl", hash = "sha256:8e17ae5e46d8e7efe51ab6494dd2010f4ca8dae51652aa3c8d55acf50bfb2e99"}, + {file = "pytest_asyncio-1.2.0.tar.gz", hash = "sha256:c609a64a2a8768462d0c99811ddb8bd2583c33fd33cf7f21af1c142e824ffb57"}, +] + +[package.dependencies] +pytest = ">=8.2,<9" +typing-extensions = {version = ">=4.12", markers = "python_version < \"3.13\""} + +[package.extras] +docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1)"] +testing = ["coverage (>=6.2)", "hypothesis (>=5.7.1)"] + [[package]] name = "python-dateutil" version = "2.9.0.post0" @@ -1536,7 +1633,7 @@ version = "4.12.2" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" -groups = ["main"] +groups = ["main", "dev"] files = [ {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, @@ -1758,4 +1855,4 @@ propcache = ">=0.2.0" [metadata] lock-version = "2.1" python-versions = ">=3.12,<4.0" -content-hash = "068fb76877123e63914d534cc0851e4e406bdfecc590098463b6c953f34b37e3" +content-hash = "f62871b658b146a8effd43e8acbb6b1ec0c2a6c8da6fc49201bb2a6edff87f3b" diff --git a/projects/alerting/pyproject.toml b/projects/alerting/pyproject.toml index 44757ca..c9222a0 100644 --- a/projects/alerting/pyproject.toml +++ b/projects/alerting/pyproject.toml @@ -8,8 +8,8 @@ readme = "README.md" python = ">=3.12,<4.0" fastapi = "^0.115.6" uvicorn = "^0.34.0" -dapr = "^1.15.0" -dapr-ext-fastapi = "^1.15.0" +dapr = "1.16.0" +dapr-ext-fastapi = "1.16.0" structlog = "^25.1.0" pydantic = "^2.10.4" typing-extensions = "^4.12.2" @@ -22,76 +22,10 @@ websockets = "^14.2" [tool.poetry.group.dev.dependencies] ruff = "^0.9.2" +pytest = "^8.4.2" +pytest-asyncio = "^1.2.0" [build-system] requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" -[tool.ruff] -select = [ - "E", # pycodestyle errors - "W", # pycodestyle warnings - "F", # pyflakes - "I", # isort - "C", # flake8-comprehensions - "B", # flake8-bugbear - "UP", # pyupgrade - "NPY", # numpydoc - "A", # flake8-annotations - "TCH001", # Move application-only imports into TYPE_CHECKING block - "TCH002", # Move third-party imports into TYPE_CHECKING block - "TCH003", # Move standard library imports into TYPE_CHECKING block -] -ignore = [ - "E501", # line too long, handled by black - "B008", # do not perform function calls in argument defaults - "C901", # too complex - "W191", # indentation contains tabs - "F722", # syntax error in forward annotation - "UP007", # X | Y syntax while we're still supporting 3.9 - "UP038", # isinstance() X | Y instance ^ - "B905", # zip() without strict (isn't supported in 3.9) -] - -exclude = [ - ".bzr", - ".direnv", - ".eggs", - ".git", - ".git-rewrite", - ".hg", - ".mypy_cache", - ".nox", - ".pants.d", - ".pytype", - ".ruff_cache", - ".svn", - ".tox", - ".venv", - "__pypackages__", - "_build", - "buck-out", - "build", - "dist", - "node_modules", - "venv", -] - -line-length = 120 -indent-width = 4 -target-version = "py312" - -[tool.ruff.lint] -fixable = ["ALL"] -unfixable = ["B"] - -dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" -extend-select = [ - "I", # isort, added per instructions in the VS code extension (https://marketplace.visualstudio.com/items?itemName=charliermarsh.ruff) -] - -[tool.ruff.format] -quote-style = "double" -indent-style = "space" -skip-magic-trailing-comma = false -line-ending = "auto" diff --git a/projects/alerting/tests/test_example.py b/projects/alerting/tests/test_example.py new file mode 100644 index 0000000..b497045 --- /dev/null +++ b/projects/alerting/tests/test_example.py @@ -0,0 +1,3 @@ +def test_example(): + """Simple example test to verify pytest is working.""" + assert True diff --git a/projects/cli/.vscode/settings.json b/projects/cli/.vscode/settings.json index 512def4..b402bbd 100644 --- a/projects/cli/.vscode/settings.json +++ b/projects/cli/.vscode/settings.json @@ -8,14 +8,13 @@ "[python]": { "editor.formatOnSave": true, "editor.codeActionsOnSave": { - "source.fixAll": "explicit", - "source.organizeImports": "explicit" + "source.fixAll.ruff": "explicit", + "source.organizeImports.ruff": "explicit" }, - "editor.defaultFormatter": "charliermarsh.ruff", + "editor.defaultFormatter": "charliermarsh.ruff" }, "python.defaultInterpreterPath": "./.venv/bin/python", "autoDocstring.docstringFormat": "google", - "editor.formatOnSave": true, "files.exclude": { "**/.DS_Store": true, "**/.git": true, diff --git a/projects/cli/README.md b/projects/cli/README.md index 06c6fc4..e08d93c 100644 --- a/projects/cli/README.md +++ b/projects/cli/README.md @@ -124,3 +124,32 @@ Alternatively, you can build the production image and run it with the following: ```bash docker compose -f compose.yaml -f compose.prod.build.yaml run --rm cli ``` + + +# Using submit.sh (in dev) +## Building the dev image +1. Navigate to the cli directory. Perform all the following steps from this directory. +```bash +cd Nemesis/projects/cli +``` + +2. Build the base images: +```bash +docker compose -f ../../compose.base.yaml build +``` + +3. Build the nemesis-cli image: +```bash +docker build -t nemesis-cli --target dev --no-cache -f Dockerfile ../.. +``` + +4. Export NEMESIS_CLI_IMAGE +```bash +export NEMESIS_CLI_IMAGE=nemesis-cli:latest +``` + +5. Run ./submit.sh as normal: +```bash +cd ../.. +./tools/submit.sh --help +``` diff --git a/projects/cli/cli/config.py b/projects/cli/cli/config.py index 5bf41b0..e315202 100644 --- a/projects/cli/cli/config.py +++ b/projects/cli/cli/config.py @@ -1,14 +1,13 @@ from pathlib import Path -from typing import Annotated, Optional, Union +from typing import Annotated, Union from urllib.parse import urlparse import yaml -from pydantic import BaseModel, Field, field_validator +from pydantic import BaseModel, ConfigDict, Field, field_validator class BaseConfig(BaseModel): - class Config: - extra = "forbid" + model_config = ConfigDict(extra="forbid") class StrictHttpUrl(str): @@ -79,7 +78,7 @@ class MythicConfig(BaseConfig): class OutflankConfig(BaseConfig): url: StrictHttpUrl credential: PasswordCredential - downloads_dir_path: Optional[Path] = Field( + downloads_dir_path: Path | None = Field( None, description="Optional: Path to Outflank C2's upload directory where files will be pulled from instead of the Outflank API", ) @@ -99,8 +98,8 @@ class Config(BaseConfig): validate_https_certs: bool = Field(True, description="Whether to validate HTTPS certificates") nemesis: NemesisConfig - mythic: Optional[list[MythicConfig]] = Field(default_factory=list) - outflank: Optional[list[OutflankConfig]] = Field(default_factory=list) + mythic: list[MythicConfig] | None = Field(default_factory=list) + outflank: list[OutflankConfig] | None = Field(default_factory=list) @field_validator("mythic", "outflank", mode="before") @classmethod diff --git a/projects/cli/cli/main.py b/projects/cli/cli/main.py index 1a47fe9..48defa4 100644 --- a/projects/cli/cli/main.py +++ b/projects/cli/cli/main.py @@ -3,7 +3,6 @@ import asyncio import sys import click - from cli.config import load_config from cli.log import setup_logging from cli.monitor import monitor_main @@ -121,16 +120,56 @@ def get_os_user_and_host_string() -> str: @click.option("-w", "--workers", default=10, help="Number of worker threads", show_default=True) @click.option("-u", "--username", default="n", help="Basic auth username", show_default=True) @click.option("-p", "--password", default="n", help="Basic auth password", show_default=True) -@click.option("--project", default="assess-test", help="Project name for metadata", show_default=True) +@click.option("-s", "--source", default=None, help="Source name for metadata (e.g., 'host://HOST1')", show_default=True) +@click.option("-j", "--project", default="assess-test", help="Project name for metadata", show_default=True) @click.option( - "--agent-id", default=("submit" + get_os_user_and_host_string()), help="Agent ID for metadata", show_default=True + "-a", + "--agent-id", + default=("submit" + get_os_user_and_host_string()), + help="Agent ID for metadata", + show_default=True, ) @click.option( "-f", "--file", "file_path", type=click.Path(exists=True, dir_okay=False, path_type=str), - help="Path to single file to submit (alternative to PATHS for backwards compatibility)", + help="Path to single file to submit", + default=None, +) +@click.option( + "--container", + is_flag=True, + help="Submit files as containers to the /containers endpoint", +) +@click.option( + "--filters", + type=click.Path(exists=True, dir_okay=False, path_type=str), + help="Path to JSON file containing file filters (only used with --container)", +) +@click.option( + "--include-pattern", + multiple=True, + help="Include glob pattern for container filtering (can be used multiple times). Only used with --container.", +) +@click.option( + "--exclude-pattern", + multiple=True, + help="Exclude glob pattern for container filtering (can be used multiple times). Only used with --container.", +) +@click.option( + "--repeat", + type=int, + default=0, + help="Number of additional times to repeat the submission (default: 0 - no repeat)", + show_default=True, +) +@click.option( + "--folder", + type=str, + default=None, + help="Parent folder path to prepend to all uploaded file paths (e.g., 'C:\\Users\\Admin')", + show_default=False, ) def submit( debug: bool, @@ -143,8 +182,16 @@ def submit( project: str, agent_id: str, file_path: str, + container: bool, + source: str | None = None, + filters: str | None = None, + include_pattern: tuple[str, ...] = (), + exclude_pattern: tuple[str, ...] = (), + repeat: int = 0, + folder: str | None = None, ): """Submit files to Nemesis for processing""" + pattern_type: str = "glob" # only handle glob formats if passed manually, for now submit_main( debug, paths, @@ -156,6 +203,14 @@ def submit( project, agent_id, file_path, + container, + source, + filters, + include_pattern, + exclude_pattern, + pattern_type, + repeat, + folder, ) @@ -166,11 +221,13 @@ def submit( @click.option("-u", "--username", default="n", help="Basic auth username", show_default=True) @click.option("-p", "--password", default="n", help="Basic auth password", show_default=True) @click.option("--project", default="assess-test", help="Project name for metadata", show_default=True) +@click.option("--source", help="Source name for metadata") @click.option( "--agent-id", default=("monitor" + get_os_user_and_host_string()), help="Agent ID for metadata", show_default=True ) @click.option("-w", "--workers", default=10, help="Number of worker threads for initial submission", show_default=True) @click.option("--only-monitor", is_flag=True, help="Only monitor for new files, don't submit existing files") +@click.option("--container", is_flag=True, help="Submit files as containers to the /containers endpoint") def monitor( path: str, debug: bool, @@ -181,6 +238,8 @@ def monitor( agent_id: str, workers: int, only_monitor: bool, + container: bool, + source: str | None = None, ): """Monitor a folder for new files and submit them to Nemesis""" monitor_main( @@ -193,6 +252,8 @@ def monitor( agent_id, only_monitor, workers, + container, + source, ) diff --git a/projects/cli/cli/monitor.py b/projects/cli/cli/monitor.py index 4370eb4..7e0e7d8 100644 --- a/projects/cli/cli/monitor.py +++ b/projects/cli/cli/monitor.py @@ -5,23 +5,34 @@ import time from pathlib import Path import click -from watchdog.events import FileSystemEventHandler -from watchdog.observers import Observer - from cli.log import setup_logging from cli.submit import submit_files +from watchdog.events import FileSystemEventHandler +from watchdog.observers import Observer class NewFileHandler(FileSystemEventHandler): """Handler for new file events in the monitored directory""" - def __init__(self, host: str, username: str, password: str, project: str, agent_id: str, logger: logging.Logger): + def __init__( + self, + host: str, + username: str, + password: str, + project: str, + agent_id: str, + logger: logging.Logger, + container: bool = False, + source: str = None, + ): self.host = host self.username = username self.password = password self.project = project self.agent_id = agent_id self.logger = logger + self.container = container + self.source = source def on_created(self, event): """Called when a file or directory is created""" @@ -43,6 +54,8 @@ class NewFileHandler(FileSystemEventHandler): password=self.password, project=self.project, agent_id=self.agent_id, + container=self.container, + source=self.source, ) except Exception as e: self.logger.error(f"Failed to submit new file {file_path}: {e}") @@ -64,6 +77,8 @@ class NewFileHandler(FileSystemEventHandler): password=self.password, project=self.project, agent_id=self.agent_id, + container=self.container, + source=self.source, ) except Exception as e: self.logger.error(f"Failed to submit moved file {file_path}: {e}") @@ -79,6 +94,8 @@ def monitor_main( agent_id: str, only_monitor: bool, workers: int, + container: bool, + source: str | None = None, ): """Monitor a folder for new files and submit them to Nemesis""" try: @@ -108,6 +125,8 @@ def monitor_main( password=password, project=project, agent_id=agent_id, + container=container, + source=source, ) if not success: @@ -118,7 +137,7 @@ def monitor_main( logger.info("Skipping existing files (--only-monitor enabled)") # Set up file system watcher - event_handler = NewFileHandler(host, username, password, project, agent_id, logger) + event_handler = NewFileHandler(host, username, password, project, agent_id, logger, container, source) observer = Observer() observer.schedule(event_handler, str(folder_path), recursive=True) diff --git a/projects/cli/cli/mythic_connector/config.py b/projects/cli/cli/mythic_connector/config.py index a298ddd..ce04269 100644 --- a/projects/cli/cli/mythic_connector/config.py +++ b/projects/cli/cli/mythic_connector/config.py @@ -124,9 +124,7 @@ VALIDATORS = [ Validator( "mythic.credential.password", must_exist=True, when=Validator("mythic.credential.token", must_exist=False) ), - Validator( - "mythic.credential.token", must_exist=True, when=Validator("mythic.credential.username", must_exist=False) - ), + Validator("mythic.credential.token", must_exist=False), # Nemesis validators Validator("nemesis.url", must_exist=True), Validator("nemesis.credential", must_exist=True), diff --git a/projects/cli/cli/mythic_connector/db.py b/projects/cli/cli/mythic_connector/db.py index 1333f04..6d2a607 100644 --- a/projects/cli/cli/mythic_connector/db.py +++ b/projects/cli/cli/mythic_connector/db.py @@ -1,5 +1,4 @@ import logging -from typing import Optional import plyvel @@ -21,7 +20,7 @@ class Database: """ self.db = plyvel.DB(path, create_if_missing=True) - def get(self, key: str) -> Optional[int]: + def get(self, key: str) -> int | None: """Get an integer value from the database. Args: diff --git a/projects/cli/cli/mythic_connector/handlers.py b/projects/cli/cli/mythic_connector/handlers.py index 6e8b73f..e7e86ad 100644 --- a/projects/cli/cli/mythic_connector/handlers.py +++ b/projects/cli/cli/mythic_connector/handlers.py @@ -6,15 +6,14 @@ from datetime import UTC, datetime, timedelta from typing import Any import urllib3 -from common.models2.api import FileMetadata, FileWithMetadataResponse -from mythic import mythic, mythic_classes - from cli.mythic_connector.config import Settings from cli.mythic_connector.db import Database from cli.mythic_connector.logger import get_logger # from cli.mythic_connector.nemesis import NemesisClient from cli.nemesis_client import NemesisClient +from common.models2.api import FileMetadata, FileWithMetadataResponse +from mythic import mythic, mythic_classes logger = get_logger(__name__) urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) @@ -175,8 +174,12 @@ class FileHandler: """Callback to upload the downloaded file to Nemesis.""" self._total_files_count += 1 + # Use the host field as the source identifier + source = f"host://{file_meta.get('host', 'unknown')}" + metadata = FileMetadata( agent_id="mythic", + source=source, project=self.cfg.project, timestamp=datetime.now(UTC), expiration=datetime.now(UTC).replace(year=datetime.now().year + 1), diff --git a/projects/cli/cli/mythic_connector/mythic_connector.py b/projects/cli/cli/mythic_connector/mythic_connector.py index e6708fe..e98bbd4 100644 --- a/projects/cli/cli/mythic_connector/mythic_connector.py +++ b/projects/cli/cli/mythic_connector/mythic_connector.py @@ -3,7 +3,6 @@ import logging import click import urllib3 - from cli.mythic_connector.config import get_settings diff --git a/projects/cli/cli/mythic_connector/sync.py b/projects/cli/cli/mythic_connector/sync.py index 3947bab..ce54468 100644 --- a/projects/cli/cli/mythic_connector/sync.py +++ b/projects/cli/cli/mythic_connector/sync.py @@ -5,14 +5,13 @@ from urllib.parse import ParseResult, urlparse, urlunparse # Third-party imports import aiohttp -from mythic import mythic - from cli.config import NemesisConfig, PasswordCredential from cli.mythic_connector.config import Settings, TokenCredential from cli.mythic_connector.db import Database from cli.mythic_connector.handlers import FileHandler from cli.mythic_connector.logger import get_logger from cli.nemesis_client import NemesisClient +from mythic import mythic logger = get_logger(__name__) diff --git a/projects/cli/cli/nemesis_client.py b/projects/cli/cli/nemesis_client.py index 915afe1..4a1dd6b 100644 --- a/projects/cli/cli/nemesis_client.py +++ b/projects/cli/cli/nemesis_client.py @@ -2,9 +2,10 @@ import logging import os from datetime import UTC, datetime from pathlib import Path -from typing import BinaryIO, Optional, Union +from typing import BinaryIO, Union import requests +from cli.config import NemesisConfig from common.models2.api import ( APIInfo, ErrorResponse, @@ -16,8 +17,6 @@ from common.models2.api import ( from requests.auth import HTTPBasicAuth from requests_toolbelt import MultipartEncoder -from cli.config import NemesisConfig - logger = logging.getLogger(__name__) @@ -34,13 +33,14 @@ class NemesisClient: cfg.credential.password, ) - def create_file_metadata(self, path: str, agent_id: str, project: str) -> FileMetadata: + def create_file_metadata(self, path: str, agent_id: str, project: str, source: str | None = None) -> FileMetadata: """Create standardized file metadata. Args: path: File path agent_id: Identifier for the agent project: Project name + source: Optional source identifier (e.g., "host://192.168.1.1", "https://site.domain.com") Returns: FileMetadata object with standard fields @@ -48,6 +48,7 @@ class NemesisClient: now = datetime.now(UTC) return FileMetadata( agent_id=agent_id, + source=source, project=project, timestamp=now, expiration=now.replace(year=now.year + 1), @@ -131,7 +132,7 @@ class NemesisClient: if need_cleanup and file_stream is not None: file_stream.close() - def get_health(self) -> Optional[Union[HealthResponse, ErrorResponse]]: + def get_health(self) -> Union[HealthResponse, ErrorResponse] | None: """Get API health status. Returns: @@ -149,7 +150,7 @@ class NemesisClient: logger.error(f"Error getting health status: {e}") return None - def get_api_info(self) -> Optional[Union[APIInfo, ErrorResponse]]: + def get_api_info(self) -> Union[APIInfo, ErrorResponse] | None: """Get API information. Returns: @@ -167,7 +168,7 @@ class NemesisClient: logger.error(f"Error getting API info: {e}") return None - def reload_yara_rules(self) -> Optional[Union[YaraReloadResponse, ErrorResponse]]: + def reload_yara_rules(self) -> Union[YaraReloadResponse, ErrorResponse] | None: """Reload Yara rules. Returns: diff --git a/projects/cli/cli/stage1_connector/cache.py b/projects/cli/cli/stage1_connector/cache.py index 0daf3f5..8ffff3b 100644 --- a/projects/cli/cli/stage1_connector/cache.py +++ b/projects/cli/cli/stage1_connector/cache.py @@ -1,5 +1,4 @@ import logging -from typing import Optional from cli.stage1_connector.outflankc2_client import Implant, OutflankC2Client @@ -21,7 +20,7 @@ class ImplantCache: self.logger.error(f"Failed to initialize implant cache: {e}") raise - async def get_implant(self, uid: str) -> Optional[Implant]: + async def get_implant(self, uid: str) -> Implant | None: """Get implant from cache, fetching from API if not found""" if uid in self.cache: return self.cache[uid] diff --git a/projects/cli/cli/stage1_connector/download_processor.py b/projects/cli/cli/stage1_connector/download_processor.py index 00a15a9..d09525a 100644 --- a/projects/cli/cli/stage1_connector/download_processor.py +++ b/projects/cli/cli/stage1_connector/download_processor.py @@ -5,23 +5,22 @@ import tempfile from dataclasses import dataclass from datetime import UTC, datetime from pathlib import Path -from typing import Optional, Union +from typing import Union import plyvel -from common.models2.api import FileMetadata, FileWithMetadataResponse - from cli.nemesis_client import NemesisClient from cli.stage1_connector.outflankc2_client import Download, Implant, OutflankC2Client +from common.models2.api import FileMetadata, FileWithMetadataResponse logger = logging.getLogger(__name__) @dataclass class DownloadedFileInfo: - file_path: Optional[Path] + file_path: Path | None delete_after: bool success: bool - temp_file: Optional[Path] = None + temp_file: Path | None = None def __post_init__(self): # Track temp file separately for cleanup @@ -48,8 +47,8 @@ class OutflankDownloadProcessor: db_path: Path, nemesis: NemesisClient, project: str, - outflank_downloads_dir_path: Optional[Path] = None, - outflank: Optional[OutflankC2Client] = None, + outflank_downloads_dir_path: Path | None = None, + outflank: OutflankC2Client | None = None, ): """Initialize with LevelDB path and optional components @@ -90,7 +89,7 @@ class OutflankDownloadProcessor: return self.db.get(key) is not None def mark_processed( - self, download: Download, success: bool = True, response: Optional[FileWithMetadataResponse] = None + self, download: Download, success: bool = True, response: FileWithMetadataResponse | None = None ): """Mark download as processed""" key = f"download:{download.uid}".encode() @@ -111,7 +110,7 @@ class OutflankDownloadProcessor: file_path: Union[Path, str], metadata: FileMetadata, delete_after: bool = False, - ) -> tuple[bool, Optional[str], Optional[FileWithMetadataResponse]]: + ) -> tuple[bool, str | None, FileWithMetadataResponse | None]: """Upload file with metadata to the API endpoint""" if not self.client: raise ValueError("NemesisClient is required") @@ -161,8 +160,12 @@ class OutflankDownloadProcessor: # Upload the file to Nemesis try: + # Use the implant hostname as the source identifier + source = f"host://{implant.hostname}" if implant.hostname else None + metadata = FileMetadata( agent_id="stage1", + source=source, project=self.project, timestamp=datetime.now(UTC), expiration=datetime.now(UTC).replace(year=datetime.now().year + 1), @@ -234,7 +237,7 @@ class OutflankDownloadProcessor: return DownloadedFileInfo(temp_file, delete_after=True, success=True) - def _cleanup_temp_file(self, temp_file: Optional[Path]) -> None: + def _cleanup_temp_file(self, temp_file: Path | None) -> None: """Clean up temporary file if it exists and hasn't been cleaned up already. Silently ignores if the file doesn't exist, as it may have been cleaned up by the upload_file method.""" diff --git a/projects/cli/cli/stage1_connector/outflankc2_client.py b/projects/cli/cli/stage1_connector/outflankc2_client.py index 722504d..9f3aa26 100644 --- a/projects/cli/cli/stage1_connector/outflankc2_client.py +++ b/projects/cli/cli/stage1_connector/outflankc2_client.py @@ -3,7 +3,7 @@ import logging from collections.abc import Callable from dataclasses import dataclass from datetime import datetime -from typing import Any, Optional, ParamSpec, TypeVar +from typing import Any, ParamSpec, TypeVar from urllib.parse import urljoin, urlparse import aiohttp @@ -203,7 +203,7 @@ class OutflankC2Client: raise @requires_auth - async def get_current_user(self) -> Optional[str]: + async def get_current_user(self) -> str | None: """Get the currently authenticated username.""" if not self._access_token: self.logger.warning("No access token available") diff --git a/projects/cli/cli/stage1_connector/stage1_connector.py b/projects/cli/cli/stage1_connector/stage1_connector.py index 915a249..5b4a5b2 100644 --- a/projects/cli/cli/stage1_connector/stage1_connector.py +++ b/projects/cli/cli/stage1_connector/stage1_connector.py @@ -3,7 +3,6 @@ import asyncio import logging import urllib3 - from cli.config import Config, OutflankConfig from cli.log import setup_logging from cli.nemesis_client import NemesisClient diff --git a/projects/cli/cli/stress_test.py b/projects/cli/cli/stress_test.py index afdbae0..4bb7de5 100755 --- a/projects/cli/cli/stress_test.py +++ b/projects/cli/cli/stress_test.py @@ -6,7 +6,6 @@ import time from dataclasses import dataclass from datetime import UTC, datetime, timedelta from pathlib import Path -from typing import Optional import aiohttp @@ -15,7 +14,7 @@ import aiohttp class TestResult: success: bool duration: float - error: Optional[str] = None + error: str | None = None class APIStressTest: diff --git a/projects/cli/cli/submit.py b/projects/cli/cli/submit.py index 32b7543..7d687df 100644 --- a/projects/cli/cli/submit.py +++ b/projects/cli/cli/submit.py @@ -4,16 +4,17 @@ import logging import os import sys import threading -from datetime import UTC, datetime +import time from pathlib import Path from queue import Empty, Queue from threading import Event, Thread -from typing import Optional import click import colorlog import requests import urllib3 +from requests.adapters import HTTPAdapter +from requests.packages.urllib3.util.retry import Retry from tqdm import tqdm # Disable SSL warnings for the submit functionality @@ -48,6 +49,7 @@ class UploadTracker: self.failures = [] # (path, error) tuples self.successes = [] # (path, bytes) tuples self.lock = threading.Lock() + self.start_time = time.perf_counter() @property def total_files(self) -> int: @@ -61,7 +63,7 @@ class UploadTracker: self.bytes_uploaded += bytes_uploaded self.successes.append((path, bytes_uploaded)) - def add_failure(self, path: Path, error: Optional[str]): + def add_failure(self, path: Path, error: str | None): """Track a failed upload with path and error message""" with self.lock: self.failed += 1 @@ -86,6 +88,13 @@ class UploadTracker: with self.lock: return self.successes.copy() + def format_duration(self, seconds: float) -> str: + """Convert seconds to HH:MM:SS format with total seconds""" + hours = int(seconds // 3600) + minutes = int((seconds % 3600) // 60) + secs = seconds % 60 + return f"{hours:02d}:{minutes:02d}:{secs:05.2f} ({seconds:.2f}s)" + def display_summary(self): """Display a summary of the upload operation""" total = self.total_files @@ -94,6 +103,8 @@ class UploadTracker: return success_rate = (self.successful / total) * 100 if total > 0 else 0 + elapsed_time = time.perf_counter() - self.start_time + files_per_sec = total / elapsed_time if elapsed_time > 0 else 0 logger.info("\nUpload Summary:") logger.info("─" * 40) @@ -101,7 +112,9 @@ class UploadTracker: logger.info(f"Successful: {self.successful:,}") logger.info(f"Failed: {self.failed:,}") logger.info(f"Success Rate: {success_rate:.1f}%") - logger.info(f"Total Uploaded: {self.format_bytes()}") + logger.info(f"Data Uploaded: {self.format_bytes()}") + logger.info(f"Duration: {self.format_duration(elapsed_time)}") + logger.info(f"Speed: {files_per_sec:.2f} files/sec") if self.failed > 0: logger.info("\nFailed Uploads:") @@ -110,6 +123,83 @@ class UploadTracker: logger.warning(f"• {path}: {error}") +def parse_filters( + filters_file: str | None, + include_patterns: tuple[str, ...], + exclude_patterns: tuple[str, ...], + pattern_type: str, +) -> dict | None: + """Parse filter options into the format expected by the API""" + + # If a filters file is provided, load it + if filters_file: + if include_patterns or exclude_patterns: + raise ValueError("Cannot specify both --filters file and --include-pattern/--exclude-pattern options") + + try: + with open(filters_file) as f: + filters_data = json.load(f) + + # Validate the structure + if not isinstance(filters_data, dict): + raise ValueError("Filters file must contain a JSON object") + + # Ensure pattern_type is set if not specified in file + if "pattern_type" not in filters_data: + filters_data["pattern_type"] = pattern_type + + return filters_data + + except json.JSONDecodeError as e: + raise ValueError(f"Invalid JSON in filters file: {e}") from e + except Exception as e: + raise ValueError(f"Error reading filters file: {e}") from e + + # If inline patterns are provided, build the filter object + elif include_patterns or exclude_patterns: + file_filters: dict = {"pattern_type": pattern_type} + + if include_patterns: + file_filters["include"] = list(include_patterns) + if exclude_patterns: + file_filters["exclude"] = list(exclude_patterns) + + return file_filters + + return None + + +def validate_filters(file_filters: dict) -> None: + """Validate the structure of file filters""" + allowed_fields = {"include", "exclude", "pattern_type"} + + if not isinstance(file_filters, dict): + raise ValueError("file_filters must be a dictionary") + + # Check for unknown fields + unknown_fields = set(file_filters.keys()) - allowed_fields + if unknown_fields: + raise ValueError(f"Unknown filter fields: {', '.join(unknown_fields)}") + + # Validate pattern_type + pattern_type = file_filters.get("pattern_type", "glob") + if pattern_type not in ["glob", "regex"]: + raise ValueError(f"pattern_type must be 'glob' or 'regex', got: {pattern_type}") + + # Validate include/exclude are lists of strings + for field in ["include", "exclude"]: + if field in file_filters: + patterns = file_filters[field] + if not isinstance(patterns, list): + raise ValueError(f"{field} must be a list of strings") + if not all(isinstance(p, str) for p in patterns): + raise ValueError(f"All {field} patterns must be strings") + + # At least one of include or exclude should be present + if not any(field in file_filters for field in ["include", "exclude"]): + raise ValueError("At least one of 'include' or 'exclude' patterns must be specified") + + def submit_main( debug: bool, paths: tuple[str, ...], @@ -121,6 +211,14 @@ def submit_main( project: str, agent_id: str, file_path: str, + container: bool, + source: str | None = None, + filters: str | None = None, + include_pattern: tuple[str, ...] = (), + exclude_pattern: tuple[str, ...] = (), + pattern_type: str = "glob", + repeat: int = 0, + folder: str | None = None, ): """Submit files to Nemesis for processing. @@ -143,6 +241,19 @@ def submit_main( # Upload with basic auth: main.py submit /etc/issue -u admin -p secret + + # Upload container with filters from file: + main.py submit archive.zip --container --filters filters.json + + # Upload container with inline patterns: + main.py submit archive.zip --container --include-pattern "*.exe" --exclude-pattern "*/temp/*" + + # Submit file twice (original + 1 repeat): + main.py submit /etc/issue --repeat 1 + + # Upload files with custom parent folder path: + main.py submit /tmp/data --folder "C:\\Users\\Admin\\Documents" -r + # Files at /tmp/data/file.txt will have path "C:\\Users\\Admin\\Documents\\file.txt" """ try: if debug: @@ -159,6 +270,16 @@ def submit_main( logger.error("No files or paths specified") sys.exit(1) + # Validate repeat parameter + if repeat < 0: + logger.error("Repeat count must be at least 0") + sys.exit(1) + + # Validate filter options + file_filters = None + if container and (filters or include_pattern or exclude_pattern): + file_filters = parse_filters(filters, include_pattern, exclude_pattern, pattern_type) + # Convert to Path objects path_objects = [Path(p) for p in paths] @@ -173,6 +294,11 @@ def submit_main( password=password, project=project, agent_id=agent_id, + container=container, + source=source, + file_filters=file_filters, + repeat=repeat, + folder=folder, ) if not success: @@ -188,47 +314,106 @@ def submit_files( host: str = "0.0.0.0:7443", recursive: bool = False, verbose: bool = False, - workers: int = 10, + workers: int = 5, username: str = "n", password: str = "n", project: str = "assess-test", - agent_id: str = "beacon123", + agent_id: str = "submit.sh", + container: bool = False, + source: str | None = None, + file_filters: dict | None = None, + repeat: int = 0, + folder: str | None = None, ): """Submit files to Nemesis""" - file_queue = Queue() - error_queue = Queue() - tracker = UploadTracker() - stop_event = Event() - - # Start counting total files (this will also start filling the queue) - total_files = stream_files(paths, recursive, file_queue) - - if total_files == 0: - logger.error("No files found to upload") + # Validate that filters are only used with container mode + if file_filters and not container: + logger.error("File filters can only be used with --container flag") return False # Validate authentication before starting uploads - # Disable SSL warnings urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) auth = (username, password) if username and password else None if not validate_auth(host, auth): return False - # Create progress bar + # Total submissions = 1 original + repeat additional submissions + total_submissions = 1 + repeat + + # Get list of files once + temp_queue = Queue() + total_files = stream_files(paths, recursive, temp_queue) + + if total_files == 0: + logger.error("No files found to upload") + return False + + # Convert queue to list for reuse across submissions + files_to_submit = [] + while not temp_queue.empty(): + try: + files_to_submit.append(temp_queue.get_nowait()) + except Empty: + break + + # Calculate total operations for progress bar + total_operations = total_files * total_submissions + + # Determine actual number of worker threads we'll use + actual_workers = min(workers, total_operations) + + # Create session with retry logic and connection pooling sized for our workers + session = create_session_with_retries(max_workers=actual_workers) + + # Create shared structures for concurrent submission + overall_tracker = UploadTracker() + error_queue = Queue() + stop_event = Event() + + if total_submissions > 1: + logger.info( + f"Starting {total_submissions} concurrent submissions ({total_files} files × {total_submissions} submissions = {total_operations} total operations)" + ) + + # Create progress bar for all operations with tqdm( - total=total_files, - desc="Uploading files", - unit="file", + total=total_operations, + desc="Uploading files concurrently" if total_submissions > 1 else "Uploading files", + unit="upload", bar_format="{l_bar}{bar}| {n_fmt}/{total_fmt} [{elapsed}<{remaining}]", ) as pbar: - # Create and start worker threads + # Create worker threads - each handles multiple submissions of the same file threads = [] - # Auth credentials already validated above - for _ in range(min(workers, total_files)): + file_submission_queue = Queue() + + # Populate queue with (file, submission_number) pairs + for submission_num in range(total_submissions): + for file_path in files_to_submit: + file_submission_queue.put((file_path, submission_num)) + + # Create worker threads + for _ in range(min(workers, total_operations)): thread = Thread( - target=worker, - args=(file_queue, host, tracker, pbar, error_queue, stop_event, verbose, auth, project, agent_id), + target=concurrent_worker, + args=( + file_submission_queue, + host, + session, + overall_tracker, + pbar, + error_queue, + stop_event, + verbose, + auth, + project, + agent_id, + container, + source, + file_filters, + paths, + folder, + ), ) thread.start() threads.append(thread) @@ -238,8 +423,8 @@ def submit_files( thread.join() # Display final metrics - tracker.display_summary() - return tracker.failed == 0 # Return True if no failures + overall_tracker.display_summary() + return overall_tracker.failed == 0 def stream_files(paths: list[Path], recursive: bool, file_queue: Queue) -> int: @@ -272,7 +457,7 @@ def stream_files(paths: list[Path], recursive: bool, file_queue: Queue) -> int: return total_files -def validate_auth(host_port: str, auth: Optional[tuple[str, str]] = None) -> bool: +def validate_auth(host_port: str, auth: tuple[str, str] | None = None) -> bool: """ Validate authentication credentials before starting uploads. Returns True if auth is valid or not required, False otherwise. @@ -298,61 +483,266 @@ def validate_auth(host_port: str, auth: Optional[tuple[str, str]] = None) -> boo return False -def create_metadata(path: str, project: str = "assess-test", agent_id: str = "beacon123") -> dict: - return { +def calculate_metadata_path(file_path: Path, base_paths: list[Path] | None, folder: str | None) -> str: + """ + Calculate the path to use in metadata, applying folder transformation if specified. + + If folder is provided: + - Find the common base from base_paths + - Calculate relative path from that base + - Join with the folder parameter + + Otherwise, return the file path as-is. + """ + if folder is None or not base_paths: + return str(file_path) + + # Resolve to absolute paths + abs_file_path = file_path.resolve() + + # Find which base path this file is under + matching_base = None + for base_path in base_paths: + abs_base = base_path.resolve() + try: + # Check if file is under this base path + abs_file_path.relative_to(abs_base) + matching_base = abs_base + break + except ValueError: + # Not under this base, try next + continue + + if matching_base is None: + # File is not under any base path, use as-is + return str(file_path) + + # Calculate relative path from the matching base + try: + if matching_base.is_file(): + # Base is a file, so the relative part is just the filename + rel_path = abs_file_path.name + else: + # Base is a directory, calculate relative path + rel_path = abs_file_path.relative_to(matching_base) + except ValueError: + # Shouldn't happen, but fallback + return str(file_path) + + # Normalize the relative path to use forward slashes + rel_path_normalized = str(rel_path).replace("\\", "/") + + # If folder is empty string, return just the relative path without any prefix + if folder == "": + return rel_path_normalized + + # Ensure folder ends with path separator if it doesn't + folder_normalized = folder.rstrip("/\\") + + # Join folder with relative path using Unix-style paths + result = folder_normalized + "/" + rel_path_normalized + + return result + + +def create_metadata( + path: str, + project: str = "assess-test", + agent_id: str = "submit.sh", + source: str | None = None, + file_filters: dict | None = None, +) -> dict: + """Create metadata dictionary for file submission""" + metadata: dict = { "agent_id": agent_id, "project": project, - "timestamp": datetime.now(UTC).isoformat(), - "expiration": datetime.now(UTC).replace(year=datetime.now().year + 1).isoformat(), + # "timestamp": datetime.now(UTC).isoformat(), # these have defaults in the submission API now + # "expiration": datetime.now(UTC).replace(year=datetime.now().year + 1).isoformat(), "path": str(path), } + if source: + metadata["source"] = source + if file_filters: + # Validate filters before adding to metadata + validate_filters(file_filters) + metadata["file_filters"] = file_filters + return metadata + + +def create_session_with_retries(max_workers: int = 20) -> requests.Session: + """ + Create a requests session with retry logic and connection pooling. + + Args: + max_workers: Maximum number of concurrent workers (used to size connection pool) + """ + session = requests.Session() + + # Configure retry strategy with exponential backoff + retry_strategy = Retry( + total=3, # Total number of retries + status_forcelist=[429, 500, 502, 503, 504], # HTTP status codes to retry on + # method_whitelist=["HEAD", "GET", "POST"], # HTTP methods to retry + backoff_factor=1, # Exponential backoff factor (1, 2, 4 seconds) + raise_on_status=False, # Don't raise on status codes in status_forcelist + ) + + # Size the connection pool to accommodate all workers plus some buffer + # Each worker needs a connection, add 50% buffer for retries and overhead + pool_size = max(20, int(max_workers * 1.5)) + + # Configure HTTP adapter with retry strategy + adapter = HTTPAdapter( + max_retries=retry_strategy, + pool_connections=10, # Number of connection pools to cache + pool_maxsize=pool_size, # Maximum number of connections in pool + pool_block=False, # Don't block when pool is full + ) + + session.mount("http://", adapter) + session.mount("https://", adapter) + + return session def upload_file( file_path: Path, host_port: str, - auth: Optional[tuple[str, str]] = None, + session: requests.Session, + auth: tuple[str, str] | None = None, project: str = "assess-test", - agent_id: str = "beacon123", -) -> tuple[bool, Optional[str], int]: + agent_id: str = "submit.sh", + container: bool = False, + source: str | None = None, + file_filters: dict | None = None, + base_paths: list[Path] | None = None, + folder: str | None = None, +) -> tuple[bool, str | None, int]: """ - Attempt to upload a file. Returns (success, error_message, bytes_uploaded). + Attempt to upload a file with retry logic. Returns (success, error_message, bytes_uploaded). If success is True, error_message will be None. """ - try: - if not os.access(file_path, os.R_OK): - raise PermissionError(f"No read permission for {file_path}") + max_retries = 3 + base_delay = 1.0 - metadata = create_metadata(str(file_path), project, agent_id) - file_size = file_path.stat().st_size + for attempt in range(max_retries): + try: + if not os.access(file_path, os.R_OK): + raise PermissionError(f"No read permission for {file_path}") - with open(file_path, "rb") as f: - files = {"file": f, "metadata": (None, json.dumps(metadata))} - response = requests.post(f"https://{host_port}/api/files", files=files, auth=auth, verify=False) - response.raise_for_status() - return True, None, file_size + # Calculate the metadata path (transformed if folder is provided) + metadata_path = calculate_metadata_path(file_path, base_paths, folder) - except PermissionError: - return False, f"Permission denied: {file_path}", 0 - except FileNotFoundError: - return False, f"File not found: {file_path}", 0 - except requests.exceptions.RequestException as e: - return False, f"Upload failed: {file_path} - {str(e)}", 0 - except Exception as e: - return False, f"Unexpected error with {file_path}: {str(e)}", 0 + metadata = create_metadata(metadata_path, project, agent_id, source, file_filters) + file_size = file_path.stat().st_size + + endpoint = "/api/containers" if container else "/api/files" + + with open(file_path, "rb") as f: + files = {"file": f, "metadata": (None, json.dumps(metadata))} + response = session.post( + f"https://{host_port}{endpoint}", + files=files, + auth=auth, + verify=False, + timeout=(30, 300), + ) + response.raise_for_status() + return True, None, file_size + + except PermissionError: + return False, f"Permission denied: {file_path}", 0 + except FileNotFoundError: + return False, f"File not found: {file_path}", 0 + except (requests.exceptions.SSLError, requests.exceptions.ConnectionError) as e: + if attempt < max_retries - 1: + delay = base_delay * (2**attempt) + logger.debug(f"SSL/Connection error on attempt {attempt + 1}, retrying in {delay}s: {str(e)}") + time.sleep(delay) + continue + return False, f"Upload failed: {file_path} - {str(e)}", 0 + except requests.exceptions.Timeout as e: + if attempt < max_retries - 1: + delay = base_delay * (2**attempt) + logger.debug(f"Timeout on attempt {attempt + 1}, retrying in {delay}s: {str(e)}") + time.sleep(delay) + continue + return False, f"Upload failed: {file_path} - {str(e)}", 0 + except requests.exceptions.RequestException as e: + if attempt < max_retries - 1 and "504" in str(e): + delay = base_delay * (2**attempt) + logger.debug(f"Server error on attempt {attempt + 1}, retrying in {delay}s: {str(e)}") + time.sleep(delay) + continue + return False, f"Upload failed: {file_path} - {str(e)}", 0 + except Exception as e: + return False, f"Unexpected error with {file_path}: {str(e)}", 0 + + return False, f"Upload failed after {max_retries} attempts: {file_path}", 0 -def worker( +def concurrent_worker( queue: Queue, host_port: str, + session: requests.Session, tracker: UploadTracker, progress_bar: tqdm, error_queue: Queue, stop_event: Event, verbose: bool, - auth: Optional[tuple[str, str]] = None, + auth: tuple[str, str] | None = None, project: str = "assess-test", - agent_id: str = "beacon123", + agent_id: str = "submit.sh", + container: bool = False, + source: str | None = None, + file_filters: dict | None = None, + base_paths: list[Path] | None = None, + folder: str | None = None, +): + """Worker thread to process (file, submission_number) pairs from the queue""" + while not stop_event.is_set(): + try: + file_path, submission_num = queue.get_nowait() + except Empty: + break + + success, error, bytes_uploaded = upload_file( + file_path, host_port, session, auth, project, agent_id, container, source, file_filters, base_paths, folder + ) + if success: + tracker.add_success(file_path, bytes_uploaded) + if verbose: + logger.debug(f"✓ {file_path} submission #{submission_num} ({bytes_uploaded:,} bytes)") + else: + tracker.add_failure(file_path, error) + if error: + error_queue.put(error) + logger.warning(f"✗ {file_path} submission #{submission_num}: {error}") + + progress_bar.update(1) + progress_bar.set_description( + f"Uploading (✓:{tracker.successful} ✗:{tracker.failed} | {tracker.format_bytes()})" + ) + queue.task_done() + + +def worker( + queue: Queue, + host_port: str, + session: requests.Session, + tracker: UploadTracker, + progress_bar: tqdm, + error_queue: Queue, + stop_event: Event, + verbose: bool, + auth: tuple[str, str] | None = None, + project: str = "assess-test", + agent_id: str = "submit.sh", + container: bool = False, + source: str | None = None, + file_filters: dict | None = None, + base_paths: list[Path] | None = None, + folder: str | None = None, ): """Worker thread to process files from the queue""" while not stop_event.is_set(): @@ -361,7 +751,9 @@ def worker( except Empty: break - success, error, bytes_uploaded = upload_file(file_path, host_port, auth, project, agent_id) + success, error, bytes_uploaded = upload_file( + file_path, host_port, session, auth, project, agent_id, container, source, file_filters, base_paths, folder + ) if success: tracker.add_success(file_path, bytes_uploaded) if verbose: diff --git a/projects/cli/poetry.lock b/projects/cli/poetry.lock index 3dbfe5b..03ff746 100644 --- a/projects/cli/poetry.lock +++ b/projects/cli/poetry.lock @@ -547,12 +547,12 @@ version = "0.4.6" description = "Cross-platform colored terminal text." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -groups = ["main"] -markers = "platform_system == \"Windows\" or sys_platform == \"win32\"" +groups = ["main", "dev"] files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] +markers = {main = "platform_system == \"Windows\" or sys_platform == \"win32\"", dev = "sys_platform == \"win32\""} [[package]] name = "colorlog" @@ -595,14 +595,14 @@ url = "../../libs/common" [[package]] name = "dapr" -version = "1.15.0" +version = "1.16.0" description = "The official release of Dapr Python SDK." optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "dapr-1.15.0-py3-none-any.whl", hash = "sha256:0093bf6df5eb9a14fbab60191a619438e0b6b336f60a7994e184276bcc35d5fb"}, - {file = "dapr-1.15.0.tar.gz", hash = "sha256:6b2373084143f164cb00702758b17a14fc4442314a1f3e2be36ee008d486c47a"}, + {file = "dapr-1.16.0-py3-none-any.whl", hash = "sha256:076dd559a0b450eae24b1c2ae779c9299ed3e06a05c1f72719a6613af8d19ced"}, + {file = "dapr-1.16.0.tar.gz", hash = "sha256:c7e3d005552a598d07608d0d502b2bc432e86678f94b2beccc13a096b9198684"}, ] [package.dependencies] @@ -965,6 +965,18 @@ files = [ [package.extras] all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] +[[package]] +name = "iniconfig" +version = "2.1.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760"}, + {file = "iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"}, +] + [[package]] name = "minio" version = "7.2.15" @@ -1116,6 +1128,34 @@ asyncio = "*" gql = {version = "*", extras = ["aiohttp", "websockets"]} pycryptodome = "*" +[[package]] +name = "packaging" +version = "25.0" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"}, + {file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"}, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746"}, + {file = "pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["coverage", "pytest", "pytest-benchmark"] + [[package]] name = "plyvel" version = "1.5.1" @@ -1481,6 +1521,21 @@ gcp-secret-manager = ["google-cloud-secret-manager (>=2.23.1)"] toml = ["tomli (>=2.0.1)"] yaml = ["pyyaml (>=6.0.1)"] +[[package]] +name = "pygments" +version = "2.19.2" +description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b"}, + {file = "pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887"}, +] + +[package.extras] +windows-terminal = ["colorama (>=0.4.6)"] + [[package]] name = "pyreadline3" version = "3.5.4" @@ -1497,6 +1552,48 @@ files = [ [package.extras] dev = ["build", "flake8", "mypy", "pytest", "twine"] +[[package]] +name = "pytest" +version = "8.4.2" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79"}, + {file = "pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01"}, +] + +[package.dependencies] +colorama = {version = ">=0.4", markers = "sys_platform == \"win32\""} +iniconfig = ">=1" +packaging = ">=20" +pluggy = ">=1.5,<2" +pygments = ">=2.7.2" + +[package.extras] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "pytest-asyncio" +version = "1.2.0" +description = "Pytest support for asyncio" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "pytest_asyncio-1.2.0-py3-none-any.whl", hash = "sha256:8e17ae5e46d8e7efe51ab6494dd2010f4ca8dae51652aa3c8d55acf50bfb2e99"}, + {file = "pytest_asyncio-1.2.0.tar.gz", hash = "sha256:c609a64a2a8768462d0c99811ddb8bd2583c33fd33cf7f21af1c142e824ffb57"}, +] + +[package.dependencies] +pytest = ">=8.2,<9" +typing-extensions = {version = ">=4.12", markers = "python_version < \"3.13\""} + +[package.extras] +docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1)"] +testing = ["coverage (>=6.2)", "hypothesis (>=5.7.1)"] + [[package]] name = "python-dateutil" version = "2.9.0.post0" @@ -1764,11 +1861,12 @@ version = "4.14.0" description = "Backported and Experimental Type Hints for Python 3.9+" optional = false python-versions = ">=3.9" -groups = ["main"] +groups = ["main", "dev"] files = [ {file = "typing_extensions-4.14.0-py3-none-any.whl", hash = "sha256:a1514509136dd0b477638fc68d6a91497af5076466ad0fa6c338e44e359944af"}, {file = "typing_extensions-4.14.0.tar.gz", hash = "sha256:8676b788e32f02ab42d9e7c61324048ae4c6d844a399eebace3d4979d75ceef4"}, ] +markers = {dev = "python_version < \"3.13\""} [[package]] name = "typing-inspection" @@ -2048,4 +2146,4 @@ propcache = ">=0.2.1" [metadata] lock-version = "2.1" python-versions = ">=3.12,<4.0" -content-hash = "91762f58d9fb199c273ee429323d873b1b8fe000134d4c80f799c37f3de8af3f" +content-hash = "54d782949abfdf19e5f70a09a63cc3e2a07f6a116ef1e3a484c19fa524819fd3" diff --git a/projects/cli/pyproject.toml b/projects/cli/pyproject.toml index 8cda596..f84cf20 100644 --- a/projects/cli/pyproject.toml +++ b/projects/cli/pyproject.toml @@ -26,9 +26,12 @@ pyyaml = "^6.0.2" common = { path = "../../libs/common", develop = true } requests-toolbelt = "^1.0.0" watchdog = "^6.0.0" +dapr = "1.16.0" [tool.poetry.group.dev.dependencies] ruff = "^0.9.2" +pytest = "^8.4.2" +pytest-asyncio = "^1.2.0" [build-system] requires = ["poetry-core>=2.0.0,<3.0.0"] @@ -40,71 +43,3 @@ submit = "cli.main:submit" module_runner = "cli.module_runner:main" stress_test = "cli.stress_test:main" -[tool.ruff] -select = [ - "E", # pycodestyle errors - "W", # pycodestyle warnings - "F", # pyflakes - "I", # isort - "C", # flake8-comprehensions - "B", # flake8-bugbear - "UP", # pyupgrade - "NPY", # numpydoc - "A", # flake8-annotations - "TCH001", # Move application-only imports into TYPE_CHECKING block - "TCH002", # Move third-party imports into TYPE_CHECKING block - "TCH003", # Move standard library imports into TYPE_CHECKING block -] -ignore = [ - "E501", # line too long, handled by black - "B008", # do not perform function calls in argument defaults - "C901", # too complex - "W191", # indentation contains tabs - "F722", # syntax error in forward annotation - "UP007", # X | Y syntax while we're still supporting 3.9 - "UP038", # isinstance() X | Y instance ^ - "B905", # zip() without strict (isn't supported in 3.9) -] - -exclude = [ - ".bzr", - ".direnv", - ".eggs", - ".git", - ".git-rewrite", - ".hg", - ".mypy_cache", - ".nox", - ".pants.d", - ".pytype", - ".ruff_cache", - ".svn", - ".tox", - ".venv", - "__pypackages__", - "_build", - "buck-out", - "build", - "dist", - "node_modules", - "venv", -] - -line-length = 120 -indent-width = 4 -target-version = "py312" - -[tool.ruff.lint] -fixable = ["ALL"] -unfixable = ["B"] - -dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" -extend-select = [ - "I", # isort, added per instructions in the VS code extension (https://marketplace.visualstudio.com/items?itemName=charliermarsh.ruff) -] - -[tool.ruff.format] -quote-style = "double" -indent-style = "space" -skip-magic-trailing-comma = false -line-ending = "auto" \ No newline at end of file diff --git a/projects/cli/tests/test_example.py b/projects/cli/tests/test_example.py new file mode 100644 index 0000000..b497045 --- /dev/null +++ b/projects/cli/tests/test_example.py @@ -0,0 +1,3 @@ +def test_example(): + """Simple example test to verify pytest is working.""" + assert True diff --git a/projects/cli/windows_filters.json b/projects/cli/windows_filters.json new file mode 100644 index 0000000..f59c85d --- /dev/null +++ b/projects/cli/windows_filters.json @@ -0,0 +1,12 @@ + +{ + "pattern_type": "regex", + "include": [ + "^(?:[A-Za-z]://|/)?[Ww]indows/[Ss]ystem32/config/" + ], + "exclude": [ + "^(?:[A-Za-z]://|/)?[Ww]indows/", + "^(?:[A-Za-z]://|/)?[Pp]rogram [Ff]iles/", + "^(?:[A-Za-z]://|/)?[Pp]rogram [Ff]iles \\(x86\\)/" + ] +} \ No newline at end of file diff --git a/projects/document_conversion/Dockerfile b/projects/document_conversion/Dockerfile index e1fcd3b..4d43443 100644 --- a/projects/document_conversion/Dockerfile +++ b/projects/document_conversion/Dockerfile @@ -3,17 +3,27 @@ ARG PYTHON_BASE_DEV_IMAGE=nemesis-python-base-dev ARG PYTHON_BASE_PROD_IMAGE=nemesis-python-base-prod FROM ${PYTHON_BASE_DEV_IMAGE} AS base -# Install dependencies in a single RUN command to reduce layers +ARG TIKA_OCR_LANGUAGES="eng" +ARG TIKA_VERSION="3.1.0" + +# Install dependencies including multiple Tesseract language packs RUN apt-get update && \ apt-get install -y --no-install-recommends \ openjdk-17-jre-headless \ wget \ tesseract-ocr \ - tesseract-ocr-eng \ libpq-dev \ postgresql-client \ - binutils && \ - wget https://archive.apache.org/dist/tika/3.1.0/tika-server-standard-3.1.0.jar -O /tika-server-standard.jar && \ + binutils \ + # Install fonts for better CJK support + fonts-noto-cjk \ + fonts-noto-color-emoji && \ + # Install Tesseract language packs dynamically + for lang in ${TIKA_OCR_LANGUAGES}; do \ + apt-get install -y --no-install-recommends tesseract-ocr-${lang} || echo "Warning: Failed to install tesseract-ocr-${lang}"; \ + done && \ + # Download Tika server + wget https://archive.apache.org/dist/tika/${TIKA_VERSION}/tika-server-standard-${TIKA_VERSION}.jar -O /tika-server-standard.jar && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* @@ -64,16 +74,24 @@ RUN poetry bundle venv --python=/usr/bin/python3 --only=main /venv # FROM nemesis-python-base-prod AS prod FROM ${PYTHON_BASE_PROD_IMAGE} AS prod +ARG TIKA_OCR_LANGUAGES="eng" + # Install dependencies in a single RUN command to reduce layers RUN apt-get update && \ apt-get install -y --no-install-recommends \ openjdk-17-jre-headless \ wget \ tesseract-ocr \ - tesseract-ocr-eng \ libpq-dev \ postgresql-client \ - binutils && \ + binutils \ + # Install fonts for better CJK support + fonts-noto-cjk \ + fonts-noto-color-emoji && \ + # Install Tesseract language packs dynamically + for lang in ${TIKA_OCR_LANGUAGES}; do \ + apt-get install -y --no-install-recommends tesseract-ocr-${lang} || echo "Warning: Failed to install tesseract-ocr-${lang}"; \ + done && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* diff --git a/projects/document_conversion/document_conversion/main.py b/projects/document_conversion/document_conversion/main.py index 72dfe3c..727be34 100644 --- a/projects/document_conversion/document_conversion/main.py +++ b/projects/document_conversion/document_conversion/main.py @@ -1,19 +1,20 @@ +import asyncio import json import os import tempfile import zipfile from contextlib import asynccontextmanager from datetime import timedelta -from typing import Optional import jpype -import jpype.imports +import jpype.imports # noqa: F401 import msoffcrypto import olefile -import psycopg +import psycopg # noqa: F401 import requests -import structlog +from common.db import get_postgres_connection_str from common.helpers import can_convert_to_pdf, can_extract_plaintext, extract_all_strings +from common.logger import WORKFLOW_CLIENT_LOG_LEVEL, WORKFLOW_RUNTIME_LOG_LEVEL, get_logger from common.models import CloudEvent, File, FileEnriched, Transform from common.state_helpers import get_file_enriched from common.storage import StorageMinio @@ -23,35 +24,85 @@ from dapr.ext.workflow import DaprWorkflowClient, DaprWorkflowContext, RetryPoli from dapr.ext.workflow.logger.options import LoggerOptions from dapr.ext.workflow.workflow_activity_context import WorkflowActivityContext from fastapi import FastAPI +from psycopg_pool import ConnectionPool from PyPDF2 import PdfReader -logger = structlog.get_logger(module=__name__) +logger = get_logger(__name__) storage = StorageMinio() db_pool = None workflow_client: DaprWorkflowClient = None -workflow_runtime = WorkflowRuntime( - logger_options=LoggerOptions( - log_level="INFO", - log_handler=None, - log_formatter=None, - ) -) +max_parallel_workflows = int(os.getenv("MAX_PARALLEL_WORKFLOWS", 3)) # maximum workflows that can run at a time +max_workflow_execution_time = int( + os.getenv("MAX_WORKFLOW_EXECUTION_TIME", 300) +) # maximum time (in seconds) until a workflow is killed -with DaprClient() as client: - secret = client.get_secret(store_name="nemesis-secret-store", key="POSTGRES_CONNECTION_STRING") - postgres_connection_string = secret.secret["POSTGRES_CONNECTION_STRING"] +logger.info(f"max_parallel_workflows: {max_parallel_workflows}", pid=os.getpid()) +logger.info(f"max_workflow_execution_time: {max_workflow_execution_time}", pid=os.getpid()) + +# Semaphore for controlling concurrent workflow execution +workflow_semaphore = asyncio.Semaphore(max_parallel_workflows) +active_workflows = {} # Track active workflows +workflow_lock = asyncio.Lock() # For synchronizing access to active_workflows + +workflow_runtime = WorkflowRuntime(logger_options=LoggerOptions(log_level=WORKFLOW_RUNTIME_LOG_LEVEL)) + +postgres_connection_string = get_postgres_connection_str() # Initialize Java Runtime and Tika def init_tika(): if not jpype.isJVMStarted(): jpype.startJVM(classpath=["/tika-server-standard.jar"]) - # Create Tika instance using Java class directly + + # Import Java classes + TikaConfig = jpype.JClass("org.apache.tika.config.TikaConfig") Tika = jpype.JClass("org.apache.tika.Tika") File = jpype.JClass("java.io.File") - return Tika(), File + + # Get OCR language from environment variable + # Note: Use underscores for language types, not hyphens (chi_sim not chi-sim) + ocr_languages = os.getenv("TIKA_OCR_LANGUAGES", "eng").replace("-", "_").replace(" ", "+") + logger.info(f"Configuring Tika with OCR languages: {ocr_languages}") + + # Read the static XML config and substitute the language parameter + with open("/tika-config.xml") as f: + config_xml = f.read() + + # Replace the hardcoded language with the environment variable value + config_xml = config_xml.replace(">eng<", f">{ocr_languages}<") + + # Write the modified config to a temporary file + with tempfile.NamedTemporaryFile(mode="w", suffix=".xml", delete=False) as temp_config: + temp_config.write(config_xml) + temp_config_path = temp_config.name + + try: + # Load Tika with the modified configuration + config = TikaConfig(File(temp_config_path)) + tika_instance = Tika(config) + logger.info(f"Tika initialized successfully with OCR languages: {ocr_languages}") + except Exception as e: + logger.warning(f"Failed to load custom Tika config: {e}, falling back to original config") + # Fall back to loading the original unmodified config file + try: + config = TikaConfig(File("/tika-config.xml")) + tika_instance = Tika(config) + logger.info("Tika initialized with original config file") + except Exception as e2: + logger.error(f"Failed to load original Tika config: {e2}") + # Last resort - use default Tika without any config file + tika_instance = Tika() + logger.info("Tika initialized with default configuration") + finally: + # Clean up temporary file + try: + os.unlink(temp_config_path) + except: + pass + + return tika_instance, File tika, JavaFile = init_tika() @@ -68,11 +119,21 @@ async def lifespan(app: FastAPI): """Lifespan manager for FastAPI - handles startup and shutdown events""" global db_pool, workflow_runtime, workflow_client try: - # start the workflow runtime + # Initialize database pool + db_pool = ConnectionPool( + postgres_connection_string, min_size=max_parallel_workflows, max_size=(3 * max_parallel_workflows) + ) + logger.info( + "Database pool initialized", + min_size=max_parallel_workflows, + max_size=(3 * max_parallel_workflows), + ) + workflow_runtime.start() - # Initialize workflow client - workflow_client = DaprWorkflowClient() + workflow_client = DaprWorkflowClient( + logger_options=LoggerOptions(log_level=WORKFLOW_CLIENT_LOG_LEVEL), + ) except Exception as e: logger.exception(e, message="Error initializing service") @@ -81,6 +142,9 @@ async def lifespan(app: FastAPI): yield # Cleanup + if db_pool: + db_pool.close() + logger.info("Database pool closed") if workflow_runtime: workflow_runtime.shutdown() if jpype.isJVMStarted(): @@ -116,7 +180,7 @@ def is_pdf_encrypted(pdf_path): return reader.is_encrypted except Exception as e: - print(f"Error checking PDF: {e}") + logger.exception(e, "Error checking PDF") return None @@ -185,7 +249,7 @@ def store_transform(ctx, activity_input): transform = activity_input["transform"] transform_type = transform["type"] - with psycopg.connect(postgres_connection_string) as conn: + with db_pool.connection() as conn: with conn.cursor() as cur: cur.execute( """ @@ -217,6 +281,7 @@ def publish_file_message(ctx: WorkflowActivityContext, activity_input: dict): object_id=transform.object_id, originating_object_id=file_enriched.object_id, agent_id=file_enriched.agent_id, + source=file_enriched.source, project=file_enriched.project, timestamp=file_enriched.timestamp, expiration=file_enriched.expiration, @@ -232,7 +297,7 @@ def publish_file_message(ctx: WorkflowActivityContext, activity_input: dict): ) logger.info( - f"Published new file message for transform", + "Published new file message for transform", new_object_id=transform.object_id, originating_object_id=file_enriched.object_id, ) @@ -242,7 +307,7 @@ def publish_file_message(ctx: WorkflowActivityContext, activity_input: dict): @workflow_runtime.activity -def extract_tika_text(ctx: WorkflowActivityContext, file_input: dict) -> Optional[dict]: +def extract_tika_text(ctx: WorkflowActivityContext, file_input: dict) -> dict | None: """Extract text using Tika.""" object_id = file_input.get("object_id") result = None @@ -254,10 +319,29 @@ def extract_tika_text(ctx: WorkflowActivityContext, file_input: dict) -> Optiona return None with storage.download(file_enriched.object_id) as temp_file: - # Extract text using Tika - java_file = JavaFile(temp_file.name) - java_text = tika.parseToString(java_file) - extracted_text = str(java_text) + # Extract text using Tika + retries + max_retries = 2 + last_exception = None + + for attempt in range(max_retries + 1): # 0, 1, 2 (3 total attempts) + try: + java_file = JavaFile(temp_file.name) + java_text = tika.parseToString(java_file) + extracted_text = str(java_text) + break + + except Exception as e: + last_exception = e + if attempt < max_retries: + logger.warning( + f"Tika extraction attempt {attempt + 1} failed, retrying...", + object_id=file_enriched.object_id, + error=str(e), + ) + continue + else: + # Final attempt failed, re-raise the last exception + raise last_exception from e if not extracted_text: return None @@ -276,7 +360,7 @@ def extract_tika_text(ctx: WorkflowActivityContext, file_input: dict) -> Optiona ) # Record success in database - with psycopg.connect(postgres_connection_string) as conn: + with db_pool.connection() as conn: with conn.cursor() as cur: cur.execute( """ @@ -298,7 +382,7 @@ def extract_tika_text(ctx: WorkflowActivityContext, file_input: dict) -> Optiona # Record failure in database try: - with psycopg.connect(postgres_connection_string) as conn: + with db_pool.connection() as conn: with conn.cursor() as cur: cur.execute( """ @@ -316,7 +400,7 @@ def extract_tika_text(ctx: WorkflowActivityContext, file_input: dict) -> Optiona @workflow_runtime.activity -def extract_strings(ctx: WorkflowActivityContext, file_input: dict) -> Optional[dict]: +def extract_strings(ctx: WorkflowActivityContext, file_input: dict) -> dict | None: """Extract strings from binary files.""" object_id = file_input.get("object_id") result = None @@ -355,7 +439,7 @@ def extract_strings(ctx: WorkflowActivityContext, file_input: dict) -> Optional[ ) # Record success in database - with psycopg.connect(postgres_connection_string) as conn: + with db_pool.connection() as conn: with conn.cursor() as cur: cur.execute( """ @@ -377,7 +461,7 @@ def extract_strings(ctx: WorkflowActivityContext, file_input: dict) -> Optional[ # Record failure in database try: - with psycopg.connect(postgres_connection_string) as conn: + with db_pool.connection() as conn: with conn.cursor() as cur: cur.execute( """ @@ -395,7 +479,7 @@ def extract_strings(ctx: WorkflowActivityContext, file_input: dict) -> Optional[ @workflow_runtime.activity -def convert_to_pdf(ctx: WorkflowActivityContext, file_input: dict) -> Optional[dict]: +def convert_to_pdf(ctx: WorkflowActivityContext, file_input: dict) -> dict | None: """Convert file to PDF using Gotenberg.""" object_id = file_input.get("object_id") result = None @@ -446,7 +530,7 @@ def convert_to_pdf(ctx: WorkflowActivityContext, file_input: dict) -> Optional[d ) # Record success in database - with psycopg.connect(postgres_connection_string) as conn: + with db_pool.connection() as conn: with conn.cursor() as cur: cur.execute( """ @@ -472,7 +556,7 @@ def convert_to_pdf(ctx: WorkflowActivityContext, file_input: dict) -> Optional[d ) # Record failure in database due to Gotenberg error - with psycopg.connect(postgres_connection_string) as conn: + with db_pool.connection() as conn: with conn.cursor() as cur: cur.execute( """ @@ -497,7 +581,7 @@ def convert_to_pdf(ctx: WorkflowActivityContext, file_input: dict) -> Optional[d # Record failure in database try: - with psycopg.connect(postgres_connection_string) as conn: + with db_pool.connection() as conn: with conn.cursor() as cur: cur.execute( """ @@ -586,12 +670,111 @@ def document_conversion_workflow(ctx: DaprWorkflowContext, workflow_input: dict) raise +# Workflow concurrency management + + +async def start_workflow_with_concurrency_control(file_enriched: FileEnriched): + """Start a workflow using semaphore for backpressure control.""" + # Acquire semaphore - this will block if we're at max capacity + # This provides natural backpressure to the Dapr pub/sub system + await workflow_semaphore.acquire() + + try: + instance_id = f"text-extraction-{file_enriched.object_id}" + + # Add to active workflows tracking + async with workflow_lock: + active_workflows[instance_id] = { + "object_id": file_enriched.object_id, + "start_time": asyncio.get_event_loop().time(), + "filename": file_enriched.file_name, + } + + logger.info( + "Starting document conversion workflow", + instance_id=instance_id, + object_id=file_enriched.object_id, + active_count=len(active_workflows), + ) + + # Schedule the workflow + workflow_client.schedule_new_workflow( + workflow=document_conversion_workflow, instance_id=instance_id, input={"object_id": file_enriched.object_id} + ) + + # Start monitoring task for this workflow + asyncio.create_task(monitor_workflow_completion(instance_id)) + + except Exception as e: + # Release semaphore on error + workflow_semaphore.release() + logger.exception(e, message="Error starting document conversion workflow") + raise + + +async def monitor_workflow_completion(instance_id: str): + """Monitor a workflow until completion and release semaphore.""" + try: + # Poll for workflow completion + start_time = asyncio.get_event_loop().time() + + while True: + try: + # Check if workflow is still running + state = workflow_client.get_workflow_state(instance_id) + + if state and hasattr(state, "runtime_status"): + status = state.runtime_status.name + + if status in ["COMPLETED", "FAILED", "TERMINATED", "ERROR"]: + elapsed_time = asyncio.get_event_loop().time() - start_time + logger.info( + "Document conversion workflow finished", + instance_id=instance_id, + status=status, + elapsed_time=f"{elapsed_time:.2f}s", + ) + break + + # Check for timeout + if (asyncio.get_event_loop().time() - start_time) > max_workflow_execution_time: + logger.warning( + "Document conversion workflow timed out", + instance_id=instance_id, + max_execution_time=max_workflow_execution_time, + ) + # Try to terminate the workflow + try: + workflow_client.terminate_workflow(instance_id) + except Exception as term_error: + logger.error(f"Failed to terminate workflow {instance_id}: {term_error}") + break + + await asyncio.sleep(0.3) + + except Exception as check_error: + logger.warning(f"Error checking workflow status for {instance_id}: {check_error}") + await asyncio.sleep(2) # Wait longer on error + + except Exception as e: + logger.exception(e, message=f"Error monitoring workflow {instance_id}") + + finally: + # Always clean up and release semaphore + async with workflow_lock: + if instance_id in active_workflows: + del active_workflows[instance_id] + + workflow_semaphore.release() + logger.debug(f"Released semaphore for workflow {instance_id}", active_count=len(active_workflows)) + + # Main handling code @dapr_app.subscribe(pubsub="pubsub", topic="file_enriched") async def handle_file_enriched(event: CloudEvent[FileEnriched]): - """Handler for file_enriched events.""" + """Handler for file_enriched events with semaphore-based concurrency control.""" try: file_enriched = event.data logger.debug("Received file_enriched event", object_id=file_enriched.object_id) @@ -623,12 +806,8 @@ async def handle_file_enriched(event: CloudEvent[FileEnriched]): ) return - instance_id = f"text-extraction-{file_enriched.object_id}" - workflow_client.schedule_new_workflow( - workflow=document_conversion_workflow, instance_id=instance_id, input={"object_id": file_enriched.object_id} - ) - - logger.info("Started text extraction workflow", instance_id=instance_id) + # Start workflow with semaphore control for backpressure + await start_workflow_with_concurrency_control(file_enriched) except Exception as e: logger.exception(e, message="Error handling file_enriched event") @@ -642,8 +821,9 @@ async def health_check(): if not db_pool: return {"status": "unhealthy", "error": "Database pool not initialized"} - async with db_pool.acquire() as connection: - await connection.execute("SELECT 1") + with db_pool.connection() as connection: + with connection.cursor() as cur: + cur.execute("SELECT 1") if not jpype.isJVMStarted(): return {"status": "unhealthy", "error": "JVM not started"} diff --git a/projects/document_conversion/poetry.lock b/projects/document_conversion/poetry.lock index 76f5702..244b0f8 100644 --- a/projects/document_conversion/poetry.lock +++ b/projects/document_conversion/poetry.lock @@ -14,103 +14,137 @@ files = [ [[package]] name = "aiohttp" -version = "3.12.13" +version = "3.13.0" description = "Async http client/server framework (asyncio)" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "aiohttp-3.12.13-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5421af8f22a98f640261ee48aae3a37f0c41371e99412d55eaf2f8a46d5dad29"}, - {file = "aiohttp-3.12.13-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0fcda86f6cb318ba36ed8f1396a6a4a3fd8f856f84d426584392083d10da4de0"}, - {file = "aiohttp-3.12.13-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4cd71c9fb92aceb5a23c4c39d8ecc80389c178eba9feab77f19274843eb9412d"}, - {file = "aiohttp-3.12.13-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34ebf1aca12845066c963016655dac897651e1544f22a34c9b461ac3b4b1d3aa"}, - {file = "aiohttp-3.12.13-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:893a4639694c5b7edd4bdd8141be296042b6806e27cc1d794e585c43010cc294"}, - {file = "aiohttp-3.12.13-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:663d8ee3ffb3494502ebcccb49078faddbb84c1d870f9c1dd5a29e85d1f747ce"}, - {file = "aiohttp-3.12.13-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f0f8f6a85a0006ae2709aa4ce05749ba2cdcb4b43d6c21a16c8517c16593aabe"}, - {file = "aiohttp-3.12.13-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1582745eb63df267c92d8b61ca655a0ce62105ef62542c00a74590f306be8cb5"}, - {file = "aiohttp-3.12.13-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d59227776ee2aa64226f7e086638baa645f4b044f2947dbf85c76ab11dcba073"}, - {file = "aiohttp-3.12.13-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:06b07c418bde1c8e737d8fa67741072bd3f5b0fb66cf8c0655172188c17e5fa6"}, - {file = "aiohttp-3.12.13-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:9445c1842680efac0f81d272fd8db7163acfcc2b1436e3f420f4c9a9c5a50795"}, - {file = "aiohttp-3.12.13-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:09c4767af0b0b98c724f5d47f2bf33395c8986995b0a9dab0575ca81a554a8c0"}, - {file = "aiohttp-3.12.13-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f3854fbde7a465318ad8d3fc5bef8f059e6d0a87e71a0d3360bb56c0bf87b18a"}, - {file = "aiohttp-3.12.13-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2332b4c361c05ecd381edb99e2a33733f3db906739a83a483974b3df70a51b40"}, - {file = "aiohttp-3.12.13-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:1561db63fa1b658cd94325d303933553ea7d89ae09ff21cc3bcd41b8521fbbb6"}, - {file = "aiohttp-3.12.13-cp310-cp310-win32.whl", hash = "sha256:a0be857f0b35177ba09d7c472825d1b711d11c6d0e8a2052804e3b93166de1ad"}, - {file = "aiohttp-3.12.13-cp310-cp310-win_amd64.whl", hash = "sha256:fcc30ad4fb5cb41a33953292d45f54ef4066746d625992aeac33b8c681173178"}, - {file = "aiohttp-3.12.13-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7c229b1437aa2576b99384e4be668af1db84b31a45305d02f61f5497cfa6f60c"}, - {file = "aiohttp-3.12.13-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:04076d8c63471e51e3689c93940775dc3d12d855c0c80d18ac5a1c68f0904358"}, - {file = "aiohttp-3.12.13-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:55683615813ce3601640cfaa1041174dc956d28ba0511c8cbd75273eb0587014"}, - {file = "aiohttp-3.12.13-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:921bc91e602d7506d37643e77819cb0b840d4ebb5f8d6408423af3d3bf79a7b7"}, - {file = "aiohttp-3.12.13-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e72d17fe0974ddeae8ed86db297e23dba39c7ac36d84acdbb53df2e18505a013"}, - {file = "aiohttp-3.12.13-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0653d15587909a52e024a261943cf1c5bdc69acb71f411b0dd5966d065a51a47"}, - {file = "aiohttp-3.12.13-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a77b48997c66722c65e157c06c74332cdf9c7ad00494b85ec43f324e5c5a9b9a"}, - {file = "aiohttp-3.12.13-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d6946bae55fd36cfb8e4092c921075cde029c71c7cb571d72f1079d1e4e013bc"}, - {file = "aiohttp-3.12.13-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f95db8c8b219bcf294a53742c7bda49b80ceb9d577c8e7aa075612b7f39ffb7"}, - {file = "aiohttp-3.12.13-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:03d5eb3cfb4949ab4c74822fb3326cd9655c2b9fe22e4257e2100d44215b2e2b"}, - {file = "aiohttp-3.12.13-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:6383dd0ffa15515283c26cbf41ac8e6705aab54b4cbb77bdb8935a713a89bee9"}, - {file = "aiohttp-3.12.13-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:6548a411bc8219b45ba2577716493aa63b12803d1e5dc70508c539d0db8dbf5a"}, - {file = "aiohttp-3.12.13-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:81b0fcbfe59a4ca41dc8f635c2a4a71e63f75168cc91026c61be665945739e2d"}, - {file = "aiohttp-3.12.13-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:6a83797a0174e7995e5edce9dcecc517c642eb43bc3cba296d4512edf346eee2"}, - {file = "aiohttp-3.12.13-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a5734d8469a5633a4e9ffdf9983ff7cdb512524645c7a3d4bc8a3de45b935ac3"}, - {file = "aiohttp-3.12.13-cp311-cp311-win32.whl", hash = "sha256:fef8d50dfa482925bb6b4c208b40d8e9fa54cecba923dc65b825a72eed9a5dbd"}, - {file = "aiohttp-3.12.13-cp311-cp311-win_amd64.whl", hash = "sha256:9a27da9c3b5ed9d04c36ad2df65b38a96a37e9cfba6f1381b842d05d98e6afe9"}, - {file = "aiohttp-3.12.13-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0aa580cf80558557285b49452151b9c69f2fa3ad94c5c9e76e684719a8791b73"}, - {file = "aiohttp-3.12.13-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b103a7e414b57e6939cc4dece8e282cfb22043efd0c7298044f6594cf83ab347"}, - {file = "aiohttp-3.12.13-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:78f64e748e9e741d2eccff9597d09fb3cd962210e5b5716047cbb646dc8fe06f"}, - {file = "aiohttp-3.12.13-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29c955989bf4c696d2ededc6b0ccb85a73623ae6e112439398935362bacfaaf6"}, - {file = "aiohttp-3.12.13-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d640191016763fab76072c87d8854a19e8e65d7a6fcfcbf017926bdbbb30a7e5"}, - {file = "aiohttp-3.12.13-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4dc507481266b410dede95dd9f26c8d6f5a14315372cc48a6e43eac652237d9b"}, - {file = "aiohttp-3.12.13-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8a94daa873465d518db073bd95d75f14302e0208a08e8c942b2f3f1c07288a75"}, - {file = "aiohttp-3.12.13-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:177f52420cde4ce0bb9425a375d95577fe082cb5721ecb61da3049b55189e4e6"}, - {file = "aiohttp-3.12.13-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0f7df1f620ec40f1a7fbcb99ea17d7326ea6996715e78f71a1c9a021e31b96b8"}, - {file = "aiohttp-3.12.13-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3062d4ad53b36e17796dce1c0d6da0ad27a015c321e663657ba1cc7659cfc710"}, - {file = "aiohttp-3.12.13-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:8605e22d2a86b8e51ffb5253d9045ea73683d92d47c0b1438e11a359bdb94462"}, - {file = "aiohttp-3.12.13-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:54fbbe6beafc2820de71ece2198458a711e224e116efefa01b7969f3e2b3ddae"}, - {file = "aiohttp-3.12.13-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:050bd277dfc3768b606fd4eae79dd58ceda67d8b0b3c565656a89ae34525d15e"}, - {file = "aiohttp-3.12.13-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:2637a60910b58f50f22379b6797466c3aa6ae28a6ab6404e09175ce4955b4e6a"}, - {file = "aiohttp-3.12.13-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e986067357550d1aaa21cfe9897fa19e680110551518a5a7cf44e6c5638cb8b5"}, - {file = "aiohttp-3.12.13-cp312-cp312-win32.whl", hash = "sha256:ac941a80aeea2aaae2875c9500861a3ba356f9ff17b9cb2dbfb5cbf91baaf5bf"}, - {file = "aiohttp-3.12.13-cp312-cp312-win_amd64.whl", hash = "sha256:671f41e6146a749b6c81cb7fd07f5a8356d46febdaaaf07b0e774ff04830461e"}, - {file = "aiohttp-3.12.13-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d4a18e61f271127465bdb0e8ff36e8f02ac4a32a80d8927aa52371e93cd87938"}, - {file = "aiohttp-3.12.13-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:532542cb48691179455fab429cdb0d558b5e5290b033b87478f2aa6af5d20ace"}, - {file = "aiohttp-3.12.13-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d7eea18b52f23c050ae9db5d01f3d264ab08f09e7356d6f68e3f3ac2de9dfabb"}, - {file = "aiohttp-3.12.13-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad7c8e5c25f2a26842a7c239de3f7b6bfb92304593ef997c04ac49fb703ff4d7"}, - {file = "aiohttp-3.12.13-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6af355b483e3fe9d7336d84539fef460120c2f6e50e06c658fe2907c69262d6b"}, - {file = "aiohttp-3.12.13-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a95cf9f097498f35c88e3609f55bb47b28a5ef67f6888f4390b3d73e2bac6177"}, - {file = "aiohttp-3.12.13-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b8ed8c38a1c584fe99a475a8f60eefc0b682ea413a84c6ce769bb19a7ff1c5ef"}, - {file = "aiohttp-3.12.13-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a0b9170d5d800126b5bc89d3053a2363406d6e327afb6afaeda2d19ee8bb103"}, - {file = "aiohttp-3.12.13-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:372feeace612ef8eb41f05ae014a92121a512bd5067db8f25101dd88a8db11da"}, - {file = "aiohttp-3.12.13-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a946d3702f7965d81f7af7ea8fb03bb33fe53d311df48a46eeca17e9e0beed2d"}, - {file = "aiohttp-3.12.13-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:a0c4725fae86555bbb1d4082129e21de7264f4ab14baf735278c974785cd2041"}, - {file = "aiohttp-3.12.13-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:9b28ea2f708234f0a5c44eb6c7d9eb63a148ce3252ba0140d050b091b6e842d1"}, - {file = "aiohttp-3.12.13-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d4f5becd2a5791829f79608c6f3dc745388162376f310eb9c142c985f9441cc1"}, - {file = "aiohttp-3.12.13-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:60f2ce6b944e97649051d5f5cc0f439360690b73909230e107fd45a359d3e911"}, - {file = "aiohttp-3.12.13-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:69fc1909857401b67bf599c793f2183fbc4804717388b0b888f27f9929aa41f3"}, - {file = "aiohttp-3.12.13-cp313-cp313-win32.whl", hash = "sha256:7d7e68787a2046b0e44ba5587aa723ce05d711e3a3665b6b7545328ac8e3c0dd"}, - {file = "aiohttp-3.12.13-cp313-cp313-win_amd64.whl", hash = "sha256:5a178390ca90419bfd41419a809688c368e63c86bd725e1186dd97f6b89c2706"}, - {file = "aiohttp-3.12.13-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:36f6c973e003dc9b0bb4e8492a643641ea8ef0e97ff7aaa5c0f53d68839357b4"}, - {file = "aiohttp-3.12.13-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6cbfc73179bd67c229eb171e2e3745d2afd5c711ccd1e40a68b90427f282eab1"}, - {file = "aiohttp-3.12.13-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1e8b27b2d414f7e3205aa23bb4a692e935ef877e3a71f40d1884f6e04fd7fa74"}, - {file = "aiohttp-3.12.13-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eabded0c2b2ef56243289112c48556c395d70150ce4220d9008e6b4b3dd15690"}, - {file = "aiohttp-3.12.13-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:003038e83f1a3ff97409999995ec02fe3008a1d675478949643281141f54751d"}, - {file = "aiohttp-3.12.13-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1b6f46613031dbc92bdcaad9c4c22c7209236ec501f9c0c5f5f0b6a689bf50f3"}, - {file = "aiohttp-3.12.13-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c332c6bb04650d59fb94ed96491f43812549a3ba6e7a16a218e612f99f04145e"}, - {file = "aiohttp-3.12.13-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3fea41a2c931fb582cb15dc86a3037329e7b941df52b487a9f8b5aa960153cbd"}, - {file = "aiohttp-3.12.13-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:846104f45d18fb390efd9b422b27d8f3cf8853f1218c537f36e71a385758c896"}, - {file = "aiohttp-3.12.13-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5d6c85ac7dd350f8da2520bac8205ce99df4435b399fa7f4dc4a70407073e390"}, - {file = "aiohttp-3.12.13-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:5a1ecce0ed281bec7da8550da052a6b89552db14d0a0a45554156f085a912f48"}, - {file = "aiohttp-3.12.13-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:5304d74867028cca8f64f1cc1215eb365388033c5a691ea7aa6b0dc47412f495"}, - {file = "aiohttp-3.12.13-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:64d1f24ee95a2d1e094a4cd7a9b7d34d08db1bbcb8aa9fb717046b0a884ac294"}, - {file = "aiohttp-3.12.13-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:119c79922a7001ca6a9e253228eb39b793ea994fd2eccb79481c64b5f9d2a055"}, - {file = "aiohttp-3.12.13-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:bb18f00396d22e2f10cd8825d671d9f9a3ba968d708a559c02a627536b36d91c"}, - {file = "aiohttp-3.12.13-cp39-cp39-win32.whl", hash = "sha256:0022de47ef63fd06b065d430ac79c6b0bd24cdae7feaf0e8c6bac23b805a23a8"}, - {file = "aiohttp-3.12.13-cp39-cp39-win_amd64.whl", hash = "sha256:29e08111ccf81b2734ae03f1ad1cb03b9615e7d8f616764f22f71209c094f122"}, - {file = "aiohttp-3.12.13.tar.gz", hash = "sha256:47e2da578528264a12e4e3dd8dd72a7289e5f812758fe086473fab037a10fcce"}, + {file = "aiohttp-3.13.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ca69ec38adf5cadcc21d0b25e2144f6a25b7db7bea7e730bac25075bc305eff0"}, + {file = "aiohttp-3.13.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:240f99f88a9a6beb53ebadac79a2e3417247aa756202ed234b1dbae13d248092"}, + {file = "aiohttp-3.13.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a4676b978a9711531e7cea499d4cdc0794c617a1c0579310ab46c9fdf5877702"}, + {file = "aiohttp-3.13.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:48fcdd5bc771cbbab8ccc9588b8b6447f6a30f9fe00898b1a5107098e00d6793"}, + {file = "aiohttp-3.13.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:eeea0cdd2f687e210c8f605f322d7b0300ba55145014a5dbe98bd4be6fff1f6c"}, + {file = "aiohttp-3.13.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:10b3f01d5aeb632adaaf39c5e93f040a550464a768d54c514050c635adcbb9d0"}, + {file = "aiohttp-3.13.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a4dc0b83e25267f42ef065ea57653de4365b56d7bc4e4cfc94fabe56998f8ee6"}, + {file = "aiohttp-3.13.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:72714919ed9b90f030f761c20670e529c4af96c31bd000917dd0c9afd1afb731"}, + {file = "aiohttp-3.13.0-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:564be41e85318403fdb176e9e5b3e852d528392f42f2c1d1efcbeeed481126d7"}, + {file = "aiohttp-3.13.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:84912962071087286333f70569362e10793f73f45c48854e6859df11001eb2d3"}, + {file = "aiohttp-3.13.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:90b570f1a146181c3d6ae8f755de66227ded49d30d050479b5ae07710f7894c5"}, + {file = "aiohttp-3.13.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:2d71ca30257ce756e37a6078b1dff2d9475fee13609ad831eac9a6531bea903b"}, + {file = "aiohttp-3.13.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:cd45eb70eca63f41bb156b7dffbe1a7760153b69892d923bdb79a74099e2ed90"}, + {file = "aiohttp-3.13.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:5ae3a19949a27982c7425a7a5a963c1268fdbabf0be15ab59448cbcf0f992519"}, + {file = "aiohttp-3.13.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ea6df292013c9f050cbf3f93eee9953d6e5acd9e64a0bf4ca16404bfd7aa9bcc"}, + {file = "aiohttp-3.13.0-cp310-cp310-win32.whl", hash = "sha256:3b64f22fbb6dcd5663de5ef2d847a5638646ef99112503e6f7704bdecb0d1c4d"}, + {file = "aiohttp-3.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:f8d877aa60d80715b2afc565f0f1aea66565824c229a2d065b31670e09fed6d7"}, + {file = "aiohttp-3.13.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:99eb94e97a42367fef5fc11e28cb2362809d3e70837f6e60557816c7106e2e20"}, + {file = "aiohttp-3.13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4696665b2713021c6eba3e2b882a86013763b442577fe5d2056a42111e732eca"}, + {file = "aiohttp-3.13.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3e6a38366f7f0d0f6ed7a1198055150c52fda552b107dad4785c0852ad7685d1"}, + {file = "aiohttp-3.13.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aab715b1a0c37f7f11f9f1f579c6fbaa51ef569e47e3c0a4644fba46077a9409"}, + {file = "aiohttp-3.13.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7972c82bed87d7bd8e374b60a6b6e816d75ba4f7c2627c2d14eed216e62738e1"}, + {file = "aiohttp-3.13.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ca8313cb852af788c78d5afdea24c40172cbfff8b35e58b407467732fde20390"}, + {file = "aiohttp-3.13.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6c333a2385d2a6298265f4b3e960590f787311b87f6b5e6e21bb8375914ef504"}, + {file = "aiohttp-3.13.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cc6d5fc5edbfb8041d9607f6a417997fa4d02de78284d386bea7ab767b5ea4f3"}, + {file = "aiohttp-3.13.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7ddedba3d0043349edc79df3dc2da49c72b06d59a45a42c1c8d987e6b8d175b8"}, + {file = "aiohttp-3.13.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:23ca762140159417a6bbc959ca1927f6949711851e56f2181ddfe8d63512b5ad"}, + {file = "aiohttp-3.13.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:bfe824d6707a5dc3c5676685f624bc0c63c40d79dc0239a7fd6c034b98c25ebe"}, + {file = "aiohttp-3.13.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:3c11fa5dd2ef773a8a5a6daa40243d83b450915992eab021789498dc87acc114"}, + {file = "aiohttp-3.13.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:00fdfe370cffede3163ba9d3f190b32c0cfc8c774f6f67395683d7b0e48cdb8a"}, + {file = "aiohttp-3.13.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:6475e42ef92717a678bfbf50885a682bb360a6f9c8819fb1a388d98198fdcb80"}, + {file = "aiohttp-3.13.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:77da5305a410910218b99f2a963092f4277d8a9c1f429c1ff1b026d1826bd0b6"}, + {file = "aiohttp-3.13.0-cp311-cp311-win32.whl", hash = "sha256:2f9d9ea547618d907f2ee6670c9a951f059c5994e4b6de8dcf7d9747b420c820"}, + {file = "aiohttp-3.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:0f19f7798996d4458c669bd770504f710014926e9970f4729cf55853ae200469"}, + {file = "aiohttp-3.13.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1c272a9a18a5ecc48a7101882230046b83023bb2a662050ecb9bfcb28d9ab53a"}, + {file = "aiohttp-3.13.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:97891a23d7fd4e1afe9c2f4473e04595e4acb18e4733b910b6577b74e7e21985"}, + {file = "aiohttp-3.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:475bd56492ce5f4cffe32b5533c6533ee0c406d1d0e6924879f83adcf51da0ae"}, + {file = "aiohttp-3.13.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c32ada0abb4bc94c30be2b681c42f058ab104d048da6f0148280a51ce98add8c"}, + {file = "aiohttp-3.13.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:4af1f8877ca46ecdd0bc0d4a6b66d4b2bddc84a79e2e8366bc0d5308e76bceb8"}, + {file = "aiohttp-3.13.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e04ab827ec4f775817736b20cdc8350f40327f9b598dec4e18c9ffdcbea88a93"}, + {file = "aiohttp-3.13.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a6d9487b9471ec36b0faedf52228cd732e89be0a2bbd649af890b5e2ce422353"}, + {file = "aiohttp-3.13.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e66c57416352f36bf98f6641ddadd47c93740a22af7150d3e9a1ef6e983f9a8"}, + {file = "aiohttp-3.13.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:469167d5372f5bb3aedff4fc53035d593884fff2617a75317740e885acd48b04"}, + {file = "aiohttp-3.13.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a9f3546b503975a69b547c9fd1582cad10ede1ce6f3e313a2f547c73a3d7814f"}, + {file = "aiohttp-3.13.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:6b4174fcec98601f0cfdf308ee29a6ae53c55f14359e848dab4e94009112ee7d"}, + {file = "aiohttp-3.13.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a533873a7a4ec2270fb362ee5a0d3b98752e4e1dc9042b257cd54545a96bd8ed"}, + {file = "aiohttp-3.13.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:ce887c5e54411d607ee0959cac15bb31d506d86a9bcaddf0b7e9d63325a7a802"}, + {file = "aiohttp-3.13.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:d871f6a30d43e32fc9252dc7b9febe1a042b3ff3908aa83868d7cf7c9579a59b"}, + {file = "aiohttp-3.13.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:222c828243b4789d79a706a876910f656fad4381661691220ba57b2ab4547865"}, + {file = "aiohttp-3.13.0-cp312-cp312-win32.whl", hash = "sha256:682d2e434ff2f1108314ff7f056ce44e457f12dbed0249b24e106e385cf154b9"}, + {file = "aiohttp-3.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:0a2be20eb23888df130214b91c262a90e2de1553d6fb7de9e9010cec994c0ff2"}, + {file = "aiohttp-3.13.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:00243e51f16f6ec0fb021659d4af92f675f3cf9f9b39efd142aa3ad641d8d1e6"}, + {file = "aiohttp-3.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:059978d2fddc462e9211362cbc8446747ecd930537fa559d3d25c256f032ff54"}, + {file = "aiohttp-3.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:564b36512a7da3b386143c611867e3f7cfb249300a1bf60889bd9985da67ab77"}, + {file = "aiohttp-3.13.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4aa995b9156ae499393d949a456a7ab0b994a8241a96db73a3b73c7a090eff6a"}, + {file = "aiohttp-3.13.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:55ca0e95a3905f62f00900255ed807c580775174252999286f283e646d675a49"}, + {file = "aiohttp-3.13.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:49ce7525853a981fc35d380aa2353536a01a9ec1b30979ea4e35966316cace7e"}, + {file = "aiohttp-3.13.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2117be9883501eaf95503bd313eb4c7a23d567edd44014ba15835a1e9ec6d852"}, + {file = "aiohttp-3.13.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d169c47e40c911f728439da853b6fd06da83761012e6e76f11cb62cddae7282b"}, + {file = "aiohttp-3.13.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:703ad3f742fc81e543638a7bebddd35acadaa0004a5e00535e795f4b6f2c25ca"}, + {file = "aiohttp-3.13.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5bf635c3476f4119b940cc8d94ad454cbe0c377e61b4527f0192aabeac1e9370"}, + {file = "aiohttp-3.13.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:cfe6285ef99e7ee51cef20609be2bc1dd0e8446462b71c9db8bb296ba632810a"}, + {file = "aiohttp-3.13.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:34d8af6391c5f2e69749d7f037b614b8c5c42093c251f336bdbfa4b03c57d6c4"}, + {file = "aiohttp-3.13.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:12f5d820fadc5848d4559ea838aef733cf37ed2a1103bba148ac2f5547c14c29"}, + {file = "aiohttp-3.13.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:0f1338b61ea66f4757a0544ed8a02ccbf60e38d9cfb3225888888dd4475ebb96"}, + {file = "aiohttp-3.13.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:582770f82513419512da096e8df21ca44f86a2e56e25dc93c5ab4df0fe065bf0"}, + {file = "aiohttp-3.13.0-cp313-cp313-win32.whl", hash = "sha256:3194b8cab8dbc882f37c13ef1262e0a3d62064fa97533d3aa124771f7bf1ecee"}, + {file = "aiohttp-3.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:7897298b3eedc790257fef8a6ec582ca04e9dbe568ba4a9a890913b925b8ea21"}, + {file = "aiohttp-3.13.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:c417f8c2e1137775569297c584a8a7144e5d1237789eae56af4faf1894a0b861"}, + {file = "aiohttp-3.13.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:f84b53326abf8e56ebc28a35cebf4a0f396a13a76300f500ab11fe0573bf0b52"}, + {file = "aiohttp-3.13.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:990a53b9d6a30b2878789e490758e568b12b4a7fb2527d0c89deb9650b0e5813"}, + {file = "aiohttp-3.13.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c811612711e01b901e18964b3e5dec0d35525150f5f3f85d0aee2935f059910a"}, + {file = "aiohttp-3.13.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:ee433e594d7948e760b5c2a78cc06ac219df33b0848793cf9513d486a9f90a52"}, + {file = "aiohttp-3.13.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:19bb08e56f57c215e9572cd65cb6f8097804412c54081d933997ddde3e5ac579"}, + {file = "aiohttp-3.13.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f27b7488144eb5dd9151cf839b195edd1569629d90ace4c5b6b18e4e75d1e63a"}, + {file = "aiohttp-3.13.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d812838c109757a11354a161c95708ae4199c4fd4d82b90959b20914c1d097f6"}, + {file = "aiohttp-3.13.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7c20db99da682f9180fa5195c90b80b159632fb611e8dbccdd99ba0be0970620"}, + {file = "aiohttp-3.13.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:cf8b0870047900eb1f17f453b4b3953b8ffbf203ef56c2f346780ff930a4d430"}, + {file = "aiohttp-3.13.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:5b8a5557d5af3f4e3add52a58c4cf2b8e6e59fc56b261768866f5337872d596d"}, + {file = "aiohttp-3.13.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:052bcdd80c1c54b8a18a9ea0cd5e36f473dc8e38d51b804cea34841f677a9971"}, + {file = "aiohttp-3.13.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:76484ba17b2832776581b7ab466d094e48eba74cb65a60aea20154dae485e8bd"}, + {file = "aiohttp-3.13.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:62d8a0adcdaf62ee56bfb37737153251ac8e4b27845b3ca065862fb01d99e247"}, + {file = "aiohttp-3.13.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5004d727499ecb95f7c9147dd0bfc5b5670f71d355f0bd26d7af2d3af8e07d2f"}, + {file = "aiohttp-3.13.0-cp314-cp314-win32.whl", hash = "sha256:a1c20c26af48aea984f63f96e5d7af7567c32cb527e33b60a0ef0a6313cf8b03"}, + {file = "aiohttp-3.13.0-cp314-cp314-win_amd64.whl", hash = "sha256:56f7d230ec66e799fbfd8350e9544f8a45a4353f1cf40c1fea74c1780f555b8f"}, + {file = "aiohttp-3.13.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:2fd35177dc483ae702f07b86c782f4f4b100a8ce4e7c5778cea016979023d9fd"}, + {file = "aiohttp-3.13.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:4df1984c8804ed336089e88ac81a9417b1fd0db7c6f867c50a9264488797e778"}, + {file = "aiohttp-3.13.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:e68c0076052dd911a81d3acc4ef2911cc4ef65bf7cadbfbc8ae762da24da858f"}, + {file = "aiohttp-3.13.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bc95c49853cd29613e4fe4ff96d73068ff89b89d61e53988442e127e8da8e7ba"}, + {file = "aiohttp-3.13.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3b3bdc89413117b40cc39baae08fd09cbdeb839d421c4e7dce6a34f6b54b3ac1"}, + {file = "aiohttp-3.13.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3e77a729df23be2116acc4e9de2767d8e92445fbca68886dd991dc912f473755"}, + {file = "aiohttp-3.13.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e88ab34826d6eeb6c67e6e92400b9ec653faf5092a35f07465f44c9f1c429f82"}, + {file = "aiohttp-3.13.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:019dbef24fe28ce2301419dd63a2b97250d9760ca63ee2976c2da2e3f182f82e"}, + {file = "aiohttp-3.13.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:2c4aeaedd20771b7b4bcdf0ae791904445df6d856c02fc51d809d12d17cffdc7"}, + {file = "aiohttp-3.13.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:b3a8e6a2058a0240cfde542b641d0e78b594311bc1a710cbcb2e1841417d5cb3"}, + {file = "aiohttp-3.13.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:f8e38d55ca36c15f36d814ea414ecb2401d860de177c49f84a327a25b3ee752b"}, + {file = "aiohttp-3.13.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:a921edbe971aade1bf45bcbb3494e30ba6863a5c78f28be992c42de980fd9108"}, + {file = "aiohttp-3.13.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:474cade59a447cb4019c0dce9f0434bf835fb558ea932f62c686fe07fe6db6a1"}, + {file = "aiohttp-3.13.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:99a303ad960747c33b65b1cb65d01a62ac73fa39b72f08a2e1efa832529b01ed"}, + {file = "aiohttp-3.13.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:bb34001fc1f05f6b323e02c278090c07a47645caae3aa77ed7ed8a3ce6abcce9"}, + {file = "aiohttp-3.13.0-cp314-cp314t-win32.whl", hash = "sha256:dea698b64235d053def7d2f08af9302a69fcd760d1c7bd9988fd5d3b6157e657"}, + {file = "aiohttp-3.13.0-cp314-cp314t-win_amd64.whl", hash = "sha256:1f164699a060c0b3616459d13c1464a981fddf36f892f0a5027cbd45121fb14b"}, + {file = "aiohttp-3.13.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:fcc425fb6fd2a00c6d91c85d084c6b75a61bc8bc12159d08e17c5711df6c5ba4"}, + {file = "aiohttp-3.13.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7c2c4c9ce834801651f81d6760d0a51035b8b239f58f298de25162fcf6f8bb64"}, + {file = "aiohttp-3.13.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f91e8f9053a07177868e813656ec57599cd2a63238844393cd01bd69c2e40147"}, + {file = "aiohttp-3.13.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:df46d9a3d78ec19b495b1107bf26e4fcf97c900279901f4f4819ac5bb2a02a4c"}, + {file = "aiohttp-3.13.0-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3b1eb9871cbe43b6ca6fac3544682971539d8a1d229e6babe43446279679609d"}, + {file = "aiohttp-3.13.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:62a3cddf8d9a2eae1f79585fa81d32e13d0c509bb9e7ad47d33c83b45a944df7"}, + {file = "aiohttp-3.13.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0f735e680c323ee7e9ef8e2ea26425c7dbc2ede0086fa83ce9d7ccab8a089f26"}, + {file = "aiohttp-3.13.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6a51839f778b0e283b43cd82bb17f1835ee2cc1bf1101765e90ae886e53e751c"}, + {file = "aiohttp-3.13.0-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ac90cfab65bc281d6752f22db5fa90419e33220af4b4fa53b51f5948f414c0e7"}, + {file = "aiohttp-3.13.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:62fd54f3e6f17976962ba67f911d62723c760a69d54f5d7b74c3ceb1a4e9ef8d"}, + {file = "aiohttp-3.13.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:cf2b60b65df05b6b2fa0d887f2189991a0dbf44a0dd18359001dc8fcdb7f1163"}, + {file = "aiohttp-3.13.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:1ccedfe280e804d9a9d7fe8b8c4309d28e364b77f40309c86596baa754af50b1"}, + {file = "aiohttp-3.13.0-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:ea01ffbe23df53ece0c8732d1585b3d6079bb8c9ee14f3745daf000051415a31"}, + {file = "aiohttp-3.13.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:19ba8625fa69523627b67f7e9901b587a4952470f68814d79cdc5bc460e9b885"}, + {file = "aiohttp-3.13.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:4b14bfae90598d331b5061fd15a7c290ea0c15b34aeb1cf620464bb5ec02a602"}, + {file = "aiohttp-3.13.0-cp39-cp39-win32.whl", hash = "sha256:cf7a4b976da219e726d0043fc94ae8169c0dba1d3a059b3c1e2c964bafc5a77d"}, + {file = "aiohttp-3.13.0-cp39-cp39-win_amd64.whl", hash = "sha256:6b9697d15231aeaed4786f090c9c8bc3ab5f0e0a6da1e76c135a310def271020"}, + {file = "aiohttp-3.13.0.tar.gz", hash = "sha256:378dbc57dd8cf341ce243f13fa1fa5394d68e2e02c15cd5f28eae35a70ec7f67"}, ] [package.dependencies] aiohappyeyeballs = ">=2.5.0" -aiosignal = ">=1.1.2" +aiosignal = ">=1.4.0" attrs = ">=17.3.0" frozenlist = ">=1.1.1" multidict = ">=4.5,<7.0" @@ -118,22 +152,23 @@ propcache = ">=0.2.0" yarl = ">=1.17.0,<2.0" [package.extras] -speedups = ["Brotli", "aiodns (>=3.3.0)", "brotlicffi"] +speedups = ["Brotli", "aiodns (>=3.3.0)", "brotlicffi", "zstandard"] [[package]] name = "aiosignal" -version = "1.3.2" +version = "1.4.0" description = "aiosignal: a list of registered asynchronous callbacks" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "aiosignal-1.3.2-py2.py3-none-any.whl", hash = "sha256:45cde58e409a301715980c2b01d0c28bdde3770d8290b5eb2173759d9acb31a5"}, - {file = "aiosignal-1.3.2.tar.gz", hash = "sha256:a8c255c66fafb1e499c9351d0bf32ff2d8a0321595ebac3b93713656d2436f54"}, + {file = "aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e"}, + {file = "aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7"}, ] [package.dependencies] frozenlist = ">=1.1.0" +typing-extensions = {version = ">=4.2", markers = "python_version < \"3.13\""} [[package]] name = "annotated-types" @@ -149,14 +184,14 @@ files = [ [[package]] name = "anyio" -version = "4.8.0" -description = "High level compatibility layer for multiple asynchronous event loop implementations" +version = "4.11.0" +description = "High-level concurrency and networking framework on top of asyncio or Trio" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "anyio-4.8.0-py3-none-any.whl", hash = "sha256:b5011f270ab5eb0abf13385f851315585cc37ef330dd88e27ec3d34d651fd47a"}, - {file = "anyio-4.8.0.tar.gz", hash = "sha256:1d9fe889df5212298c0c0723fa20479d1b94883a2df44bd3897aa91083316f7a"}, + {file = "anyio-4.11.0-py3-none-any.whl", hash = "sha256:0287e96f4d26d4149305414d4e3bc32f0dcd0862365a4bddea19d7a1ec38c4fc"}, + {file = "anyio-4.11.0.tar.gz", hash = "sha256:82a8d0b81e318cc5ce71a5f1f8b5c4e63619620b63141ef8c995fa0db95a57c4"}, ] [package.dependencies] @@ -165,293 +200,361 @@ sniffio = ">=1.1" typing_extensions = {version = ">=4.5", markers = "python_version < \"3.13\""} [package.extras] -doc = ["Sphinx (>=7.4,<8.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx_rtd_theme"] -test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "trustme", "truststore (>=0.9.1)", "uvloop (>=0.21)"] -trio = ["trio (>=0.26.1)"] +trio = ["trio (>=0.31.0)"] [[package]] name = "argon2-cffi" -version = "23.1.0" +version = "25.1.0" description = "Argon2 for Python" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" groups = ["main"] files = [ - {file = "argon2_cffi-23.1.0-py3-none-any.whl", hash = "sha256:c670642b78ba29641818ab2e68bd4e6a78ba53b7eff7b4c3815ae16abf91c7ea"}, - {file = "argon2_cffi-23.1.0.tar.gz", hash = "sha256:879c3e79a2729ce768ebb7d36d4609e3a78a4ca2ec3a9f12286ca057e3d0db08"}, + {file = "argon2_cffi-25.1.0-py3-none-any.whl", hash = "sha256:fdc8b074db390fccb6eb4a3604ae7231f219aa669a2652e0f20e16ba513d5741"}, + {file = "argon2_cffi-25.1.0.tar.gz", hash = "sha256:694ae5cc8a42f4c4e2bf2ca0e64e51e23a040c6a517a85074683d3959e1346c1"}, ] [package.dependencies] argon2-cffi-bindings = "*" -[package.extras] -dev = ["argon2-cffi[tests,typing]", "tox (>4)"] -docs = ["furo", "myst-parser", "sphinx", "sphinx-copybutton", "sphinx-notfound-page"] -tests = ["hypothesis", "pytest"] -typing = ["mypy"] - [[package]] name = "argon2-cffi-bindings" -version = "21.2.0" +version = "25.1.0" description = "Low-level CFFI bindings for Argon2" optional = false -python-versions = ">=3.6" +python-versions = ">=3.9" groups = ["main"] files = [ - {file = "argon2-cffi-bindings-21.2.0.tar.gz", hash = "sha256:bb89ceffa6c791807d1305ceb77dbfacc5aa499891d2c55661c6459651fc39e3"}, - {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ccb949252cb2ab3a08c02024acb77cfb179492d5701c7cbdbfd776124d4d2367"}, - {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9524464572e12979364b7d600abf96181d3541da11e23ddf565a32e70bd4dc0d"}, - {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b746dba803a79238e925d9046a63aa26bf86ab2a2fe74ce6b009a1c3f5c8f2ae"}, - {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:58ed19212051f49a523abb1dbe954337dc82d947fb6e5a0da60f7c8471a8476c"}, - {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:bd46088725ef7f58b5a1ef7ca06647ebaf0eb4baff7d1d0d177c6cc8744abd86"}, - {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_i686.whl", hash = "sha256:8cd69c07dd875537a824deec19f978e0f2078fdda07fd5c42ac29668dda5f40f"}, - {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:f1152ac548bd5b8bcecfb0b0371f082037e47128653df2e8ba6e914d384f3c3e"}, - {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-win32.whl", hash = "sha256:603ca0aba86b1349b147cab91ae970c63118a0f30444d4bc80355937c950c082"}, - {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-win_amd64.whl", hash = "sha256:b2ef1c30440dbbcba7a5dc3e319408b59676e2e039e2ae11a8775ecf482b192f"}, - {file = "argon2_cffi_bindings-21.2.0-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:e415e3f62c8d124ee16018e491a009937f8cf7ebf5eb430ffc5de21b900dad93"}, - {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3e385d1c39c520c08b53d63300c3ecc28622f076f4c2b0e6d7e796e9f6502194"}, - {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c3e3cc67fdb7d82c4718f19b4e7a87123caf8a93fde7e23cf66ac0337d3cb3f"}, - {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6a22ad9800121b71099d0fb0a65323810a15f2e292f2ba450810a7316e128ee5"}, - {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f9f8b450ed0547e3d473fdc8612083fd08dd2120d6ac8f73828df9b7d45bb351"}, - {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:93f9bf70084f97245ba10ee36575f0c3f1e7d7724d67d8e5b08e61787c320ed7"}, - {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3b9ef65804859d335dc6b31582cad2c5166f0c3e7975f324d9ffaa34ee7e6583"}, - {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4966ef5848d820776f5f562a7d45fdd70c2f330c961d0d745b784034bd9f48d"}, - {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20ef543a89dee4db46a1a6e206cd015360e5a75822f76df533845c3cbaf72670"}, - {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed2937d286e2ad0cc79a7087d3c272832865f779430e0cc2b4f3718d3159b0cb"}, - {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:5e00316dabdaea0b2dd82d141cc66889ced0cdcbfa599e8b471cf22c620c329a"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:3d3f05610594151994ca9ccb3c771115bdb4daef161976a266f0dd8aa9996b8f"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:8b8efee945193e667a396cbc7b4fb7d357297d6234d30a489905d96caabde56b"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:3c6702abc36bf3ccba3f802b799505def420a1b7039862014a65db3205967f5a"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a1c70058c6ab1e352304ac7e3b52554daadacd8d453c1752e547c76e9c99ac44"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e2fd3bfbff3c5d74fef31a722f729bf93500910db650c925c2d6ef879a7e51cb"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c4f9665de60b1b0e99bcd6be4f17d90339698ce954cfd8d9cf4f91c995165a92"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ba92837e4a9aa6a508c8d2d7883ed5a8f6c308c89a4790e1e447a220deb79a85"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-win32.whl", hash = "sha256:84a461d4d84ae1295871329b346a97f68eade8c53b6ed9a7ca2d7467f3c8ff6f"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b55aec3565b65f56455eebc9b9f34130440404f27fe21c3b375bf1ea4d8fbae6"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:87c33a52407e4c41f3b70a9c2d3f6056d88b10dad7695be708c5021673f55623"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:aecba1723ae35330a008418a91ea6cfcedf6d31e5fbaa056a166462ff066d500"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:2630b6240b495dfab90aebe159ff784d08ea999aa4b0d17efa734055a07d2f44"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:7aef0c91e2c0fbca6fc68e7555aa60ef7008a739cbe045541e438373bc54d2b0"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e021e87faa76ae0d413b619fe2b65ab9a037f24c60a1e6cc43457ae20de6dc6"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d3e924cfc503018a714f94a49a149fdc0b644eaead5d1f089330399134fa028a"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:c87b72589133f0346a1cb8d5ecca4b933e3c9b64656c9d175270a000e73b288d"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1db89609c06afa1a214a69a462ea741cf735b29a57530478c06eb81dd403de99"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-win32.whl", hash = "sha256:473bcb5f82924b1becbb637b63303ec8d10e84c8d241119419897a26116515d2"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-win_amd64.whl", hash = "sha256:a98cd7d17e9f7ce244c0803cad3c23a7d379c301ba618a5fa76a67d116618b98"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-win_arm64.whl", hash = "sha256:b0fdbcf513833809c882823f98dc2f931cf659d9a1429616ac3adebb49f5db94"}, + {file = "argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:6dca33a9859abf613e22733131fc9194091c1fa7cb3e131c143056b4856aa47e"}, + {file = "argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:21378b40e1b8d1655dd5310c84a40fc19a9aa5e6366e835ceb8576bf0fea716d"}, + {file = "argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d588dec224e2a83edbdc785a5e6f3c6cd736f46bfd4b441bbb5aa1f5085e584"}, + {file = "argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5acb4e41090d53f17ca1110c3427f0a130f944b896fc8c83973219c97f57b690"}, + {file = "argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:da0c79c23a63723aa5d782250fbf51b768abca630285262fb5144ba5ae01e520"}, + {file = "argon2_cffi_bindings-25.1.0.tar.gz", hash = "sha256:b957f3e6ea4d55d820e40ff76f450952807013d361a65d7f28acc0acbf29229d"}, ] [package.dependencies] -cffi = ">=1.0.1" - -[package.extras] -dev = ["cogapp", "pre-commit", "pytest", "wheel"] -tests = ["pytest"] +cffi = [ + {version = ">=1.0.1", markers = "python_version < \"3.14\""}, + {version = ">=2.0.0b1", markers = "python_version >= \"3.14\""}, +] [[package]] -name = "attrs" -version = "25.1.0" -description = "Classes Without Boilerplate" +name = "asyncio" +version = "4.0.0" +description = "Deprecated backport of asyncio; use the stdlib package instead" optional = false -python-versions = ">=3.8" +python-versions = ">=3.4" groups = ["main"] files = [ - {file = "attrs-25.1.0-py3-none-any.whl", hash = "sha256:c75a69e28a550a7e93789579c22aa26b0f5b83b75dc4e08fe092980051e1090a"}, - {file = "attrs-25.1.0.tar.gz", hash = "sha256:1c97078a80c814273a76b2a298a932eb681c87415c11dee0a6921de7f1b02c3e"}, + {file = "asyncio-4.0.0-py3-none-any.whl", hash = "sha256:c1eddb0659231837046809e68103969b2bef8b0400d59cfa6363f6b5ed8cc88b"}, + {file = "asyncio-4.0.0.tar.gz", hash = "sha256:570cd9e50db83bc1629152d4d0b7558d6451bb1bfd5dfc2e935d96fc2f40329b"}, +] + +[[package]] +name = "asyncpg" +version = "0.30.0" +description = "An asyncio PostgreSQL driver" +optional = false +python-versions = ">=3.8.0" +groups = ["main"] +files = [ + {file = "asyncpg-0.30.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bfb4dd5ae0699bad2b233672c8fc5ccbd9ad24b89afded02341786887e37927e"}, + {file = "asyncpg-0.30.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dc1f62c792752a49f88b7e6f774c26077091b44caceb1983509edc18a2222ec0"}, + {file = "asyncpg-0.30.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3152fef2e265c9c24eec4ee3d22b4f4d2703d30614b0b6753e9ed4115c8a146f"}, + {file = "asyncpg-0.30.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c7255812ac85099a0e1ffb81b10dc477b9973345793776b128a23e60148dd1af"}, + {file = "asyncpg-0.30.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:578445f09f45d1ad7abddbff2a3c7f7c291738fdae0abffbeb737d3fc3ab8b75"}, + {file = "asyncpg-0.30.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c42f6bb65a277ce4d93f3fba46b91a265631c8df7250592dd4f11f8b0152150f"}, + {file = "asyncpg-0.30.0-cp310-cp310-win32.whl", hash = "sha256:aa403147d3e07a267ada2ae34dfc9324e67ccc4cdca35261c8c22792ba2b10cf"}, + {file = "asyncpg-0.30.0-cp310-cp310-win_amd64.whl", hash = "sha256:fb622c94db4e13137c4c7f98834185049cc50ee01d8f657ef898b6407c7b9c50"}, + {file = "asyncpg-0.30.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5e0511ad3dec5f6b4f7a9e063591d407eee66b88c14e2ea636f187da1dcfff6a"}, + {file = "asyncpg-0.30.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:915aeb9f79316b43c3207363af12d0e6fd10776641a7de8a01212afd95bdf0ed"}, + {file = "asyncpg-0.30.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c198a00cce9506fcd0bf219a799f38ac7a237745e1d27f0e1f66d3707c84a5a"}, + {file = "asyncpg-0.30.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3326e6d7381799e9735ca2ec9fd7be4d5fef5dcbc3cb555d8a463d8460607956"}, + {file = "asyncpg-0.30.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:51da377487e249e35bd0859661f6ee2b81db11ad1f4fc036194bc9cb2ead5056"}, + {file = "asyncpg-0.30.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bc6d84136f9c4d24d358f3b02be4b6ba358abd09f80737d1ac7c444f36108454"}, + {file = "asyncpg-0.30.0-cp311-cp311-win32.whl", hash = "sha256:574156480df14f64c2d76450a3f3aaaf26105869cad3865041156b38459e935d"}, + {file = "asyncpg-0.30.0-cp311-cp311-win_amd64.whl", hash = "sha256:3356637f0bd830407b5597317b3cb3571387ae52ddc3bca6233682be88bbbc1f"}, + {file = "asyncpg-0.30.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c902a60b52e506d38d7e80e0dd5399f657220f24635fee368117b8b5fce1142e"}, + {file = "asyncpg-0.30.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:aca1548e43bbb9f0f627a04666fedaca23db0a31a84136ad1f868cb15deb6e3a"}, + {file = "asyncpg-0.30.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c2a2ef565400234a633da0eafdce27e843836256d40705d83ab7ec42074efb3"}, + {file = "asyncpg-0.30.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1292b84ee06ac8a2ad8e51c7475aa309245874b61333d97411aab835c4a2f737"}, + {file = "asyncpg-0.30.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0f5712350388d0cd0615caec629ad53c81e506b1abaaf8d14c93f54b35e3595a"}, + {file = "asyncpg-0.30.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:db9891e2d76e6f425746c5d2da01921e9a16b5a71a1c905b13f30e12a257c4af"}, + {file = "asyncpg-0.30.0-cp312-cp312-win32.whl", hash = "sha256:68d71a1be3d83d0570049cd1654a9bdfe506e794ecc98ad0873304a9f35e411e"}, + {file = "asyncpg-0.30.0-cp312-cp312-win_amd64.whl", hash = "sha256:9a0292c6af5c500523949155ec17b7fe01a00ace33b68a476d6b5059f9630305"}, + {file = "asyncpg-0.30.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:05b185ebb8083c8568ea8a40e896d5f7af4b8554b64d7719c0eaa1eb5a5c3a70"}, + {file = "asyncpg-0.30.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c47806b1a8cbb0a0db896f4cd34d89942effe353a5035c62734ab13b9f938da3"}, + {file = "asyncpg-0.30.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b6fde867a74e8c76c71e2f64f80c64c0f3163e687f1763cfaf21633ec24ec33"}, + {file = "asyncpg-0.30.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46973045b567972128a27d40001124fbc821c87a6cade040cfcd4fa8a30bcdc4"}, + {file = "asyncpg-0.30.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9110df111cabc2ed81aad2f35394a00cadf4f2e0635603db6ebbd0fc896f46a4"}, + {file = "asyncpg-0.30.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:04ff0785ae7eed6cc138e73fc67b8e51d54ee7a3ce9b63666ce55a0bf095f7ba"}, + {file = "asyncpg-0.30.0-cp313-cp313-win32.whl", hash = "sha256:ae374585f51c2b444510cdf3595b97ece4f233fde739aa14b50e0d64e8a7a590"}, + {file = "asyncpg-0.30.0-cp313-cp313-win_amd64.whl", hash = "sha256:f59b430b8e27557c3fb9869222559f7417ced18688375825f8f12302c34e915e"}, + {file = "asyncpg-0.30.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:29ff1fc8b5bf724273782ff8b4f57b0f8220a1b2324184846b39d1ab4122031d"}, + {file = "asyncpg-0.30.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:64e899bce0600871b55368b8483e5e3e7f1860c9482e7f12e0a771e747988168"}, + {file = "asyncpg-0.30.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b290f4726a887f75dcd1b3006f484252db37602313f806e9ffc4e5996cfe5cb"}, + {file = "asyncpg-0.30.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f86b0e2cd3f1249d6fe6fd6cfe0cd4538ba994e2d8249c0491925629b9104d0f"}, + {file = "asyncpg-0.30.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:393af4e3214c8fa4c7b86da6364384c0d1b3298d45803375572f415b6f673f38"}, + {file = "asyncpg-0.30.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:fd4406d09208d5b4a14db9a9dbb311b6d7aeeab57bded7ed2f8ea41aeef39b34"}, + {file = "asyncpg-0.30.0-cp38-cp38-win32.whl", hash = "sha256:0b448f0150e1c3b96cb0438a0d0aa4871f1472e58de14a3ec320dbb2798fb0d4"}, + {file = "asyncpg-0.30.0-cp38-cp38-win_amd64.whl", hash = "sha256:f23b836dd90bea21104f69547923a02b167d999ce053f3d502081acea2fba15b"}, + {file = "asyncpg-0.30.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6f4e83f067b35ab5e6371f8a4c93296e0439857b4569850b178a01385e82e9ad"}, + {file = "asyncpg-0.30.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5df69d55add4efcd25ea2a3b02025b669a285b767bfbf06e356d68dbce4234ff"}, + {file = "asyncpg-0.30.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3479a0d9a852c7c84e822c073622baca862d1217b10a02dd57ee4a7a081f708"}, + {file = "asyncpg-0.30.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26683d3b9a62836fad771a18ecf4659a30f348a561279d6227dab96182f46144"}, + {file = "asyncpg-0.30.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1b982daf2441a0ed314bd10817f1606f1c28b1136abd9e4f11335358c2c631cb"}, + {file = "asyncpg-0.30.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1c06a3a50d014b303e5f6fc1e5f95eb28d2cee89cf58384b700da621e5d5e547"}, + {file = "asyncpg-0.30.0-cp39-cp39-win32.whl", hash = "sha256:1b11a555a198b08f5c4baa8f8231c74a366d190755aa4f99aacec5970afe929a"}, + {file = "asyncpg-0.30.0-cp39-cp39-win_amd64.whl", hash = "sha256:8b684a3c858a83cd876f05958823b68e8d14ec01bb0c0d14a6704c5bf9711773"}, + {file = "asyncpg-0.30.0.tar.gz", hash = "sha256:c551e9928ab6707602f44811817f82ba3c446e018bfe1d3abecc8ba5f3eac851"}, ] [package.extras] -benchmark = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -cov = ["cloudpickle", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier (<24.7)"] -tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"] +docs = ["Sphinx (>=8.1.3,<8.2.0)", "sphinx-rtd-theme (>=1.2.2)"] +gssauth = ["gssapi", "sspilib"] +test = ["distro (>=1.9.0,<1.10.0)", "flake8 (>=6.1,<7.0)", "flake8-pyi (>=24.1.0,<24.2.0)", "gssapi", "k5test", "mypy (>=1.8.0,<1.9.0)", "sspilib", "uvloop (>=0.15.3)"] + +[[package]] +name = "attrs" +version = "25.4.0" +description = "Classes Without Boilerplate" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373"}, + {file = "attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11"}, +] [[package]] name = "certifi" -version = "2025.1.31" +version = "2025.10.5" description = "Python package for providing Mozilla's CA Bundle." optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" groups = ["main"] files = [ - {file = "certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe"}, - {file = "certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651"}, + {file = "certifi-2025.10.5-py3-none-any.whl", hash = "sha256:0f212c2744a9bb6de0c56639a6f68afe01ecd92d91f14ae897c4fe7bbeeef0de"}, + {file = "certifi-2025.10.5.tar.gz", hash = "sha256:47c09d31ccf2acf0be3f701ea53595ee7e0b8fa08801c6624be771df09ae7b43"}, ] [[package]] name = "cffi" -version = "1.17.1" +version = "2.0.0" description = "Foreign Function Interface for Python calling C code." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["main"] files = [ - {file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"}, - {file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"}, - {file = "cffi-1.17.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382"}, - {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702"}, - {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3"}, - {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6"}, - {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17"}, - {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8"}, - {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e"}, - {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be"}, - {file = "cffi-1.17.1-cp310-cp310-win32.whl", hash = "sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c"}, - {file = "cffi-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15"}, - {file = "cffi-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401"}, - {file = "cffi-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf"}, - {file = "cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4"}, - {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41"}, - {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1"}, - {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6"}, - {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d"}, - {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6"}, - {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f"}, - {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b"}, - {file = "cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655"}, - {file = "cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0"}, - {file = "cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4"}, - {file = "cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c"}, - {file = "cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36"}, - {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5"}, - {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff"}, - {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99"}, - {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93"}, - {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3"}, - {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8"}, - {file = "cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65"}, - {file = "cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903"}, - {file = "cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e"}, - {file = "cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2"}, - {file = "cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3"}, - {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683"}, - {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5"}, - {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4"}, - {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd"}, - {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed"}, - {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9"}, - {file = "cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d"}, - {file = "cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a"}, - {file = "cffi-1.17.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:636062ea65bd0195bc012fea9321aca499c0504409f413dc88af450b57ffd03b"}, - {file = "cffi-1.17.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7eac2ef9b63c79431bc4b25f1cd649d7f061a28808cbc6c47b534bd789ef964"}, - {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e221cf152cff04059d011ee126477f0d9588303eb57e88923578ace7baad17f9"}, - {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:31000ec67d4221a71bd3f67df918b1f88f676f1c3b535a7eb473255fdc0b83fc"}, - {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f17be4345073b0a7b8ea599688f692ac3ef23ce28e5df79c04de519dbc4912c"}, - {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2b1fac190ae3ebfe37b979cc1ce69c81f4e4fe5746bb401dca63a9062cdaf1"}, - {file = "cffi-1.17.1-cp38-cp38-win32.whl", hash = "sha256:7596d6620d3fa590f677e9ee430df2958d2d6d6de2feeae5b20e82c00b76fbf8"}, - {file = "cffi-1.17.1-cp38-cp38-win_amd64.whl", hash = "sha256:78122be759c3f8a014ce010908ae03364d00a1f81ab5c7f4a7a5120607ea56e1"}, - {file = "cffi-1.17.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b2ab587605f4ba0bf81dc0cb08a41bd1c0a5906bd59243d56bad7668a6fc6c16"}, - {file = "cffi-1.17.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:28b16024becceed8c6dfbc75629e27788d8a3f9030691a1dbf9821a128b22c36"}, - {file = "cffi-1.17.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d599671f396c4723d016dbddb72fe8e0397082b0a77a4fab8028923bec050e8"}, - {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca74b8dbe6e8e8263c0ffd60277de77dcee6c837a3d0881d8c1ead7268c9e576"}, - {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87"}, - {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98e3969bcff97cae1b2def8ba499ea3d6f31ddfdb7635374834cf89a1a08ecf0"}, - {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdf5ce3acdfd1661132f2a9c19cac174758dc2352bfe37d98aa7512c6b7178b3"}, - {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9755e4345d1ec879e3849e62222a18c7174d65a6a92d5b346b1863912168b595"}, - {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f1e22e8c4419538cb197e4dd60acc919d7696e5ef98ee4da4e01d3f8cfa4cc5a"}, - {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c03e868a0b3bc35839ba98e74211ed2b05d2119be4e8a0f224fba9384f1fe02e"}, - {file = "cffi-1.17.1-cp39-cp39-win32.whl", hash = "sha256:e31ae45bc2e29f6b2abd0de1cc3b9d5205aa847cafaecb8af1476a609a2f6eb7"}, - {file = "cffi-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662"}, - {file = "cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824"}, + {file = "cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44"}, + {file = "cffi-2.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49"}, + {file = "cffi-2.0.0-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c"}, + {file = "cffi-2.0.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb"}, + {file = "cffi-2.0.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0"}, + {file = "cffi-2.0.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4"}, + {file = "cffi-2.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453"}, + {file = "cffi-2.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495"}, + {file = "cffi-2.0.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5"}, + {file = "cffi-2.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb"}, + {file = "cffi-2.0.0-cp310-cp310-win32.whl", hash = "sha256:1f72fb8906754ac8a2cc3f9f5aaa298070652a0ffae577e0ea9bd480dc3c931a"}, + {file = "cffi-2.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:b18a3ed7d5b3bd8d9ef7a8cb226502c6bf8308df1525e1cc676c3680e7176739"}, + {file = "cffi-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe"}, + {file = "cffi-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c"}, + {file = "cffi-2.0.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92"}, + {file = "cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93"}, + {file = "cffi-2.0.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5"}, + {file = "cffi-2.0.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664"}, + {file = "cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26"}, + {file = "cffi-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9"}, + {file = "cffi-2.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414"}, + {file = "cffi-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743"}, + {file = "cffi-2.0.0-cp311-cp311-win32.whl", hash = "sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5"}, + {file = "cffi-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5"}, + {file = "cffi-2.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d"}, + {file = "cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d"}, + {file = "cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c"}, + {file = "cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe"}, + {file = "cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062"}, + {file = "cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e"}, + {file = "cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037"}, + {file = "cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba"}, + {file = "cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94"}, + {file = "cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187"}, + {file = "cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18"}, + {file = "cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5"}, + {file = "cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6"}, + {file = "cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb"}, + {file = "cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca"}, + {file = "cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b"}, + {file = "cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b"}, + {file = "cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2"}, + {file = "cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3"}, + {file = "cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26"}, + {file = "cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c"}, + {file = "cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b"}, + {file = "cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27"}, + {file = "cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75"}, + {file = "cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91"}, + {file = "cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5"}, + {file = "cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13"}, + {file = "cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b"}, + {file = "cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c"}, + {file = "cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef"}, + {file = "cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775"}, + {file = "cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205"}, + {file = "cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1"}, + {file = "cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f"}, + {file = "cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25"}, + {file = "cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad"}, + {file = "cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9"}, + {file = "cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d"}, + {file = "cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c"}, + {file = "cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8"}, + {file = "cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc"}, + {file = "cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592"}, + {file = "cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512"}, + {file = "cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4"}, + {file = "cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e"}, + {file = "cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6"}, + {file = "cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9"}, + {file = "cffi-2.0.0-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:fe562eb1a64e67dd297ccc4f5addea2501664954f2692b69a76449ec7913ecbf"}, + {file = "cffi-2.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:de8dad4425a6ca6e4e5e297b27b5c824ecc7581910bf9aee86cb6835e6812aa7"}, + {file = "cffi-2.0.0-cp39-cp39-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:4647afc2f90d1ddd33441e5b0e85b16b12ddec4fca55f0d9671fef036ecca27c"}, + {file = "cffi-2.0.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3f4d46d8b35698056ec29bca21546e1551a205058ae1a181d871e278b0b28165"}, + {file = "cffi-2.0.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:e6e73b9e02893c764e7e8d5bb5ce277f1a009cd5243f8228f75f842bf937c534"}, + {file = "cffi-2.0.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:cb527a79772e5ef98fb1d700678fe031e353e765d1ca2d409c92263c6d43e09f"}, + {file = "cffi-2.0.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:61d028e90346df14fedc3d1e5441df818d095f3b87d286825dfcbd6459b7ef63"}, + {file = "cffi-2.0.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0f6084a0ea23d05d20c3edcda20c3d006f9b6f3fefeac38f59262e10cef47ee2"}, + {file = "cffi-2.0.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:1cd13c99ce269b3ed80b417dcd591415d3372bcac067009b6e0f59c7d4015e65"}, + {file = "cffi-2.0.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:89472c9762729b5ae1ad974b777416bfda4ac5642423fa93bd57a09204712322"}, + {file = "cffi-2.0.0-cp39-cp39-win32.whl", hash = "sha256:2081580ebb843f759b9f617314a24ed5738c51d2aee65d31e02f6f7a2b97707a"}, + {file = "cffi-2.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:b882b3df248017dba09d6b16defe9b5c407fe32fc7c65a9c69798e6175601be9"}, + {file = "cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529"}, ] [package.dependencies] -pycparser = "*" +pycparser = {version = "*", markers = "implementation_name != \"PyPy\""} [[package]] name = "charset-normalizer" -version = "3.4.1" +version = "3.4.3" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7" groups = ["main"] files = [ - {file = "charset_normalizer-3.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e218488cd232553829be0664c2292d3af2eeeb94b32bea483cf79ac6a694e037"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80ed5e856eb7f30115aaf94e4a08114ccc8813e6ed1b5efa74f9f82e8509858f"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b010a7a4fd316c3c484d482922d13044979e78d1861f0e0650423144c616a46a"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4532bff1b8421fd0a320463030c7520f56a79c9024a4e88f01c537316019005a"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d973f03c0cb71c5ed99037b870f2be986c3c05e63622c017ea9816881d2dd247"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3a3bd0dcd373514dcec91c411ddb9632c0d7d92aed7093b8c3bbb6d69ca74408"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:d9c3cdf5390dcd29aa8056d13e8e99526cda0305acc038b96b30352aff5ff2bb"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2bdfe3ac2e1bbe5b59a1a63721eb3b95fc9b6817ae4a46debbb4e11f6232428d"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:eab677309cdb30d047996b36d34caeda1dc91149e4fdca0b1a039b3f79d9a807"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-win32.whl", hash = "sha256:c0429126cf75e16c4f0ad00ee0eae4242dc652290f940152ca8c75c3a4b6ee8f"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:9f0b8b1c6d84c8034a44893aba5e767bf9c7a211e313a9605d9c617d7083829f"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-win32.whl", hash = "sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f30bf9fd9be89ecb2360c7d94a711f00c09b976258846efe40db3d05828e8089"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:97f68b8d6831127e4787ad15e6757232e14e12060bec17091b85eb1486b91d8d"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7974a0b5ecd505609e3b19742b60cee7aa2aa2fb3151bc917e6e2646d7667dcf"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc54db6c8593ef7d4b2a331b58653356cf04f67c960f584edb7c3d8c97e8f39e"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:311f30128d7d333eebd7896965bfcfbd0065f1716ec92bd5638d7748eb6f936a"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:7d053096f67cd1241601111b698f5cad775f97ab25d81567d3f59219b5f1adbd"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:807f52c1f798eef6cf26beb819eeb8819b1622ddfeef9d0977a8502d4db6d534"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:dccbe65bd2f7f7ec22c4ff99ed56faa1e9f785482b9bbd7c717e26fd723a1d1e"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:2fb9bd477fdea8684f78791a6de97a953c51831ee2981f8e4f583ff3b9d9687e"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:01732659ba9b5b873fc117534143e4feefecf3b2078b0a6a2e925271bb6f4cfa"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-win32.whl", hash = "sha256:7a4f97a081603d2050bfaffdefa5b02a9ec823f8348a572e39032caa8404a487"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:7b1bef6280950ee6c177b326508f86cad7ad4dff12454483b51d8b7d673a2c5d"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ecddf25bee22fe4fe3737a399d0d177d72bc22be6913acfab364b40bce1ba83c"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c60ca7339acd497a55b0ea5d506b2a2612afb2826560416f6894e8b5770d4a9"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b7b2d86dd06bfc2ade3312a83a5c364c7ec2e3498f8734282c6c3d4b07b346b8"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd78cfcda14a1ef52584dbb008f7ac81c1328c0f58184bf9a84c49c605002da6"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e27f48bcd0957c6d4cb9d6fa6b61d192d0b13d5ef563e5f2ae35feafc0d179c"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01ad647cdd609225c5350561d084b42ddf732f4eeefe6e678765636791e78b9a"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:619a609aa74ae43d90ed2e89bdd784765de0a25ca761b93e196d938b8fd1dbbd"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:89149166622f4db9b4b6a449256291dc87a99ee53151c74cbd82a53c8c2f6ccd"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:7709f51f5f7c853f0fb938bcd3bc59cdfdc5203635ffd18bf354f6967ea0f824"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:345b0426edd4e18138d6528aed636de7a9ed169b4aaf9d61a8c19e39d26838ca"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0907f11d019260cdc3f94fbdb23ff9125f6b5d1039b76003b5b0ac9d6a6c9d5b"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-win32.whl", hash = "sha256:ea0d8d539afa5eb2728aa1932a988a9a7af94f18582ffae4bc10b3fbdad0626e"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:329ce159e82018d646c7ac45b01a430369d526569ec08516081727a20e9e4af4"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b97e690a2118911e39b4042088092771b4ae3fc3aa86518f84b8cf6888dbdb41"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78baa6d91634dfb69ec52a463534bc0df05dbd546209b79a3880a34487f4b84f"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1a2bc9f351a75ef49d664206d51f8e5ede9da246602dc2d2726837620ea034b2"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75832c08354f595c760a804588b9357d34ec00ba1c940c15e31e96d902093770"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0af291f4fe114be0280cdd29d533696a77b5b49cfde5467176ecab32353395c4"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0167ddc8ab6508fe81860a57dd472b2ef4060e8d378f0cc555707126830f2537"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2a75d49014d118e4198bcee5ee0a6f25856b29b12dbf7cd012791f8a6cc5c496"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:363e2f92b0f0174b2f8238240a1a30142e3db7b957a5dd5689b0e75fb717cc78"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ab36c8eb7e454e34e60eb55ca5d241a5d18b2c6244f6827a30e451c42410b5f7"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:4c0907b1928a36d5a998d72d64d8eaa7244989f7aaaf947500d3a800c83a3fd6"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:04432ad9479fa40ec0f387795ddad4437a2b50417c69fa275e212933519ff294"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-win32.whl", hash = "sha256:3bed14e9c89dcb10e8f3a29f9ccac4955aebe93c71ae803af79265c9ca5644c5"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:49402233c892a461407c512a19435d1ce275543138294f7ef013f0b63d5d3765"}, - {file = "charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85"}, - {file = "charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fb7f67a1bfa6e40b438170ebdc8158b78dc465a5a67b6dde178a46987b244a72"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cc9370a2da1ac13f0153780040f465839e6cccb4a1e44810124b4e22483c93fe"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:07a0eae9e2787b586e129fdcbe1af6997f8d0e5abaa0bc98c0e20e124d67e601"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:74d77e25adda8581ffc1c720f1c81ca082921329452eba58b16233ab1842141c"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d0e909868420b7049dafd3a31d45125b31143eec59235311fc4c57ea26a4acd2"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c6f162aabe9a91a309510d74eeb6507fab5fff92337a15acbe77753d88d9dcf0"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:4ca4c094de7771a98d7fbd67d9e5dbf1eb73efa4f744a730437d8a3a5cf994f0"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:02425242e96bcf29a49711b0ca9f37e451da7c70562bc10e8ed992a5a7a25cc0"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:78deba4d8f9590fe4dae384aeff04082510a709957e968753ff3c48399f6f92a"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-win32.whl", hash = "sha256:d79c198e27580c8e958906f803e63cddb77653731be08851c7df0b1a14a8fc0f"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-win_amd64.whl", hash = "sha256:c6e490913a46fa054e03699c70019ab869e990270597018cef1d8562132c2669"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b256ee2e749283ef3ddcff51a675ff43798d92d746d1a6e4631bf8c707d22d0b"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:13faeacfe61784e2559e690fc53fa4c5ae97c6fcedb8eb6fb8d0a15b475d2c64"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:00237675befef519d9af72169d8604a067d92755e84fe76492fef5441db05b91"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:585f3b2a80fbd26b048a0be90c5aae8f06605d3c92615911c3a2b03a8a3b796f"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e78314bdc32fa80696f72fa16dc61168fda4d6a0c014e0380f9d02f0e5d8a07"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:96b2b3d1a83ad55310de8c7b4a2d04d9277d5591f40761274856635acc5fcb30"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:939578d9d8fd4299220161fdd76e86c6a251987476f5243e8864a7844476ba14"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:fd10de089bcdcd1be95a2f73dbe6254798ec1bda9f450d5828c96f93e2536b9c"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1e8ac75d72fa3775e0b7cb7e4629cec13b7514d928d15ef8ea06bca03ef01cae"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-win32.whl", hash = "sha256:6cf8fd4c04756b6b60146d98cd8a77d0cdae0e1ca20329da2ac85eed779b6849"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:31a9a6f775f9bcd865d88ee350f0ffb0e25936a7f930ca98995c05abf1faf21c"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e28e334d3ff134e88989d90ba04b47d84382a828c061d0d1027b1b12a62b39b1"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0cacf8f7297b0c4fcb74227692ca46b4a5852f8f4f24b3c766dd94a1075c4884"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c6fd51128a41297f5409deab284fecbe5305ebd7e5a1f959bee1c054622b7018"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3cfb2aad70f2c6debfbcb717f23b7eb55febc0bb23dcffc0f076009da10c6392"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1606f4a55c0fd363d754049cdf400175ee96c992b1f8018b993941f221221c5f"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:027b776c26d38b7f15b26a5da1044f376455fb3766df8fc38563b4efbc515154"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:42e5088973e56e31e4fa58eb6bd709e42fc03799c11c42929592889a2e54c491"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:cc34f233c9e71701040d772aa7490318673aa7164a0efe3172b2981218c26d93"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:320e8e66157cc4e247d9ddca8e21f427efc7a04bbd0ac8a9faf56583fa543f9f"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-win32.whl", hash = "sha256:fb6fecfd65564f208cbf0fba07f107fb661bcd1a7c389edbced3f7a493f70e37"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:86df271bf921c2ee3818f0522e9a5b8092ca2ad8b065ece5d7d9d0e9f4849bcc"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:14c2a87c65b351109f6abfc424cab3927b3bdece6f706e4d12faaf3d52ee5efe"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:41d1fc408ff5fdfb910200ec0e74abc40387bccb3252f3f27c0676731df2b2c8"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1bb60174149316da1c35fa5233681f7c0f9f514509b8e399ab70fea5f17e45c9"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:30d006f98569de3459c2fc1f2acde170b7b2bd265dc1943e87e1a4efe1b67c31"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:416175faf02e4b0810f1f38bcb54682878a4af94059a1cd63b8747244420801f"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6aab0f181c486f973bc7262a97f5aca3ee7e1437011ef0c2ec04b5a11d16c927"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:fdabf8315679312cfa71302f9bd509ded4f2f263fb5b765cf1433b39106c3cc9"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:bd28b817ea8c70215401f657edef3a8aa83c29d447fb0b622c35403780ba11d5"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:18343b2d246dc6761a249ba1fb13f9ee9a2bcd95decc767319506056ea4ad4dc"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-win32.whl", hash = "sha256:6fb70de56f1859a3f71261cbe41005f56a7842cc348d3aeb26237560bfa5e0ce"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-win_amd64.whl", hash = "sha256:cf1ebb7d78e1ad8ec2a8c4732c7be2e736f6e5123a4146c5b89c9d1f585f8cef"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3cd35b7e8aedeb9e34c41385fda4f73ba609e561faedfae0a9e75e44ac558a15"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b89bc04de1d83006373429975f8ef9e7932534b8cc9ca582e4db7d20d91816db"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2001a39612b241dae17b4687898843f254f8748b796a2e16f1051a17078d991d"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8dcfc373f888e4fb39a7bc57e93e3b845e7f462dacc008d9749568b1c4ece096"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:18b97b8404387b96cdbd30ad660f6407799126d26a39ca65729162fd810a99aa"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ccf600859c183d70eb47e05a44cd80a4ce77394d1ac0f79dbd2dd90a69a3a049"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:53cd68b185d98dde4ad8990e56a58dea83a4162161b1ea9272e5c9182ce415e0"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:30a96e1e1f865f78b030d65241c1ee850cdf422d869e9028e2fc1d5e4db73b92"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d716a916938e03231e86e43782ca7878fb602a125a91e7acb8b5112e2e96ac16"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-win32.whl", hash = "sha256:c6dbd0ccdda3a2ba7c2ecd9d77b37f3b5831687d8dc1b6ca5f56a4880cc7b7ce"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-win_amd64.whl", hash = "sha256:73dc19b562516fc9bcf6e5d6e596df0b4eb98d87e4f79f3ae71840e6ed21361c"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0f2be7e0cf7754b9a30eb01f4295cc3d4358a479843b31f328afd210e2c7598c"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c60e092517a73c632ec38e290eba714e9627abe9d301c8c8a12ec32c314a2a4b"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:252098c8c7a873e17dd696ed98bbe91dbacd571da4b87df3736768efa7a792e4"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3653fad4fe3ed447a596ae8638b437f827234f01a8cd801842e43f3d0a6b281b"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8999f965f922ae054125286faf9f11bc6932184b93011d138925a1773830bbe9"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d95bfb53c211b57198bb91c46dd5a2d8018b3af446583aab40074bf7988401cb"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:5b413b0b1bfd94dbf4023ad6945889f374cd24e3f62de58d6bb102c4d9ae534a"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:b5e3b2d152e74e100a9e9573837aba24aab611d39428ded46f4e4022ea7d1942"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:a2d08ac246bb48479170408d6c19f6385fa743e7157d716e144cad849b2dd94b"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-win32.whl", hash = "sha256:ec557499516fc90fd374bf2e32349a2887a876fbf162c160e3c01b6849eaf557"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:5d8d01eac18c423815ed4f4a2ec3b439d654e55ee4ad610e153cf02faf67ea40"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:70bfc5f2c318afece2f5838ea5e4c3febada0be750fcf4775641052bbba14d05"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:23b6b24d74478dc833444cbd927c338349d6ae852ba53a0d02a2de1fce45b96e"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:34a7f768e3f985abdb42841e20e17b330ad3aaf4bb7e7aeeb73db2e70f077b99"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fb731e5deb0c7ef82d698b0f4c5bb724633ee2a489401594c5c88b02e6cb15f7"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:257f26fed7d7ff59921b78244f3cd93ed2af1800ff048c33f624c87475819dd7"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1ef99f0456d3d46a50945c98de1774da86f8e992ab5c77865ea8b8195341fc19"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:2c322db9c8c89009a990ef07c3bcc9f011a3269bc06782f916cd3d9eed7c9312"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:511729f456829ef86ac41ca78c63a5cb55240ed23b4b737faca0eb1abb1c41bc"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:88ab34806dea0671532d3f82d82b85e8fc23d7b2dd12fa837978dad9bb392a34"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-win32.whl", hash = "sha256:16a8770207946ac75703458e2c743631c79c59c5890c80011d536248f8eaa432"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:d22dbedd33326a4a5190dd4fe9e9e693ef12160c77382d9e87919bce54f3d4ca"}, + {file = "charset_normalizer-3.4.3-py3-none-any.whl", hash = "sha256:ce571ab16d890d23b5c278547ba694193a45011ff86a9162a71307ed9f86759a"}, + {file = "charset_normalizer-3.4.3.tar.gz", hash = "sha256:6fce4b8500244f6fcb71465d4a4930d132ba9ab8e71a7859e6a5d59851068d14"}, ] [[package]] name = "click" -version = "8.1.8" +version = "8.3.0" description = "Composable command line interface toolkit" optional = false -python-versions = ">=3.7" +python-versions = ">=3.10" groups = ["main"] files = [ - {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"}, - {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"}, + {file = "click-8.3.0-py3-none-any.whl", hash = "sha256:9b9f285302c6e3064f4330c05f05b81945b2a39544279343e6e7c5f27a9baddc"}, + {file = "click-8.3.0.tar.gz", hash = "sha256:e7b8232224eba16f4ebe410c25ced9f7875cb5f3263ffc93cc3e8da705e229c4"}, ] [package.dependencies] @@ -463,12 +566,12 @@ version = "0.4.6" description = "Cross-platform colored terminal text." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -groups = ["main"] -markers = "platform_system == \"Windows\"" +groups = ["main", "dev"] files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] +markers = {main = "platform_system == \"Windows\"", dev = "sys_platform == \"win32\""} [[package]] name = "common" @@ -481,7 +584,7 @@ files = [] develop = true [package.dependencies] -dapr = "^1.14.0" +dapr = "1.16.0" fastapi = "^0.115.6" minio = "^7.2.14" pydantic = "^2.10.5" @@ -493,72 +596,91 @@ url = "../../libs/common" [[package]] name = "cryptography" -version = "44.0.2" +version = "46.0.2" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." optional = false -python-versions = "!=3.9.0,!=3.9.1,>=3.7" +python-versions = "!=3.9.0,!=3.9.1,>=3.8" groups = ["main"] files = [ - {file = "cryptography-44.0.2-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:efcfe97d1b3c79e486554efddeb8f6f53a4cdd4cf6086642784fa31fc384e1d7"}, - {file = "cryptography-44.0.2-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29ecec49f3ba3f3849362854b7253a9f59799e3763b0c9d0826259a88efa02f1"}, - {file = "cryptography-44.0.2-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc821e161ae88bfe8088d11bb39caf2916562e0a2dc7b6d56714a48b784ef0bb"}, - {file = "cryptography-44.0.2-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:3c00b6b757b32ce0f62c574b78b939afab9eecaf597c4d624caca4f9e71e7843"}, - {file = "cryptography-44.0.2-cp37-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7bdcd82189759aba3816d1f729ce42ffded1ac304c151d0a8e89b9996ab863d5"}, - {file = "cryptography-44.0.2-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:4973da6ca3db4405c54cd0b26d328be54c7747e89e284fcff166132eb7bccc9c"}, - {file = "cryptography-44.0.2-cp37-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:4e389622b6927d8133f314949a9812972711a111d577a5d1f4bee5e58736b80a"}, - {file = "cryptography-44.0.2-cp37-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:f514ef4cd14bb6fb484b4a60203e912cfcb64f2ab139e88c2274511514bf7308"}, - {file = "cryptography-44.0.2-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1bc312dfb7a6e5d66082c87c34c8a62176e684b6fe3d90fcfe1568de675e6688"}, - {file = "cryptography-44.0.2-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:3b721b8b4d948b218c88cb8c45a01793483821e709afe5f622861fc6182b20a7"}, - {file = "cryptography-44.0.2-cp37-abi3-win32.whl", hash = "sha256:51e4de3af4ec3899d6d178a8c005226491c27c4ba84101bfb59c901e10ca9f79"}, - {file = "cryptography-44.0.2-cp37-abi3-win_amd64.whl", hash = "sha256:c505d61b6176aaf982c5717ce04e87da5abc9a36a5b39ac03905c4aafe8de7aa"}, - {file = "cryptography-44.0.2-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:8e0ddd63e6bf1161800592c71ac794d3fb8001f2caebe0966e77c5234fa9efc3"}, - {file = "cryptography-44.0.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:81276f0ea79a208d961c433a947029e1a15948966658cf6710bbabb60fcc2639"}, - {file = "cryptography-44.0.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a1e657c0f4ea2a23304ee3f964db058c9e9e635cc7019c4aa21c330755ef6fd"}, - {file = "cryptography-44.0.2-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:6210c05941994290f3f7f175a4a57dbbb2afd9273657614c506d5976db061181"}, - {file = "cryptography-44.0.2-cp39-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d1c3572526997b36f245a96a2b1713bf79ce99b271bbcf084beb6b9b075f29ea"}, - {file = "cryptography-44.0.2-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:b042d2a275c8cee83a4b7ae30c45a15e6a4baa65a179a0ec2d78ebb90e4f6699"}, - {file = "cryptography-44.0.2-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:d03806036b4f89e3b13b6218fefea8d5312e450935b1a2d55f0524e2ed7c59d9"}, - {file = "cryptography-44.0.2-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:c7362add18b416b69d58c910caa217f980c5ef39b23a38a0880dfd87bdf8cd23"}, - {file = "cryptography-44.0.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:8cadc6e3b5a1f144a039ea08a0bdb03a2a92e19c46be3285123d32029f40a922"}, - {file = "cryptography-44.0.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6f101b1f780f7fc613d040ca4bdf835c6ef3b00e9bd7125a4255ec574c7916e4"}, - {file = "cryptography-44.0.2-cp39-abi3-win32.whl", hash = "sha256:3dc62975e31617badc19a906481deacdeb80b4bb454394b4098e3f2525a488c5"}, - {file = "cryptography-44.0.2-cp39-abi3-win_amd64.whl", hash = "sha256:5f6f90b72d8ccadb9c6e311c775c8305381db88374c65fa1a68250aa8a9cb3a6"}, - {file = "cryptography-44.0.2-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:af4ff3e388f2fa7bff9f7f2b31b87d5651c45731d3e8cfa0944be43dff5cfbdb"}, - {file = "cryptography-44.0.2-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:0529b1d5a0105dd3731fa65680b45ce49da4d8115ea76e9da77a875396727b41"}, - {file = "cryptography-44.0.2-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:7ca25849404be2f8e4b3c59483d9d3c51298a22c1c61a0e84415104dacaf5562"}, - {file = "cryptography-44.0.2-pp310-pypy310_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:268e4e9b177c76d569e8a145a6939eca9a5fec658c932348598818acf31ae9a5"}, - {file = "cryptography-44.0.2-pp310-pypy310_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:9eb9d22b0a5d8fd9925a7764a054dca914000607dff201a24c791ff5c799e1fa"}, - {file = "cryptography-44.0.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:2bf7bf75f7df9715f810d1b038870309342bff3069c5bd8c6b96128cb158668d"}, - {file = "cryptography-44.0.2-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:909c97ab43a9c0c0b0ada7a1281430e4e5ec0458e6d9244c0e821bbf152f061d"}, - {file = "cryptography-44.0.2-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:96e7a5e9d6e71f9f4fca8eebfd603f8e86c5225bb18eb621b2c1e50b290a9471"}, - {file = "cryptography-44.0.2-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:d1b3031093a366ac767b3feb8bcddb596671b3aaff82d4050f984da0c248b615"}, - {file = "cryptography-44.0.2-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:04abd71114848aa25edb28e225ab5f268096f44cf0127f3d36975bdf1bdf3390"}, - {file = "cryptography-44.0.2.tar.gz", hash = "sha256:c63454aa261a0cf0c5b4718349629793e9e634993538db841165b3df74f37ec0"}, + {file = "cryptography-46.0.2-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:f3e32ab7dd1b1ef67b9232c4cf5e2ee4cd517d4316ea910acaaa9c5712a1c663"}, + {file = "cryptography-46.0.2-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1fd1a69086926b623ef8126b4c33d5399ce9e2f3fac07c9c734c2a4ec38b6d02"}, + {file = "cryptography-46.0.2-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:bb7fb9cd44c2582aa5990cf61a4183e6f54eea3172e54963787ba47287edd135"}, + {file = "cryptography-46.0.2-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:9066cfd7f146f291869a9898b01df1c9b0e314bfa182cef432043f13fc462c92"}, + {file = "cryptography-46.0.2-cp311-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:97e83bf4f2f2c084d8dd792d13841d0a9b241643151686010866bbd076b19659"}, + {file = "cryptography-46.0.2-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:4a766d2a5d8127364fd936572c6e6757682fc5dfcbdba1632d4554943199f2fa"}, + {file = "cryptography-46.0.2-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:fab8f805e9675e61ed8538f192aad70500fa6afb33a8803932999b1049363a08"}, + {file = "cryptography-46.0.2-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:1e3b6428a3d56043bff0bb85b41c535734204e599c1c0977e1d0f261b02f3ad5"}, + {file = "cryptography-46.0.2-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:1a88634851d9b8de8bb53726f4300ab191d3b2f42595e2581a54b26aba71b7cc"}, + {file = "cryptography-46.0.2-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:be939b99d4e091eec9a2bcf41aaf8f351f312cd19ff74b5c83480f08a8a43e0b"}, + {file = "cryptography-46.0.2-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9f13b040649bc18e7eb37936009b24fd31ca095a5c647be8bb6aaf1761142bd1"}, + {file = "cryptography-46.0.2-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:9bdc25e4e01b261a8fda4e98618f1c9515febcecebc9566ddf4a70c63967043b"}, + {file = "cryptography-46.0.2-cp311-abi3-win32.whl", hash = "sha256:8b9bf67b11ef9e28f4d78ff88b04ed0929fcd0e4f70bb0f704cfc32a5c6311ee"}, + {file = "cryptography-46.0.2-cp311-abi3-win_amd64.whl", hash = "sha256:758cfc7f4c38c5c5274b55a57ef1910107436f4ae842478c4989abbd24bd5acb"}, + {file = "cryptography-46.0.2-cp311-abi3-win_arm64.whl", hash = "sha256:218abd64a2e72f8472c2102febb596793347a3e65fafbb4ad50519969da44470"}, + {file = "cryptography-46.0.2-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:bda55e8dbe8533937956c996beaa20266a8eca3570402e52ae52ed60de1faca8"}, + {file = "cryptography-46.0.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e7155c0b004e936d381b15425273aee1cebc94f879c0ce82b0d7fecbf755d53a"}, + {file = "cryptography-46.0.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a61c154cc5488272a6c4b86e8d5beff4639cdb173d75325ce464d723cda0052b"}, + {file = "cryptography-46.0.2-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:9ec3f2e2173f36a9679d3b06d3d01121ab9b57c979de1e6a244b98d51fea1b20"}, + {file = "cryptography-46.0.2-cp314-cp314t-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2fafb6aa24e702bbf74de4cb23bfa2c3beb7ab7683a299062b69724c92e0fa73"}, + {file = "cryptography-46.0.2-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:0c7ffe8c9b1fcbb07a26d7c9fa5e857c2fe80d72d7b9e0353dcf1d2180ae60ee"}, + {file = "cryptography-46.0.2-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:5840f05518caa86b09d23f8b9405a7b6d5400085aa14a72a98fdf5cf1568c0d2"}, + {file = "cryptography-46.0.2-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:27c53b4f6a682a1b645fbf1cd5058c72cf2f5aeba7d74314c36838c7cbc06e0f"}, + {file = "cryptography-46.0.2-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:512c0250065e0a6b286b2db4bbcc2e67d810acd53eb81733e71314340366279e"}, + {file = "cryptography-46.0.2-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:07c0eb6657c0e9cca5891f4e35081dbf985c8131825e21d99b4f440a8f496f36"}, + {file = "cryptography-46.0.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:48b983089378f50cba258f7f7aa28198c3f6e13e607eaf10472c26320332ca9a"}, + {file = "cryptography-46.0.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e6f6775eaaa08c0eec73e301f7592f4367ccde5e4e4df8e58320f2ebf161ea2c"}, + {file = "cryptography-46.0.2-cp314-cp314t-win32.whl", hash = "sha256:e8633996579961f9b5a3008683344c2558d38420029d3c0bc7ff77c17949a4e1"}, + {file = "cryptography-46.0.2-cp314-cp314t-win_amd64.whl", hash = "sha256:48c01988ecbb32979bb98731f5c2b2f79042a6c58cc9a319c8c2f9987c7f68f9"}, + {file = "cryptography-46.0.2-cp314-cp314t-win_arm64.whl", hash = "sha256:8e2ad4d1a5899b7caa3a450e33ee2734be7cc0689010964703a7c4bcc8dd4fd0"}, + {file = "cryptography-46.0.2-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:a08e7401a94c002e79dc3bc5231b6558cd4b2280ee525c4673f650a37e2c7685"}, + {file = "cryptography-46.0.2-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d30bc11d35743bf4ddf76674a0a369ec8a21f87aaa09b0661b04c5f6c46e8d7b"}, + {file = "cryptography-46.0.2-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:bca3f0ce67e5a2a2cf524e86f44697c4323a86e0fd7ba857de1c30d52c11ede1"}, + {file = "cryptography-46.0.2-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ff798ad7a957a5021dcbab78dfff681f0cf15744d0e6af62bd6746984d9c9e9c"}, + {file = "cryptography-46.0.2-cp38-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:cb5e8daac840e8879407acbe689a174f5ebaf344a062f8918e526824eb5d97af"}, + {file = "cryptography-46.0.2-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:3f37aa12b2d91e157827d90ce78f6180f0c02319468a0aea86ab5a9566da644b"}, + {file = "cryptography-46.0.2-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:5e38f203160a48b93010b07493c15f2babb4e0f2319bbd001885adb3f3696d21"}, + {file = "cryptography-46.0.2-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:d19f5f48883752b5ab34cff9e2f7e4a7f216296f33714e77d1beb03d108632b6"}, + {file = "cryptography-46.0.2-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:04911b149eae142ccd8c9a68892a70c21613864afb47aba92d8c7ed9cc001023"}, + {file = "cryptography-46.0.2-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:8b16c1ede6a937c291d41176934268e4ccac2c6521c69d3f5961c5a1e11e039e"}, + {file = "cryptography-46.0.2-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:747b6f4a4a23d5a215aadd1d0b12233b4119c4313df83ab4137631d43672cc90"}, + {file = "cryptography-46.0.2-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6b275e398ab3a7905e168c036aad54b5969d63d3d9099a0a66cc147a3cc983be"}, + {file = "cryptography-46.0.2-cp38-abi3-win32.whl", hash = "sha256:0b507c8e033307e37af61cb9f7159b416173bdf5b41d11c4df2e499a1d8e007c"}, + {file = "cryptography-46.0.2-cp38-abi3-win_amd64.whl", hash = "sha256:f9b2dc7668418fb6f221e4bf701f716e05e8eadb4f1988a2487b11aedf8abe62"}, + {file = "cryptography-46.0.2-cp38-abi3-win_arm64.whl", hash = "sha256:91447f2b17e83c9e0c89f133119d83f94ce6e0fb55dd47da0a959316e6e9cfa1"}, + {file = "cryptography-46.0.2-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:f25a41f5b34b371a06dad3f01799706631331adc7d6c05253f5bca22068c7a34"}, + {file = "cryptography-46.0.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:e12b61e0b86611e3f4c1756686d9086c1d36e6fd15326f5658112ad1f1cc8807"}, + {file = "cryptography-46.0.2-pp311-pypy311_pp73-macosx_10_9_x86_64.whl", hash = "sha256:1d3b3edd145953832e09607986f2bd86f85d1dc9c48ced41808b18009d9f30e5"}, + {file = "cryptography-46.0.2-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:fe245cf4a73c20592f0f48da39748b3513db114465be78f0a36da847221bd1b4"}, + {file = "cryptography-46.0.2-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:2b9cad9cf71d0c45566624ff76654e9bae5f8a25970c250a26ccfc73f8553e2d"}, + {file = "cryptography-46.0.2-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:9bd26f2f75a925fdf5e0a446c0de2714f17819bf560b44b7480e4dd632ad6c46"}, + {file = "cryptography-46.0.2-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:7282d8f092b5be7172d6472f29b0631f39f18512a3642aefe52c3c0e0ccfad5a"}, + {file = "cryptography-46.0.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:c4b93af7920cdf80f71650769464ccf1fb49a4b56ae0024173c24c48eb6b1612"}, + {file = "cryptography-46.0.2.tar.gz", hash = "sha256:21b6fc8c71a3f9a604f028a329e5560009cc4a3a828bfea5fcba8eb7647d88fe"}, ] [package.dependencies] -cffi = {version = ">=1.12", markers = "platform_python_implementation != \"PyPy\""} +cffi = {version = ">=2.0.0", markers = "python_full_version >= \"3.9\" and platform_python_implementation != \"PyPy\""} [package.extras] -docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=3.0.0)"] +docs = ["sphinx (>=5.3.0)", "sphinx-inline-tabs", "sphinx-rtd-theme (>=3.0.0)"] docstest = ["pyenchant (>=3)", "readme-renderer (>=30.0)", "sphinxcontrib-spelling (>=7.3.1)"] -nox = ["nox (>=2024.4.15)", "nox[uv] (>=2024.3.2)"] -pep8test = ["check-sdist", "click (>=8.0.1)", "mypy (>=1.4)", "ruff (>=0.3.6)"] +nox = ["nox[uv] (>=2024.4.15)"] +pep8test = ["check-sdist", "click (>=8.0.1)", "mypy (>=1.14)", "ruff (>=0.11.11)"] sdist = ["build (>=1.0.0)"] ssh = ["bcrypt (>=3.1.5)"] -test = ["certifi (>=2024)", "cryptography-vectors (==44.0.2)", "pretend (>=0.7)", "pytest (>=7.4.0)", "pytest-benchmark (>=4.0)", "pytest-cov (>=2.10.1)", "pytest-xdist (>=3.5.0)"] +test = ["certifi (>=2024)", "cryptography-vectors (==46.0.2)", "pretend (>=0.7)", "pytest (>=7.4.0)", "pytest-benchmark (>=4.0)", "pytest-cov (>=2.10.1)", "pytest-xdist (>=3.5.0)"] test-randomorder = ["pytest-randomly"] [[package]] name = "dapr" -version = "1.15.0" +version = "1.16.0" description = "The official release of Dapr Python SDK." optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "dapr-1.15.0-py3-none-any.whl", hash = "sha256:0093bf6df5eb9a14fbab60191a619438e0b6b336f60a7994e184276bcc35d5fb"}, - {file = "dapr-1.15.0.tar.gz", hash = "sha256:6b2373084143f164cb00702758b17a14fc4442314a1f3e2be36ee008d486c47a"}, + {file = "dapr-1.16.0-py3-none-any.whl", hash = "sha256:076dd559a0b450eae24b1c2ae779c9299ed3e06a05c1f72719a6613af8d19ced"}, + {file = "dapr-1.16.0.tar.gz", hash = "sha256:c7e3d005552a598d07608d0d502b2bc432e86678f94b2beccc13a096b9198684"}, ] [package.dependencies] @@ -571,62 +693,64 @@ typing-extensions = ">=4.4.0" [[package]] name = "dapr-ext-fastapi" -version = "1.15.0" +version = "1.16.0" description = "The official release of Dapr FastAPI extension." optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "dapr-ext-fastapi-1.15.0.tar.gz", hash = "sha256:d5411d24c6dcc256041b29383caa95d6024e58ab094ad24f7e72b9396bc245b0"}, - {file = "dapr_ext_fastapi-1.15.0-py3-none-any.whl", hash = "sha256:429f15345a4b2fb89586fe691d9c8addba0001246c54cf0fc7e5c7c3a6075c97"}, + {file = "dapr-ext-fastapi-1.16.0.tar.gz", hash = "sha256:10108c3831ae2164c1589c86e6b86fe8ee146650514961841d9ab5eb783f4a76"}, + {file = "dapr_ext_fastapi-1.16.0-py3-none-any.whl", hash = "sha256:9dcc0aaceb361c5132295450a71d4f9ddec09ab5848dbfe2a8b0cf9050fd903e"}, ] [package.dependencies] -dapr = ">=1.15.0" +dapr = ">=1.16.0" fastapi = ">=0.60.1" uvicorn = ">=0.11.6" [[package]] name = "dapr-ext-workflow" -version = "1.15.0" +version = "1.16.0" description = "The official release of Dapr Python SDK Workflow Authoring Extension." optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "dapr-ext-workflow-1.15.0.tar.gz", hash = "sha256:c05508005a7bbd54968a4b626a3e2034e5c841b85e7a67be7f2b80b3d006345b"}, - {file = "dapr_ext_workflow-1.15.0-py3-none-any.whl", hash = "sha256:2c2d2c63a8fed92e01417328a54e140775b170d3994b1f0fb54346bb50ad17dd"}, + {file = "dapr-ext-workflow-1.16.0.tar.gz", hash = "sha256:7487d174394d305e668784f4bac2dcecc757a1e0a8ddf6e5e1cb32c0a887be78"}, + {file = "dapr_ext_workflow-1.16.0-py3-none-any.whl", hash = "sha256:028f6b3a340a5a8f0b061eacdef60de1ce52de2340f9636f517f799f73437ee8"}, ] [package.dependencies] -dapr = ">=1.15.0" -durabletask-dapr = ">=0.2.0a7" +dapr = ">=1.16.0" +durabletask-dapr = ">=0.2.0a8" [[package]] name = "durabletask-dapr" -version = "0.2.0a7" +version = "0.2.0a9" description = "A Durable Task Client SDK for Python" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "durabletask_dapr-0.2.0a7-py3-none-any.whl", hash = "sha256:a13b410ee8404882984d8e442a27d06570face32ca2d3c60c73b7377013ea1e9"}, - {file = "durabletask_dapr-0.2.0a7.tar.gz", hash = "sha256:72fdb1055dbf47be3c9f24812fb41902e14ed27f19ccbd7af1372c4bad74963f"}, + {file = "durabletask_dapr-0.2.0a9-py3-none-any.whl", hash = "sha256:48c401c30c05a6122bdd1ee245e9b65c56dabb2dde3dfe6fe1d4ee47085e4a2e"}, + {file = "durabletask_dapr-0.2.0a9.tar.gz", hash = "sha256:ec481840a043a9d15f67628386b0694e60a04de8015f8f56883f55e490ebbb56"}, ] [package.dependencies] +asyncio = "*" grpcio = "*" +protobuf = "*" [[package]] name = "fastapi" -version = "0.115.10" +version = "0.115.14" description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" optional = false python-versions = ">=3.8" groups = ["main"] files = [ - {file = "fastapi-0.115.10-py3-none-any.whl", hash = "sha256:47346c5437e933e68909a835cf63890a9bd52fb6091b2499b996c08a01ca43a5"}, - {file = "fastapi-0.115.10.tar.gz", hash = "sha256:920cdc95c1c6ca073656deae80ad254512d131031c2d7759c87ae469572911ee"}, + {file = "fastapi-0.115.14-py3-none-any.whl", hash = "sha256:6c0c8bf9420bd58f565e585036d971872472b4f7d3f6c73b698e10cffdefb3ca"}, + {file = "fastapi-0.115.14.tar.gz", hash = "sha256:b1de15cdc1c499a4da47914db35d0e4ef8f1ce62b624e94e0e5824421df99739"}, ] [package.dependencies] @@ -640,104 +764,142 @@ standard = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "htt [[package]] name = "frozenlist" -version = "1.5.0" +version = "1.8.0" description = "A list-like structure which implements collections.abc.MutableSequence" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["main"] files = [ - {file = "frozenlist-1.5.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5b6a66c18b5b9dd261ca98dffcb826a525334b2f29e7caa54e182255c5f6a65a"}, - {file = "frozenlist-1.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d1b3eb7b05ea246510b43a7e53ed1653e55c2121019a97e60cad7efb881a97bb"}, - {file = "frozenlist-1.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:15538c0cbf0e4fa11d1e3a71f823524b0c46299aed6e10ebb4c2089abd8c3bec"}, - {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e79225373c317ff1e35f210dd5f1344ff31066ba8067c307ab60254cd3a78ad5"}, - {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9272fa73ca71266702c4c3e2d4a28553ea03418e591e377a03b8e3659d94fa76"}, - {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:498524025a5b8ba81695761d78c8dd7382ac0b052f34e66939c42df860b8ff17"}, - {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:92b5278ed9d50fe610185ecd23c55d8b307d75ca18e94c0e7de328089ac5dcba"}, - {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f3c8c1dacd037df16e85227bac13cca58c30da836c6f936ba1df0c05d046d8d"}, - {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f2ac49a9bedb996086057b75bf93538240538c6d9b38e57c82d51f75a73409d2"}, - {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e66cc454f97053b79c2ab09c17fbe3c825ea6b4de20baf1be28919460dd7877f"}, - {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:5a3ba5f9a0dfed20337d3e966dc359784c9f96503674c2faf015f7fe8e96798c"}, - {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6321899477db90bdeb9299ac3627a6a53c7399c8cd58d25da094007402b039ab"}, - {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:76e4753701248476e6286f2ef492af900ea67d9706a0155335a40ea21bf3b2f5"}, - {file = "frozenlist-1.5.0-cp310-cp310-win32.whl", hash = "sha256:977701c081c0241d0955c9586ffdd9ce44f7a7795df39b9151cd9a6fd0ce4cfb"}, - {file = "frozenlist-1.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:189f03b53e64144f90990d29a27ec4f7997d91ed3d01b51fa39d2dbe77540fd4"}, - {file = "frozenlist-1.5.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:fd74520371c3c4175142d02a976aee0b4cb4a7cc912a60586ffd8d5929979b30"}, - {file = "frozenlist-1.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2f3f7a0fbc219fb4455264cae4d9f01ad41ae6ee8524500f381de64ffaa077d5"}, - {file = "frozenlist-1.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f47c9c9028f55a04ac254346e92977bf0f166c483c74b4232bee19a6697e4778"}, - {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0996c66760924da6e88922756d99b47512a71cfd45215f3570bf1e0b694c206a"}, - {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a2fe128eb4edeabe11896cb6af88fca5346059f6c8d807e3b910069f39157869"}, - {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1a8ea951bbb6cacd492e3948b8da8c502a3f814f5d20935aae74b5df2b19cf3d"}, - {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:de537c11e4aa01d37db0d403b57bd6f0546e71a82347a97c6a9f0dcc532b3a45"}, - {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c2623347b933fcb9095841f1cc5d4ff0b278addd743e0e966cb3d460278840d"}, - {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cee6798eaf8b1416ef6909b06f7dc04b60755206bddc599f52232606e18179d3"}, - {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f5f9da7f5dbc00a604fe74aa02ae7c98bcede8a3b8b9666f9f86fc13993bc71a"}, - {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:90646abbc7a5d5c7c19461d2e3eeb76eb0b204919e6ece342feb6032c9325ae9"}, - {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:bdac3c7d9b705d253b2ce370fde941836a5f8b3c5c2b8fd70940a3ea3af7f4f2"}, - {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:03d33c2ddbc1816237a67f66336616416e2bbb6beb306e5f890f2eb22b959cdf"}, - {file = "frozenlist-1.5.0-cp311-cp311-win32.whl", hash = "sha256:237f6b23ee0f44066219dae14c70ae38a63f0440ce6750f868ee08775073f942"}, - {file = "frozenlist-1.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:0cc974cc93d32c42e7b0f6cf242a6bd941c57c61b618e78b6c0a96cb72788c1d"}, - {file = "frozenlist-1.5.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:31115ba75889723431aa9a4e77d5f398f5cf976eea3bdf61749731f62d4a4a21"}, - {file = "frozenlist-1.5.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7437601c4d89d070eac8323f121fcf25f88674627505334654fd027b091db09d"}, - {file = "frozenlist-1.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7948140d9f8ece1745be806f2bfdf390127cf1a763b925c4a805c603df5e697e"}, - {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:feeb64bc9bcc6b45c6311c9e9b99406660a9c05ca8a5b30d14a78555088b0b3a"}, - {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:683173d371daad49cffb8309779e886e59c2f369430ad28fe715f66d08d4ab1a"}, - {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7d57d8f702221405a9d9b40f9da8ac2e4a1a8b5285aac6100f3393675f0a85ee"}, - {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30c72000fbcc35b129cb09956836c7d7abf78ab5416595e4857d1cae8d6251a6"}, - {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:000a77d6034fbad9b6bb880f7ec073027908f1b40254b5d6f26210d2dab1240e"}, - {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5d7f5a50342475962eb18b740f3beecc685a15b52c91f7d975257e13e029eca9"}, - {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:87f724d055eb4785d9be84e9ebf0f24e392ddfad00b3fe036e43f489fafc9039"}, - {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:6e9080bb2fb195a046e5177f10d9d82b8a204c0736a97a153c2466127de87784"}, - {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9b93d7aaa36c966fa42efcaf716e6b3900438632a626fb09c049f6a2f09fc631"}, - {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:52ef692a4bc60a6dd57f507429636c2af8b6046db8b31b18dac02cbc8f507f7f"}, - {file = "frozenlist-1.5.0-cp312-cp312-win32.whl", hash = "sha256:29d94c256679247b33a3dc96cce0f93cbc69c23bf75ff715919332fdbb6a32b8"}, - {file = "frozenlist-1.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:8969190d709e7c48ea386db202d708eb94bdb29207a1f269bab1196ce0dcca1f"}, - {file = "frozenlist-1.5.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7a1a048f9215c90973402e26c01d1cff8a209e1f1b53f72b95c13db61b00f953"}, - {file = "frozenlist-1.5.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:dd47a5181ce5fcb463b5d9e17ecfdb02b678cca31280639255ce9d0e5aa67af0"}, - {file = "frozenlist-1.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1431d60b36d15cda188ea222033eec8e0eab488f39a272461f2e6d9e1a8e63c2"}, - {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6482a5851f5d72767fbd0e507e80737f9c8646ae7fd303def99bfe813f76cf7f"}, - {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:44c49271a937625619e862baacbd037a7ef86dd1ee215afc298a417ff3270608"}, - {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:12f78f98c2f1c2429d42e6a485f433722b0061d5c0b0139efa64f396efb5886b"}, - {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ce3aa154c452d2467487765e3adc730a8c153af77ad84096bc19ce19a2400840"}, - {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9b7dc0c4338e6b8b091e8faf0db3168a37101943e687f373dce00959583f7439"}, - {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:45e0896250900b5aa25180f9aec243e84e92ac84bd4a74d9ad4138ef3f5c97de"}, - {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:561eb1c9579d495fddb6da8959fd2a1fca2c6d060d4113f5844b433fc02f2641"}, - {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:df6e2f325bfee1f49f81aaac97d2aa757c7646534a06f8f577ce184afe2f0a9e"}, - {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:140228863501b44b809fb39ec56b5d4071f4d0aa6d216c19cbb08b8c5a7eadb9"}, - {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7707a25d6a77f5d27ea7dc7d1fc608aa0a478193823f88511ef5e6b8a48f9d03"}, - {file = "frozenlist-1.5.0-cp313-cp313-win32.whl", hash = "sha256:31a9ac2b38ab9b5a8933b693db4939764ad3f299fcaa931a3e605bc3460e693c"}, - {file = "frozenlist-1.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:11aabdd62b8b9c4b84081a3c246506d1cddd2dd93ff0ad53ede5defec7886b28"}, - {file = "frozenlist-1.5.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:dd94994fc91a6177bfaafd7d9fd951bc8689b0a98168aa26b5f543868548d3ca"}, - {file = "frozenlist-1.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2d0da8bbec082bf6bf18345b180958775363588678f64998c2b7609e34719b10"}, - {file = "frozenlist-1.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:73f2e31ea8dd7df61a359b731716018c2be196e5bb3b74ddba107f694fbd7604"}, - {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:828afae9f17e6de596825cf4228ff28fbdf6065974e5ac1410cecc22f699d2b3"}, - {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f1577515d35ed5649d52ab4319db757bb881ce3b2b796d7283e6634d99ace307"}, - {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2150cc6305a2c2ab33299453e2968611dacb970d2283a14955923062c8d00b10"}, - {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a72b7a6e3cd2725eff67cd64c8f13335ee18fc3c7befc05aed043d24c7b9ccb9"}, - {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c16d2fa63e0800723139137d667e1056bee1a1cf7965153d2d104b62855e9b99"}, - {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:17dcc32fc7bda7ce5875435003220a457bcfa34ab7924a49a1c19f55b6ee185c"}, - {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:97160e245ea33d8609cd2b8fd997c850b56db147a304a262abc2b3be021a9171"}, - {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:f1e6540b7fa044eee0bb5111ada694cf3dc15f2b0347ca125ee9ca984d5e9e6e"}, - {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:91d6c171862df0a6c61479d9724f22efb6109111017c87567cfeb7b5d1449fdf"}, - {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:c1fac3e2ace2eb1052e9f7c7db480818371134410e1f5c55d65e8f3ac6d1407e"}, - {file = "frozenlist-1.5.0-cp38-cp38-win32.whl", hash = "sha256:b97f7b575ab4a8af9b7bc1d2ef7f29d3afee2226bd03ca3875c16451ad5a7723"}, - {file = "frozenlist-1.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:374ca2dabdccad8e2a76d40b1d037f5bd16824933bf7bcea3e59c891fd4a0923"}, - {file = "frozenlist-1.5.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:9bbcdfaf4af7ce002694a4e10a0159d5a8d20056a12b05b45cea944a4953f972"}, - {file = "frozenlist-1.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1893f948bf6681733aaccf36c5232c231e3b5166d607c5fa77773611df6dc336"}, - {file = "frozenlist-1.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2b5e23253bb709ef57a8e95e6ae48daa9ac5f265637529e4ce6b003a37b2621f"}, - {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f253985bb515ecd89629db13cb58d702035ecd8cfbca7d7a7e29a0e6d39af5f"}, - {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:04a5c6babd5e8fb7d3c871dc8b321166b80e41b637c31a995ed844a6139942b6"}, - {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9fe0f1c29ba24ba6ff6abf688cb0b7cf1efab6b6aa6adc55441773c252f7411"}, - {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:226d72559fa19babe2ccd920273e767c96a49b9d3d38badd7c91a0fdeda8ea08"}, - {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15b731db116ab3aedec558573c1a5eec78822b32292fe4f2f0345b7f697745c2"}, - {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:366d8f93e3edfe5a918c874702f78faac300209a4d5bf38352b2c1bdc07a766d"}, - {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:1b96af8c582b94d381a1c1f51ffaedeb77c821c690ea5f01da3d70a487dd0a9b"}, - {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:c03eff4a41bd4e38415cbed054bbaff4a075b093e2394b6915dca34a40d1e38b"}, - {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:50cf5e7ee9b98f22bdecbabf3800ae78ddcc26e4a435515fc72d97903e8488e0"}, - {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1e76bfbc72353269c44e0bc2cfe171900fbf7f722ad74c9a7b638052afe6a00c"}, - {file = "frozenlist-1.5.0-cp39-cp39-win32.whl", hash = "sha256:666534d15ba8f0fda3f53969117383d5dc021266b3c1a42c9ec4855e4b58b9d3"}, - {file = "frozenlist-1.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:5c28f4b5dbef8a0d8aad0d4de24d1e9e981728628afaf4ea0792f5d0939372f0"}, - {file = "frozenlist-1.5.0-py3-none-any.whl", hash = "sha256:d994863bba198a4a518b467bb971c56e1db3f180a25c6cf7bb1949c267f748c3"}, - {file = "frozenlist-1.5.0.tar.gz", hash = "sha256:81d5af29e61b9c8348e876d442253723928dce6433e0e76cd925cd83f1b4b817"}, + {file = "frozenlist-1.8.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b37f6d31b3dcea7deb5e9696e529a6aa4a898adc33db82da12e4c60a7c4d2011"}, + {file = "frozenlist-1.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ef2b7b394f208233e471abc541cc6991f907ffd47dc72584acee3147899d6565"}, + {file = "frozenlist-1.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a88f062f072d1589b7b46e951698950e7da00442fc1cacbe17e19e025dc327ad"}, + {file = "frozenlist-1.8.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f57fb59d9f385710aa7060e89410aeb5058b99e62f4d16b08b91986b9a2140c2"}, + {file = "frozenlist-1.8.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:799345ab092bee59f01a915620b5d014698547afd011e691a208637312db9186"}, + {file = "frozenlist-1.8.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c23c3ff005322a6e16f71bf8692fcf4d5a304aaafe1e262c98c6d4adc7be863e"}, + {file = "frozenlist-1.8.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8a76ea0f0b9dfa06f254ee06053d93a600865b3274358ca48a352ce4f0798450"}, + {file = "frozenlist-1.8.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c7366fe1418a6133d5aa824ee53d406550110984de7637d65a178010f759c6ef"}, + {file = "frozenlist-1.8.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:13d23a45c4cebade99340c4165bd90eeb4a56c6d8a9d8aa49568cac19a6d0dc4"}, + {file = "frozenlist-1.8.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:e4a3408834f65da56c83528fb52ce7911484f0d1eaf7b761fc66001db1646eff"}, + {file = "frozenlist-1.8.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:42145cd2748ca39f32801dad54aeea10039da6f86e303659db90db1c4b614c8c"}, + {file = "frozenlist-1.8.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:e2de870d16a7a53901e41b64ffdf26f2fbb8917b3e6ebf398098d72c5b20bd7f"}, + {file = "frozenlist-1.8.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:20e63c9493d33ee48536600d1a5c95eefc870cd71e7ab037763d1fbb89cc51e7"}, + {file = "frozenlist-1.8.0-cp310-cp310-win32.whl", hash = "sha256:adbeebaebae3526afc3c96fad434367cafbfd1b25d72369a9e5858453b1bb71a"}, + {file = "frozenlist-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:667c3777ca571e5dbeb76f331562ff98b957431df140b54c85fd4d52eea8d8f6"}, + {file = "frozenlist-1.8.0-cp310-cp310-win_arm64.whl", hash = "sha256:80f85f0a7cc86e7a54c46d99c9e1318ff01f4687c172ede30fd52d19d1da1c8e"}, + {file = "frozenlist-1.8.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:09474e9831bc2b2199fad6da3c14c7b0fbdd377cce9d3d77131be28906cb7d84"}, + {file = "frozenlist-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:17c883ab0ab67200b5f964d2b9ed6b00971917d5d8a92df149dc2c9779208ee9"}, + {file = "frozenlist-1.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fa47e444b8ba08fffd1c18e8cdb9a75db1b6a27f17507522834ad13ed5922b93"}, + {file = "frozenlist-1.8.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2552f44204b744fba866e573be4c1f9048d6a324dfe14475103fd51613eb1d1f"}, + {file = "frozenlist-1.8.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:957e7c38f250991e48a9a73e6423db1bb9dd14e722a10f6b8bb8e16a0f55f695"}, + {file = "frozenlist-1.8.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8585e3bb2cdea02fc88ffa245069c36555557ad3609e83be0ec71f54fd4abb52"}, + {file = "frozenlist-1.8.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:edee74874ce20a373d62dc28b0b18b93f645633c2943fd90ee9d898550770581"}, + {file = "frozenlist-1.8.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c9a63152fe95756b85f31186bddf42e4c02c6321207fd6601a1c89ebac4fe567"}, + {file = "frozenlist-1.8.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b6db2185db9be0a04fecf2f241c70b63b1a242e2805be291855078f2b404dd6b"}, + {file = "frozenlist-1.8.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:f4be2e3d8bc8aabd566f8d5b8ba7ecc09249d74ba3c9ed52e54dc23a293f0b92"}, + {file = "frozenlist-1.8.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:c8d1634419f39ea6f5c427ea2f90ca85126b54b50837f31497f3bf38266e853d"}, + {file = "frozenlist-1.8.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:1a7fa382a4a223773ed64242dbe1c9c326ec09457e6b8428efb4118c685c3dfd"}, + {file = "frozenlist-1.8.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:11847b53d722050808926e785df837353bd4d75f1d494377e59b23594d834967"}, + {file = "frozenlist-1.8.0-cp311-cp311-win32.whl", hash = "sha256:27c6e8077956cf73eadd514be8fb04d77fc946a7fe9f7fe167648b0b9085cc25"}, + {file = "frozenlist-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:ac913f8403b36a2c8610bbfd25b8013488533e71e62b4b4adce9c86c8cea905b"}, + {file = "frozenlist-1.8.0-cp311-cp311-win_arm64.whl", hash = "sha256:d4d3214a0f8394edfa3e303136d0575eece0745ff2b47bd2cb2e66dd92d4351a"}, + {file = "frozenlist-1.8.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:78f7b9e5d6f2fdb88cdde9440dc147259b62b9d3b019924def9f6478be254ac1"}, + {file = "frozenlist-1.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:229bf37d2e4acdaf808fd3f06e854a4a7a3661e871b10dc1f8f1896a3b05f18b"}, + {file = "frozenlist-1.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f833670942247a14eafbb675458b4e61c82e002a148f49e68257b79296e865c4"}, + {file = "frozenlist-1.8.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:494a5952b1c597ba44e0e78113a7266e656b9794eec897b19ead706bd7074383"}, + {file = "frozenlist-1.8.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96f423a119f4777a4a056b66ce11527366a8bb92f54e541ade21f2374433f6d4"}, + {file = "frozenlist-1.8.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3462dd9475af2025c31cc61be6652dfa25cbfb56cbbf52f4ccfe029f38decaf8"}, + {file = "frozenlist-1.8.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4c800524c9cd9bac5166cd6f55285957fcfc907db323e193f2afcd4d9abd69b"}, + {file = "frozenlist-1.8.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d6a5df73acd3399d893dafc71663ad22534b5aa4f94e8a2fabfe856c3c1b6a52"}, + {file = "frozenlist-1.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:405e8fe955c2280ce66428b3ca55e12b3c4e9c336fb2103a4937e891c69a4a29"}, + {file = "frozenlist-1.8.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:908bd3f6439f2fef9e85031b59fd4f1297af54415fb60e4254a95f75b3cab3f3"}, + {file = "frozenlist-1.8.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:294e487f9ec720bd8ffcebc99d575f7eff3568a08a253d1ee1a0378754b74143"}, + {file = "frozenlist-1.8.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:74c51543498289c0c43656701be6b077f4b265868fa7f8a8859c197006efb608"}, + {file = "frozenlist-1.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:776f352e8329135506a1d6bf16ac3f87bc25b28e765949282dcc627af36123aa"}, + {file = "frozenlist-1.8.0-cp312-cp312-win32.whl", hash = "sha256:433403ae80709741ce34038da08511d4a77062aa924baf411ef73d1146e74faf"}, + {file = "frozenlist-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:34187385b08f866104f0c0617404c8eb08165ab1272e884abc89c112e9c00746"}, + {file = "frozenlist-1.8.0-cp312-cp312-win_arm64.whl", hash = "sha256:fe3c58d2f5db5fbd18c2987cba06d51b0529f52bc3a6cdc33d3f4eab725104bd"}, + {file = "frozenlist-1.8.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8d92f1a84bb12d9e56f818b3a746f3efba93c1b63c8387a73dde655e1e42282a"}, + {file = "frozenlist-1.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:96153e77a591c8adc2ee805756c61f59fef4cf4073a9275ee86fe8cba41241f7"}, + {file = "frozenlist-1.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f21f00a91358803399890ab167098c131ec2ddd5f8f5fd5fe9c9f2c6fcd91e40"}, + {file = "frozenlist-1.8.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fb30f9626572a76dfe4293c7194a09fb1fe93ba94c7d4f720dfae3b646b45027"}, + {file = "frozenlist-1.8.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eaa352d7047a31d87dafcacbabe89df0aa506abb5b1b85a2fb91bc3faa02d822"}, + {file = "frozenlist-1.8.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:03ae967b4e297f58f8c774c7eabcce57fe3c2434817d4385c50661845a058121"}, + {file = "frozenlist-1.8.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f6292f1de555ffcc675941d65fffffb0a5bcd992905015f85d0592201793e0e5"}, + {file = "frozenlist-1.8.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29548f9b5b5e3460ce7378144c3010363d8035cea44bc0bf02d57f5a685e084e"}, + {file = "frozenlist-1.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ec3cc8c5d4084591b4237c0a272cc4f50a5b03396a47d9caaf76f5d7b38a4f11"}, + {file = "frozenlist-1.8.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:517279f58009d0b1f2e7c1b130b377a349405da3f7621ed6bfae50b10adf20c1"}, + {file = "frozenlist-1.8.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:db1e72ede2d0d7ccb213f218df6a078a9c09a7de257c2fe8fcef16d5925230b1"}, + {file = "frozenlist-1.8.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b4dec9482a65c54a5044486847b8a66bf10c9cb4926d42927ec4e8fd5db7fed8"}, + {file = "frozenlist-1.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:21900c48ae04d13d416f0e1e0c4d81f7931f73a9dfa0b7a8746fb2fe7dd970ed"}, + {file = "frozenlist-1.8.0-cp313-cp313-win32.whl", hash = "sha256:8b7b94a067d1c504ee0b16def57ad5738701e4ba10cec90529f13fa03c833496"}, + {file = "frozenlist-1.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:878be833caa6a3821caf85eb39c5ba92d28e85df26d57afb06b35b2efd937231"}, + {file = "frozenlist-1.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:44389d135b3ff43ba8cc89ff7f51f5a0bb6b63d829c8300f79a2fe4fe61bcc62"}, + {file = "frozenlist-1.8.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:e25ac20a2ef37e91c1b39938b591457666a0fa835c7783c3a8f33ea42870db94"}, + {file = "frozenlist-1.8.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:07cdca25a91a4386d2e76ad992916a85038a9b97561bf7a3fd12d5d9ce31870c"}, + {file = "frozenlist-1.8.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4e0c11f2cc6717e0a741f84a527c52616140741cd812a50422f83dc31749fb52"}, + {file = "frozenlist-1.8.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b3210649ee28062ea6099cfda39e147fa1bc039583c8ee4481cb7811e2448c51"}, + {file = "frozenlist-1.8.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:581ef5194c48035a7de2aefc72ac6539823bb71508189e5de01d60c9dcd5fa65"}, + {file = "frozenlist-1.8.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3ef2d026f16a2b1866e1d86fc4e1291e1ed8a387b2c333809419a2f8b3a77b82"}, + {file = "frozenlist-1.8.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5500ef82073f599ac84d888e3a8c1f77ac831183244bfd7f11eaa0289fb30714"}, + {file = "frozenlist-1.8.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:50066c3997d0091c411a66e710f4e11752251e6d2d73d70d8d5d4c76442a199d"}, + {file = "frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:5c1c8e78426e59b3f8005e9b19f6ff46e5845895adbde20ece9218319eca6506"}, + {file = "frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:eefdba20de0d938cec6a89bd4d70f346a03108a19b9df4248d3cf0d88f1b0f51"}, + {file = "frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:cf253e0e1c3ceb4aaff6df637ce033ff6535fb8c70a764a8f46aafd3d6ab798e"}, + {file = "frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:032efa2674356903cd0261c4317a561a6850f3ac864a63fc1583147fb05a79b0"}, + {file = "frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6da155091429aeba16851ecb10a9104a108bcd32f6c1642867eadaee401c1c41"}, + {file = "frozenlist-1.8.0-cp313-cp313t-win32.whl", hash = "sha256:0f96534f8bfebc1a394209427d0f8a63d343c9779cda6fc25e8e121b5fd8555b"}, + {file = "frozenlist-1.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5d63a068f978fc69421fb0e6eb91a9603187527c86b7cd3f534a5b77a592b888"}, + {file = "frozenlist-1.8.0-cp313-cp313t-win_arm64.whl", hash = "sha256:bf0a7e10b077bf5fb9380ad3ae8ce20ef919a6ad93b4552896419ac7e1d8e042"}, + {file = "frozenlist-1.8.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cee686f1f4cadeb2136007ddedd0aaf928ab95216e7691c63e50a8ec066336d0"}, + {file = "frozenlist-1.8.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:119fb2a1bd47307e899c2fac7f28e85b9a543864df47aa7ec9d3c1b4545f096f"}, + {file = "frozenlist-1.8.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4970ece02dbc8c3a92fcc5228e36a3e933a01a999f7094ff7c23fbd2beeaa67c"}, + {file = "frozenlist-1.8.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:cba69cb73723c3f329622e34bdbf5ce1f80c21c290ff04256cff1cd3c2036ed2"}, + {file = "frozenlist-1.8.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:778a11b15673f6f1df23d9586f83c4846c471a8af693a22e066508b77d201ec8"}, + {file = "frozenlist-1.8.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0325024fe97f94c41c08872db482cf8ac4800d80e79222c6b0b7b162d5b13686"}, + {file = "frozenlist-1.8.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:97260ff46b207a82a7567b581ab4190bd4dfa09f4db8a8b49d1a958f6aa4940e"}, + {file = "frozenlist-1.8.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:54b2077180eb7f83dd52c40b2750d0a9f175e06a42e3213ce047219de902717a"}, + {file = "frozenlist-1.8.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2f05983daecab868a31e1da44462873306d3cbfd76d1f0b5b69c473d21dbb128"}, + {file = "frozenlist-1.8.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:33f48f51a446114bc5d251fb2954ab0164d5be02ad3382abcbfe07e2531d650f"}, + {file = "frozenlist-1.8.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:154e55ec0655291b5dd1b8731c637ecdb50975a2ae70c606d100750a540082f7"}, + {file = "frozenlist-1.8.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:4314debad13beb564b708b4a496020e5306c7333fa9a3ab90374169a20ffab30"}, + {file = "frozenlist-1.8.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:073f8bf8becba60aa931eb3bc420b217bb7d5b8f4750e6f8b3be7f3da85d38b7"}, + {file = "frozenlist-1.8.0-cp314-cp314-win32.whl", hash = "sha256:bac9c42ba2ac65ddc115d930c78d24ab8d4f465fd3fc473cdedfccadb9429806"}, + {file = "frozenlist-1.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:3e0761f4d1a44f1d1a47996511752cf3dcec5bbdd9cc2b4fe595caf97754b7a0"}, + {file = "frozenlist-1.8.0-cp314-cp314-win_arm64.whl", hash = "sha256:d1eaff1d00c7751b7c6662e9c5ba6eb2c17a2306ba5e2a37f24ddf3cc953402b"}, + {file = "frozenlist-1.8.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:d3bb933317c52d7ea5004a1c442eef86f426886fba134ef8cf4226ea6ee1821d"}, + {file = "frozenlist-1.8.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:8009897cdef112072f93a0efdce29cd819e717fd2f649ee3016efd3cd885a7ed"}, + {file = "frozenlist-1.8.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2c5dcbbc55383e5883246d11fd179782a9d07a986c40f49abe89ddf865913930"}, + {file = "frozenlist-1.8.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:39ecbc32f1390387d2aa4f5a995e465e9e2f79ba3adcac92d68e3e0afae6657c"}, + {file = "frozenlist-1.8.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92db2bf818d5cc8d9c1f1fc56b897662e24ea5adb36ad1f1d82875bd64e03c24"}, + {file = "frozenlist-1.8.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2dc43a022e555de94c3b68a4ef0b11c4f747d12c024a520c7101709a2144fb37"}, + {file = "frozenlist-1.8.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cb89a7f2de3602cfed448095bab3f178399646ab7c61454315089787df07733a"}, + {file = "frozenlist-1.8.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:33139dc858c580ea50e7e60a1b0ea003efa1fd42e6ec7fdbad78fff65fad2fd2"}, + {file = "frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:168c0969a329b416119507ba30b9ea13688fafffac1b7822802537569a1cb0ef"}, + {file = "frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:28bd570e8e189d7f7b001966435f9dac6718324b5be2990ac496cf1ea9ddb7fe"}, + {file = "frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b2a095d45c5d46e5e79ba1e5b9cb787f541a8dee0433836cea4b96a2c439dcd8"}, + {file = "frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:eab8145831a0d56ec9c4139b6c3e594c7a83c2c8be25d5bcf2d86136a532287a"}, + {file = "frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:974b28cf63cc99dfb2188d8d222bc6843656188164848c4f679e63dae4b0708e"}, + {file = "frozenlist-1.8.0-cp314-cp314t-win32.whl", hash = "sha256:342c97bf697ac5480c0a7ec73cd700ecfa5a8a40ac923bd035484616efecc2df"}, + {file = "frozenlist-1.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:06be8f67f39c8b1dc671f5d83aaefd3358ae5cdcf8314552c57e7ed3e6475bdd"}, + {file = "frozenlist-1.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:102e6314ca4da683dca92e3b1355490fed5f313b768500084fbe6371fddfdb79"}, + {file = "frozenlist-1.8.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:d8b7138e5cd0647e4523d6685b0eac5d4be9a184ae9634492f25c6eb38c12a47"}, + {file = "frozenlist-1.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a6483e309ca809f1efd154b4d37dc6d9f61037d6c6a81c2dc7a15cb22c8c5dca"}, + {file = "frozenlist-1.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1b9290cf81e95e93fdf90548ce9d3c1211cf574b8e3f4b3b7cb0537cf2227068"}, + {file = "frozenlist-1.8.0-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:59a6a5876ca59d1b63af8cd5e7ffffb024c3dc1e9cf9301b21a2e76286505c95"}, + {file = "frozenlist-1.8.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6dc4126390929823e2d2d9dc79ab4046ed74680360fc5f38b585c12c66cdf459"}, + {file = "frozenlist-1.8.0-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:332db6b2563333c5671fecacd085141b5800cb866be16d5e3eb15a2086476675"}, + {file = "frozenlist-1.8.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9ff15928d62a0b80bb875655c39bf517938c7d589554cbd2669be42d97c2cb61"}, + {file = "frozenlist-1.8.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7bf6cdf8e07c8151fba6fe85735441240ec7f619f935a5205953d58009aef8c6"}, + {file = "frozenlist-1.8.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:48e6d3f4ec5c7273dfe83ff27c91083c6c9065af655dc2684d2c200c94308bb5"}, + {file = "frozenlist-1.8.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:1a7607e17ad33361677adcd1443edf6f5da0ce5e5377b798fba20fae194825f3"}, + {file = "frozenlist-1.8.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:5a3a935c3a4e89c733303a2d5a7c257ea44af3a56c8202df486b7f5de40f37e1"}, + {file = "frozenlist-1.8.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:940d4a017dbfed9daf46a3b086e1d2167e7012ee297fef9e1c545c4d022f5178"}, + {file = "frozenlist-1.8.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:b9be22a69a014bc47e78072d0ecae716f5eb56c15238acca0f43d6eb8e4a5bda"}, + {file = "frozenlist-1.8.0-cp39-cp39-win32.whl", hash = "sha256:1aa77cb5697069af47472e39612976ed05343ff2e84a3dcf15437b232cbfd087"}, + {file = "frozenlist-1.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:7398c222d1d405e796970320036b1b563892b65809d9e5261487bb2c7f7b5c6a"}, + {file = "frozenlist-1.8.0-cp39-cp39-win_arm64.whl", hash = "sha256:b4f3b365f31c6cd4af24545ca0a244a53688cad8834e32f56831c4923b50a103"}, + {file = "frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d"}, + {file = "frozenlist-1.8.0.tar.gz", hash = "sha256:3ede829ed8d842f6cd48fc7081d7a41001a56f1f38603f9d49bf3020d59a31ad"}, ] [[package]] @@ -760,88 +922,97 @@ grpc = ["grpcio (>=1.44.0,<2.0.0)"] [[package]] name = "grpcio" -version = "1.70.0" +version = "1.75.1" description = "HTTP/2-based RPC framework" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["main"] files = [ - {file = "grpcio-1.70.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:95469d1977429f45fe7df441f586521361e235982a0b39e33841549143ae2851"}, - {file = "grpcio-1.70.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:ed9718f17fbdb472e33b869c77a16d0b55e166b100ec57b016dc7de9c8d236bf"}, - {file = "grpcio-1.70.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:374d014f29f9dfdb40510b041792e0e2828a1389281eb590df066e1cc2b404e5"}, - {file = "grpcio-1.70.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2af68a6f5c8f78d56c145161544ad0febbd7479524a59c16b3e25053f39c87f"}, - {file = "grpcio-1.70.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce7df14b2dcd1102a2ec32f621cc9fab6695effef516efbc6b063ad749867295"}, - {file = "grpcio-1.70.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:c78b339869f4dbf89881e0b6fbf376313e4f845a42840a7bdf42ee6caed4b11f"}, - {file = "grpcio-1.70.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:58ad9ba575b39edef71f4798fdb5c7b6d02ad36d47949cd381d4392a5c9cbcd3"}, - {file = "grpcio-1.70.0-cp310-cp310-win32.whl", hash = "sha256:2b0d02e4b25a5c1f9b6c7745d4fa06efc9fd6a611af0fb38d3ba956786b95199"}, - {file = "grpcio-1.70.0-cp310-cp310-win_amd64.whl", hash = "sha256:0de706c0a5bb9d841e353f6343a9defc9fc35ec61d6eb6111802f3aa9fef29e1"}, - {file = "grpcio-1.70.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:17325b0be0c068f35770f944124e8839ea3185d6d54862800fc28cc2ffad205a"}, - {file = "grpcio-1.70.0-cp311-cp311-macosx_10_14_universal2.whl", hash = "sha256:dbe41ad140df911e796d4463168e33ef80a24f5d21ef4d1e310553fcd2c4a386"}, - {file = "grpcio-1.70.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:5ea67c72101d687d44d9c56068328da39c9ccba634cabb336075fae2eab0d04b"}, - {file = "grpcio-1.70.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cb5277db254ab7586769e490b7b22f4ddab3876c490da0a1a9d7c695ccf0bf77"}, - {file = "grpcio-1.70.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7831a0fc1beeeb7759f737f5acd9fdcda520e955049512d68fda03d91186eea"}, - {file = "grpcio-1.70.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:27cc75e22c5dba1fbaf5a66c778e36ca9b8ce850bf58a9db887754593080d839"}, - {file = "grpcio-1.70.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d63764963412e22f0491d0d32833d71087288f4e24cbcddbae82476bfa1d81fd"}, - {file = "grpcio-1.70.0-cp311-cp311-win32.whl", hash = "sha256:bb491125103c800ec209d84c9b51f1c60ea456038e4734688004f377cfacc113"}, - {file = "grpcio-1.70.0-cp311-cp311-win_amd64.whl", hash = "sha256:d24035d49e026353eb042bf7b058fb831db3e06d52bee75c5f2f3ab453e71aca"}, - {file = "grpcio-1.70.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:ef4c14508299b1406c32bdbb9fb7b47612ab979b04cf2b27686ea31882387cff"}, - {file = "grpcio-1.70.0-cp312-cp312-macosx_10_14_universal2.whl", hash = "sha256:aa47688a65643afd8b166928a1da6247d3f46a2784d301e48ca1cc394d2ffb40"}, - {file = "grpcio-1.70.0-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:880bfb43b1bb8905701b926274eafce5c70a105bc6b99e25f62e98ad59cb278e"}, - {file = "grpcio-1.70.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9e654c4b17d07eab259d392e12b149c3a134ec52b11ecdc6a515b39aceeec898"}, - {file = "grpcio-1.70.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2394e3381071045a706ee2eeb6e08962dd87e8999b90ac15c55f56fa5a8c9597"}, - {file = "grpcio-1.70.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:b3c76701428d2df01964bc6479422f20e62fcbc0a37d82ebd58050b86926ef8c"}, - {file = "grpcio-1.70.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ac073fe1c4cd856ebcf49e9ed6240f4f84d7a4e6ee95baa5d66ea05d3dd0df7f"}, - {file = "grpcio-1.70.0-cp312-cp312-win32.whl", hash = "sha256:cd24d2d9d380fbbee7a5ac86afe9787813f285e684b0271599f95a51bce33528"}, - {file = "grpcio-1.70.0-cp312-cp312-win_amd64.whl", hash = "sha256:0495c86a55a04a874c7627fd33e5beaee771917d92c0e6d9d797628ac40e7655"}, - {file = "grpcio-1.70.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:aa573896aeb7d7ce10b1fa425ba263e8dddd83d71530d1322fd3a16f31257b4a"}, - {file = "grpcio-1.70.0-cp313-cp313-macosx_10_14_universal2.whl", hash = "sha256:d405b005018fd516c9ac529f4b4122342f60ec1cee181788249372524e6db429"}, - {file = "grpcio-1.70.0-cp313-cp313-manylinux_2_17_aarch64.whl", hash = "sha256:f32090238b720eb585248654db8e3afc87b48d26ac423c8dde8334a232ff53c9"}, - {file = "grpcio-1.70.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dfa089a734f24ee5f6880c83d043e4f46bf812fcea5181dcb3a572db1e79e01c"}, - {file = "grpcio-1.70.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f19375f0300b96c0117aca118d400e76fede6db6e91f3c34b7b035822e06c35f"}, - {file = "grpcio-1.70.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:7c73c42102e4a5ec76608d9b60227d917cea46dff4d11d372f64cbeb56d259d0"}, - {file = "grpcio-1.70.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:0a5c78d5198a1f0aa60006cd6eb1c912b4a1520b6a3968e677dbcba215fabb40"}, - {file = "grpcio-1.70.0-cp313-cp313-win32.whl", hash = "sha256:fe9dbd916df3b60e865258a8c72ac98f3ac9e2a9542dcb72b7a34d236242a5ce"}, - {file = "grpcio-1.70.0-cp313-cp313-win_amd64.whl", hash = "sha256:4119fed8abb7ff6c32e3d2255301e59c316c22d31ab812b3fbcbaf3d0d87cc68"}, - {file = "grpcio-1.70.0-cp38-cp38-linux_armv7l.whl", hash = "sha256:8058667a755f97407fca257c844018b80004ae8035565ebc2812cc550110718d"}, - {file = "grpcio-1.70.0-cp38-cp38-macosx_10_14_universal2.whl", hash = "sha256:879a61bf52ff8ccacbedf534665bb5478ec8e86ad483e76fe4f729aaef867cab"}, - {file = "grpcio-1.70.0-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:0ba0a173f4feacf90ee618fbc1a27956bfd21260cd31ced9bc707ef551ff7dc7"}, - {file = "grpcio-1.70.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:558c386ecb0148f4f99b1a65160f9d4b790ed3163e8610d11db47838d452512d"}, - {file = "grpcio-1.70.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:412faabcc787bbc826f51be261ae5fa996b21263de5368a55dc2cf824dc5090e"}, - {file = "grpcio-1.70.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:3b0f01f6ed9994d7a0b27eeddea43ceac1b7e6f3f9d86aeec0f0064b8cf50fdb"}, - {file = "grpcio-1.70.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:7385b1cb064734005204bc8994eed7dcb801ed6c2eda283f613ad8c6c75cf873"}, - {file = "grpcio-1.70.0-cp38-cp38-win32.whl", hash = "sha256:07269ff4940f6fb6710951116a04cd70284da86d0a4368fd5a3b552744511f5a"}, - {file = "grpcio-1.70.0-cp38-cp38-win_amd64.whl", hash = "sha256:aba19419aef9b254e15011b230a180e26e0f6864c90406fdbc255f01d83bc83c"}, - {file = "grpcio-1.70.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:4f1937f47c77392ccd555728f564a49128b6a197a05a5cd527b796d36f3387d0"}, - {file = "grpcio-1.70.0-cp39-cp39-macosx_10_14_universal2.whl", hash = "sha256:0cd430b9215a15c10b0e7d78f51e8a39d6cf2ea819fd635a7214fae600b1da27"}, - {file = "grpcio-1.70.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:e27585831aa6b57b9250abaf147003e126cd3a6c6ca0c531a01996f31709bed1"}, - {file = "grpcio-1.70.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c1af8e15b0f0fe0eac75195992a63df17579553b0c4af9f8362cc7cc99ccddf4"}, - {file = "grpcio-1.70.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cbce24409beaee911c574a3d75d12ffb8c3e3dd1b813321b1d7a96bbcac46bf4"}, - {file = "grpcio-1.70.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:ff4a8112a79464919bb21c18e956c54add43ec9a4850e3949da54f61c241a4a6"}, - {file = "grpcio-1.70.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5413549fdf0b14046c545e19cfc4eb1e37e9e1ebba0ca390a8d4e9963cab44d2"}, - {file = "grpcio-1.70.0-cp39-cp39-win32.whl", hash = "sha256:b745d2c41b27650095e81dea7091668c040457483c9bdb5d0d9de8f8eb25e59f"}, - {file = "grpcio-1.70.0-cp39-cp39-win_amd64.whl", hash = "sha256:a31d7e3b529c94e930a117b2175b2efd179d96eb3c7a21ccb0289a8ab05b645c"}, - {file = "grpcio-1.70.0.tar.gz", hash = "sha256:8d1584a68d5922330025881e63a6c1b54cc8117291d382e4fa69339b6d914c56"}, + {file = "grpcio-1.75.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:1712b5890b22547dd29f3215c5788d8fc759ce6dd0b85a6ba6e2731f2d04c088"}, + {file = "grpcio-1.75.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:8d04e101bba4b55cea9954e4aa71c24153ba6182481b487ff376da28d4ba46cf"}, + {file = "grpcio-1.75.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:683cfc70be0c1383449097cba637317e4737a357cfc185d887fd984206380403"}, + {file = "grpcio-1.75.1-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:491444c081a54dcd5e6ada57314321ae526377f498d4aa09d975c3241c5b9e1c"}, + {file = "grpcio-1.75.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ce08d4e112d0d38487c2b631ec8723deac9bc404e9c7b1011426af50a79999e4"}, + {file = "grpcio-1.75.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5a2acda37fc926ccc4547977ac3e56b1df48fe200de968e8c8421f6e3093df6c"}, + {file = "grpcio-1.75.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:745c5fe6bf05df6a04bf2d11552c7d867a2690759e7ab6b05c318a772739bd75"}, + {file = "grpcio-1.75.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:259526a7159d39e2db40d566fe3e8f8e034d0fb2db5bf9c00e09aace655a4c2b"}, + {file = "grpcio-1.75.1-cp310-cp310-win32.whl", hash = "sha256:f4b29b9aabe33fed5df0a85e5f13b09ff25e2c05bd5946d25270a8bd5682dac9"}, + {file = "grpcio-1.75.1-cp310-cp310-win_amd64.whl", hash = "sha256:cf2e760978dcce7ff7d465cbc7e276c3157eedc4c27aa6de7b594c7a295d3d61"}, + {file = "grpcio-1.75.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:573855ca2e58e35032aff30bfbd1ee103fbcf4472e4b28d4010757700918e326"}, + {file = "grpcio-1.75.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:6a4996a2c8accc37976dc142d5991adf60733e223e5c9a2219e157dc6a8fd3a2"}, + {file = "grpcio-1.75.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b1ea1bbe77ecbc1be00af2769f4ae4a88ce93be57a4f3eebd91087898ed749f9"}, + {file = "grpcio-1.75.1-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:e5b425aee54cc5e3e3c58f00731e8a33f5567965d478d516d35ef99fd648ab68"}, + {file = "grpcio-1.75.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0049a7bf547dafaeeb1db17079ce79596c298bfe308fc084d023c8907a845b9a"}, + {file = "grpcio-1.75.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5b8ea230c7f77c0a1a3208a04a1eda164633fb0767b4cefd65a01079b65e5b1f"}, + {file = "grpcio-1.75.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:36990d629c3c9fb41e546414e5af52d0a7af37ce7113d9682c46d7e2919e4cca"}, + {file = "grpcio-1.75.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b10ad908118d38c2453ade7ff790e5bce36580c3742919007a2a78e3a1e521ca"}, + {file = "grpcio-1.75.1-cp311-cp311-win32.whl", hash = "sha256:d6be2b5ee7bea656c954dcf6aa8093c6f0e6a3ef9945c99d99fcbfc88c5c0bfe"}, + {file = "grpcio-1.75.1-cp311-cp311-win_amd64.whl", hash = "sha256:61c692fb05956b17dd6d1ab480f7f10ad0536dba3bc8fd4e3c7263dc244ed772"}, + {file = "grpcio-1.75.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:7b888b33cd14085d86176b1628ad2fcbff94cfbbe7809465097aa0132e58b018"}, + {file = "grpcio-1.75.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:8775036efe4ad2085975531d221535329f5dac99b6c2a854a995456098f99546"}, + {file = "grpcio-1.75.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:bb658f703468d7fbb5dcc4037c65391b7dc34f808ac46ed9136c24fc5eeb041d"}, + {file = "grpcio-1.75.1-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:4b7177a1cdb3c51b02b0c0a256b0a72fdab719600a693e0e9037949efffb200b"}, + {file = "grpcio-1.75.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7d4fa6ccc3ec2e68a04f7b883d354d7fea22a34c44ce535a2f0c0049cf626ddf"}, + {file = "grpcio-1.75.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3d86880ecaeb5b2f0a8afa63824de93adb8ebe4e49d0e51442532f4e08add7d6"}, + {file = "grpcio-1.75.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a8041d2f9e8a742aeae96f4b047ee44e73619f4f9d24565e84d5446c623673b6"}, + {file = "grpcio-1.75.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3652516048bf4c314ce12be37423c79829f46efffb390ad64149a10c6071e8de"}, + {file = "grpcio-1.75.1-cp312-cp312-win32.whl", hash = "sha256:44b62345d8403975513af88da2f3d5cc76f73ca538ba46596f92a127c2aea945"}, + {file = "grpcio-1.75.1-cp312-cp312-win_amd64.whl", hash = "sha256:b1e191c5c465fa777d4cafbaacf0c01e0d5278022082c0abbd2ee1d6454ed94d"}, + {file = "grpcio-1.75.1-cp313-cp313-linux_armv7l.whl", hash = "sha256:3bed22e750d91d53d9e31e0af35a7b0b51367e974e14a4ff229db5b207647884"}, + {file = "grpcio-1.75.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:5b8f381eadcd6ecaa143a21e9e80a26424c76a0a9b3d546febe6648f3a36a5ac"}, + {file = "grpcio-1.75.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5bf4001d3293e3414d0cf99ff9b1139106e57c3a66dfff0c5f60b2a6286ec133"}, + {file = "grpcio-1.75.1-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:9f82ff474103e26351dacfe8d50214e7c9322960d8d07ba7fa1d05ff981c8b2d"}, + {file = "grpcio-1.75.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0ee119f4f88d9f75414217823d21d75bfe0e6ed40135b0cbbfc6376bc9f7757d"}, + {file = "grpcio-1.75.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:664eecc3abe6d916fa6cf8dd6b778e62fb264a70f3430a3180995bf2da935446"}, + {file = "grpcio-1.75.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:c32193fa08b2fbebf08fe08e84f8a0aad32d87c3ad42999c65e9449871b1c66e"}, + {file = "grpcio-1.75.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5cebe13088b9254f6e615bcf1da9131d46cfa4e88039454aca9cb65f639bd3bc"}, + {file = "grpcio-1.75.1-cp313-cp313-win32.whl", hash = "sha256:4b4c678e7ed50f8ae8b8dbad15a865ee73ce12668b6aaf411bf3258b5bc3f970"}, + {file = "grpcio-1.75.1-cp313-cp313-win_amd64.whl", hash = "sha256:5573f51e3f296a1bcf71e7a690c092845fb223072120f4bdb7a5b48e111def66"}, + {file = "grpcio-1.75.1-cp314-cp314-linux_armv7l.whl", hash = "sha256:c05da79068dd96723793bffc8d0e64c45f316248417515f28d22204d9dae51c7"}, + {file = "grpcio-1.75.1-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:06373a94fd16ec287116a825161dca179a0402d0c60674ceeec8c9fba344fe66"}, + {file = "grpcio-1.75.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4484f4b7287bdaa7a5b3980f3c7224c3c622669405d20f69549f5fb956ad0421"}, + {file = "grpcio-1.75.1-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:2720c239c1180eee69f7883c1d4c83fc1a495a2535b5fa322887c70bf02b16e8"}, + {file = "grpcio-1.75.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:07a554fa31c668cf0e7a188678ceeca3cb8fead29bbe455352e712ec33ca701c"}, + {file = "grpcio-1.75.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:3e71a2105210366bfc398eef7f57a664df99194f3520edb88b9c3a7e46ee0d64"}, + {file = "grpcio-1.75.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:8679aa8a5b67976776d3c6b0521e99d1c34db8a312a12bcfd78a7085cb9b604e"}, + {file = "grpcio-1.75.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:aad1c774f4ebf0696a7f148a56d39a3432550612597331792528895258966dc0"}, + {file = "grpcio-1.75.1-cp314-cp314-win32.whl", hash = "sha256:62ce42d9994446b307649cb2a23335fa8e927f7ab2cbf5fcb844d6acb4d85f9c"}, + {file = "grpcio-1.75.1-cp314-cp314-win_amd64.whl", hash = "sha256:f86e92275710bea3000cb79feca1762dc0ad3b27830dd1a74e82ab321d4ee464"}, + {file = "grpcio-1.75.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:c09fba33327c3ac11b5c33dbdd8218eef8990d78f83b1656d628831812a8c0fb"}, + {file = "grpcio-1.75.1-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:7e21400b037be29545704889e72e586c238e346dcb2d08d8a7288d16c883a9ec"}, + {file = "grpcio-1.75.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c12121e509b9f8b0914d10054d24120237d19e870b1cd82acbb8a9b9ddd198a3"}, + {file = "grpcio-1.75.1-cp39-cp39-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:73577a93e692b3474b1bfe84285d098de36705dbd838bb4d6a056d326e4dc880"}, + {file = "grpcio-1.75.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e19e7dfa0d7ca7dea22be464339e18ac608fd75d88c56770c646cdabe54bc724"}, + {file = "grpcio-1.75.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:4e1c28f51c1cf67eccdfc1065e8e866c9ed622f09773ca60947089c117f848a1"}, + {file = "grpcio-1.75.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:030a6164bc2ca726052778c0cf8e3249617a34e368354f9e6107c27ad4af8c28"}, + {file = "grpcio-1.75.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:67697efef5a98d46d5db7b1720fa4043536f8b8e5072a5d61cfca762f287e939"}, + {file = "grpcio-1.75.1-cp39-cp39-win32.whl", hash = "sha256:52015cf73eb5d76f6404e0ce0505a69b51fd1f35810b3a01233b34b10baafb41"}, + {file = "grpcio-1.75.1-cp39-cp39-win_amd64.whl", hash = "sha256:9fe51e4a1f896ea84ac750900eae34d9e9b896b5b1e4a30b02dc31ad29f36383"}, + {file = "grpcio-1.75.1.tar.gz", hash = "sha256:3e81d89ece99b9ace23a6916880baca613c03a799925afb2857887efa8b1b3d2"}, ] +[package.dependencies] +typing-extensions = ">=4.12,<5.0" + [package.extras] -protobuf = ["grpcio-tools (>=1.70.0)"] +protobuf = ["grpcio-tools (>=1.75.1)"] [[package]] name = "grpcio-status" -version = "1.62.3" +version = "1.75.1" description = "Status proto mapping for gRPC" optional = false -python-versions = ">=3.6" +python-versions = ">=3.9" groups = ["main"] files = [ - {file = "grpcio-status-1.62.3.tar.gz", hash = "sha256:289bdd7b2459794a12cf95dc0cb727bd4a1742c37bd823f760236c937e53a485"}, - {file = "grpcio_status-1.62.3-py3-none-any.whl", hash = "sha256:f9049b762ba8de6b1086789d8315846e094edac2c50beaf462338b301a8fd4b8"}, + {file = "grpcio_status-1.75.1-py3-none-any.whl", hash = "sha256:f681b301be26dcf7abf5c765d4a22e4098765e1a65cbdfa3efca384edf8e4e3c"}, + {file = "grpcio_status-1.75.1.tar.gz", hash = "sha256:8162afa21833a2085c91089cc395ad880fac1378a1d60233d976649ed724cbf8"}, ] [package.dependencies] googleapis-common-protos = ">=1.5.5" -grpcio = ">=1.62.3" -protobuf = ">=4.21.6" +grpcio = ">=1.75.1" +protobuf = ">=6.31.1,<7.0.0" [[package]] name = "h11" @@ -870,48 +1041,54 @@ files = [ [package.extras] all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] +[[package]] +name = "iniconfig" +version = "2.1.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760"}, + {file = "iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"}, +] + [[package]] name = "jpype1" -version = "1.5.2" +version = "1.6.0" description = "A Python to Java bridge" optional = false python-versions = ">=3.8" groups = ["main"] files = [ - {file = "jpype1-1.5.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7b2da98c142812ca40a18a735b33e47c6511b03debf1e979630f4cf473b68a87"}, - {file = "jpype1-1.5.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fcfc5c1d45d6b108800d172ea817bda585db7f1646d6a98d14da9aca66e0eb44"}, - {file = "jpype1-1.5.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:156f469a976cc61f695a2455db6de7334774388e7d53c3da8a0a23d9c062d1b2"}, - {file = "jpype1-1.5.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdca93cc74f8db1f604d2ea6adb764dec4dec68528f1ee68308fa3d524095739"}, - {file = "jpype1-1.5.2-cp310-cp310-win_amd64.whl", hash = "sha256:924b0a0cf93d3dddb3f79286fbe40f8c901c78ed61216edbe108666234df43e0"}, - {file = "jpype1-1.5.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c9f6ab8dd284c16e2617a697d54c3d0304b08020a37386ed96103a129391a2d9"}, - {file = "jpype1-1.5.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a54a771ee56260f98e5b9a77455084e4a48061967de13dabf628bdba9c8122e0"}, - {file = "jpype1-1.5.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:05dc6d2759111483a9c808a50e67f556efe494e999585c7d7e7d6d8a8ee58525"}, - {file = "jpype1-1.5.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b900e154826a076118d074166596f1d817e113e07084bf0c9c43d8064a86ab77"}, - {file = "jpype1-1.5.2-cp311-cp311-win_amd64.whl", hash = "sha256:0a0d18d4384b3df2e55282545737dfcf18c604504f1382ad14f880bef960f265"}, - {file = "jpype1-1.5.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:1e1db9ac909ad2ae0e40b04c2aa88cb14250d5245d69715561507681f2b08b2f"}, - {file = "jpype1-1.5.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:994fb7b319b453f77ad4b6aff01e0dd4180ea74a6fe5a031e4e9db92dbe95376"}, - {file = "jpype1-1.5.2-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bec2f1009d7221fb3443decfb8f326039febc93578aadedfb3e052dab0afbf5a"}, - {file = "jpype1-1.5.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b5b1fb2b430a50f081ea0ee24d19232ae0d03dbfe3dd076ec5f8ae42b30a656f"}, - {file = "jpype1-1.5.2-cp312-cp312-win_amd64.whl", hash = "sha256:c7b1c2d76d211cab60be16505d32a6b3c9fffc51ce79c68e81a3d48e5effff2d"}, - {file = "jpype1-1.5.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:4acb098cb1698b14b6e5c79e275f4c70dcc01b0fb93425f206d0a5e380e43c66"}, - {file = "jpype1-1.5.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c08480c7d18125664a12bf0a244b96b49c05105306b65937dbefeb05ab4b2847"}, - {file = "jpype1-1.5.2-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6819f231c651ef876ffb23158083ea498ff80b57c46da537148412aa22235a13"}, - {file = "jpype1-1.5.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42fe8db66ad4e5c66f637f5c4de82fca880ba696104e1f4a7e575885923dead8"}, - {file = "jpype1-1.5.2-cp313-cp313-win_amd64.whl", hash = "sha256:2b96365f1302df2fb3c6ad73117d6fe450a55b7550fd7fecadac3cec5bc7117c"}, - {file = "jpype1-1.5.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ea5001f6a7a42be6f5f500dcb20dad5738fef6a7d19c86dbcf482b803f01cab"}, - {file = "jpype1-1.5.2-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:220d33305998ff7a7cbc9c0b5eea54f2691fdc21e60d52ad56276ea13fd5bd4c"}, - {file = "jpype1-1.5.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f1ed152fdd5200067dc0cdcd4683530b87a6be3987b98596cdf5a7d3ac4c679"}, - {file = "jpype1-1.5.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:538e395365d54bd9777c782f0ba55c3c9a81f1b6a71869945711c042e4cdca8e"}, - {file = "jpype1-1.5.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bafa7841d70f6290de42125fbfedd7028d805cf95c5e7a2984681f5677e30ee1"}, - {file = "jpype1-1.5.2-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e442956197b9ba73ff1a294f2814622fc68956f5caa3ac2fb3d14236669d6f67"}, - {file = "jpype1-1.5.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3e4531677f41bed635efae17a4f8ceaaea7daf8e6a0a0dd5da153925bc54ca0"}, - {file = "jpype1-1.5.2-cp38-cp38-win_amd64.whl", hash = "sha256:9fd339787973cfd7abca1afd495b1ed6373a62c0b5a365caa02ac5ac46a606be"}, - {file = "jpype1-1.5.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8b75d33e93a3bc6543ddf97c24ee0adb5a86a69fb67f0e4f4fa1c8c3970bbf98"}, - {file = "jpype1-1.5.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea21bca4cece752cd3ee88fcd62ce8f444feac8dc7244475fdb9c0e8712e07ea"}, - {file = "jpype1-1.5.2-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:670daf6b13f32653438ebde81aa30f6f48149b95dad4d80a40b3ebea47622164"}, - {file = "jpype1-1.5.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:54744265ef36665d110d139a4b81d10532694c6077b23ef60f3609feadc22d30"}, - {file = "jpype1-1.5.2-cp39-cp39-win_amd64.whl", hash = "sha256:68e1d118200fc46f4ea4bf20900081587ec04de484037c997b0a3b7c5eb71fe3"}, - {file = "jpype1-1.5.2.tar.gz", hash = "sha256:74a42eccf21d30394c1832aec3985a14965fa5320da087b65029d172c0cec43b"}, + {file = "jpype1-1.6.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:533cf7ced58a4b74272f3d1e962fe33f305184d2fcb3856ca79867d5b6f0fb8b"}, + {file = "jpype1-1.6.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:597063698c074e5bf34e696626423a528cdf099989e22a837e889a0d671fd9e4"}, + {file = "jpype1-1.6.0-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:2f0cd698d160ba825952393b4d87911e7eedcbf5af381bb6438126de863f66b6"}, + {file = "jpype1-1.6.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fcabb8cce3be16528bd26e4b73e41d7b8c778111f14de52c33c25e2a9d4c9a9f"}, + {file = "jpype1-1.6.0-cp310-cp310-win_amd64.whl", hash = "sha256:f02c419d6cdd45ed5576d95f0ab4732371c760ba4b01ea9bd646b98b3c21a16f"}, + {file = "jpype1-1.6.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:40b523e11b22398a8ddb777f1e6e8d55108a631311f35b48b0ed0f4c9198d025"}, + {file = "jpype1-1.6.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ae5927e441894d41b65e08390a0ea6a6512fe222c07aa33fbab623512092fdbd"}, + {file = "jpype1-1.6.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:9d85c2077172a32dd8ffde8711cb57c5bc351378ceb44dd8c3bdd80f27fa8caf"}, + {file = "jpype1-1.6.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f5a02cbba4022a0aa47ee617bc12349457988c653491484a988dc8f4e6269dfc"}, + {file = "jpype1-1.6.0-cp311-cp311-win_amd64.whl", hash = "sha256:966daa892e6cd9ec3b2b92fd7a6d572687b29021e6d28fb73995a62ddd0a11eb"}, + {file = "jpype1-1.6.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:5454322887505097c46f41521becf31cda2303f54e35cb42e20f1a180055a558"}, + {file = "jpype1-1.6.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cf646459023a0dd71b3cc3aeeb977225a93f94c97839bb4e6146977b166c7caf"}, + {file = "jpype1-1.6.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:7a98b34bc68a117881382dffa9b02d5e1d0d94325f85d7f564c33cc9e9d2916b"}, + {file = "jpype1-1.6.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b56d7b662ce353c7f876e9ff3d4e775918348340795801e1f00c3b6241006264"}, + {file = "jpype1-1.6.0-cp312-cp312-win_amd64.whl", hash = "sha256:cf41134bc59c28de2721614ed00a34f76f69f2b525583a5c42fee5c7bae05d40"}, + {file = "jpype1-1.6.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ce1da9ff01010a4529de194aecc053bfc8ab25440ea6b63579ef8c8d381da21d"}, + {file = "jpype1-1.6.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4a387430cd4f3f68761013dca7f71b79d8d64b47dfd56eedcbd44c894d98d7f2"}, + {file = "jpype1-1.6.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:6c9d99f41a5d05a8a2b6aeda796f646904c1c2b68aed0cc9a3bbc165483d9e1e"}, + {file = "jpype1-1.6.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:55ba7257988a69bee84f7cd1444131725d5447999149fdafdba25bf46e4b1a3f"}, + {file = "jpype1-1.6.0-cp313-cp313-win_amd64.whl", hash = "sha256:ef8dd72442d91cde7e5eefef24ab71323cdd4137283a59f79d265a6620f7d0ab"}, + {file = "jpype1-1.6.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:dc0c1c8b989b9857f87648980a746084b323858b0450225eab3642bb674645d6"}, + {file = "jpype1-1.6.0-cp313-cp313t-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:cbbba16381ccd23530ad4ef09f7b2b1b12bd7fd0f6ca9fe4c7b4e3da8fe4cf63"}, + {file = "jpype1-1.6.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1a3814e4f65d67e36bdb03b8851d5ece8d7a408aa3a24251ea0609bb8fba77dd"}, + {file = "jpype1-1.6.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f5c1c4fc3458fcde7a82974470f4d1cd0622b139027c67bc714f5762fc4ed6f9"}, + {file = "jpype1-1.6.0-cp39-cp39-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:c828e0272b57c687855442288de696d5377ee458cc19a0c4cb766eb53d908881"}, + {file = "jpype1-1.6.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:560ed113d718909406e3b0e1f4c48595977cb8378a5c9404420cece00788fc86"}, + {file = "jpype1-1.6.0-cp39-cp39-win_amd64.whl", hash = "sha256:9afc02877787571e4cd312b90cbc4057bb986ac02728c3750c36ab59aded9044"}, + {file = "jpype1-1.6.0.tar.gz", hash = "sha256:2d46b2a14f8f0e6f17d8aa22b4fc3a64b2790851ebf1409ad79a37c698fd6e9a"}, ] [package.dependencies] @@ -923,14 +1100,14 @@ tests = ["pytest"] [[package]] name = "minio" -version = "7.2.15" +version = "7.2.18" description = "MinIO Python SDK for Amazon S3 Compatible Cloud Storage" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "minio-7.2.15-py3-none-any.whl", hash = "sha256:c06ef7a43e5d67107067f77b6c07ebdd68733e5aa7eed03076472410ca19d876"}, - {file = "minio-7.2.15.tar.gz", hash = "sha256:5247df5d4dca7bfa4c9b20093acd5ad43e82d8710ceb059d79c6eea970f49f79"}, + {file = "minio-7.2.18-py3-none-any.whl", hash = "sha256:f23a6edbff8d0bc4b5c1a61b2628a01c5a3342aefc613ff9c276012e6321108f"}, + {file = "minio-7.2.18.tar.gz", hash = "sha256:173402a5716099159c5659f9de75be204ebe248557b9f1cc9cf45aa70e9d3024"}, ] [package.dependencies] @@ -958,104 +1135,158 @@ olefile = ">=0.46" [[package]] name = "multidict" -version = "6.1.0" +version = "6.7.0" description = "multidict implementation" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["main"] files = [ - {file = "multidict-6.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3380252550e372e8511d49481bd836264c009adb826b23fefcc5dd3c69692f60"}, - {file = "multidict-6.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:99f826cbf970077383d7de805c0681799491cb939c25450b9b5b3ced03ca99f1"}, - {file = "multidict-6.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a114d03b938376557927ab23f1e950827c3b893ccb94b62fd95d430fd0e5cf53"}, - {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1c416351ee6271b2f49b56ad7f308072f6f44b37118d69c2cad94f3fa8a40d5"}, - {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6b5d83030255983181005e6cfbac1617ce9746b219bc2aad52201ad121226581"}, - {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3e97b5e938051226dc025ec80980c285b053ffb1e25a3db2a3aa3bc046bf7f56"}, - {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d618649d4e70ac6efcbba75be98b26ef5078faad23592f9b51ca492953012429"}, - {file = "multidict-6.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10524ebd769727ac77ef2278390fb0068d83f3acb7773792a5080f2b0abf7748"}, - {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ff3827aef427c89a25cc96ded1759271a93603aba9fb977a6d264648ebf989db"}, - {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:06809f4f0f7ab7ea2cabf9caca7d79c22c0758b58a71f9d32943ae13c7ace056"}, - {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f179dee3b863ab1c59580ff60f9d99f632f34ccb38bf67a33ec6b3ecadd0fd76"}, - {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:aaed8b0562be4a0876ee3b6946f6869b7bcdb571a5d1496683505944e268b160"}, - {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3c8b88a2ccf5493b6c8da9076fb151ba106960a2df90c2633f342f120751a9e7"}, - {file = "multidict-6.1.0-cp310-cp310-win32.whl", hash = "sha256:4a9cb68166a34117d6646c0023c7b759bf197bee5ad4272f420a0141d7eb03a0"}, - {file = "multidict-6.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:20b9b5fbe0b88d0bdef2012ef7dee867f874b72528cf1d08f1d59b0e3850129d"}, - {file = "multidict-6.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3efe2c2cb5763f2f1b275ad2bf7a287d3f7ebbef35648a9726e3b69284a4f3d6"}, - {file = "multidict-6.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c7053d3b0353a8b9de430a4f4b4268ac9a4fb3481af37dfe49825bf45ca24156"}, - {file = "multidict-6.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:27e5fc84ccef8dfaabb09d82b7d179c7cf1a3fbc8a966f8274fcb4ab2eb4cadb"}, - {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e2b90b43e696f25c62656389d32236e049568b39320e2735d51f08fd362761b"}, - {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d83a047959d38a7ff552ff94be767b7fd79b831ad1cd9920662db05fec24fe72"}, - {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d1a9dd711d0877a1ece3d2e4fea11a8e75741ca21954c919406b44e7cf971304"}, - {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec2abea24d98246b94913b76a125e855eb5c434f7c46546046372fe60f666351"}, - {file = "multidict-6.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4867cafcbc6585e4b678876c489b9273b13e9fff9f6d6d66add5e15d11d926cb"}, - {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5b48204e8d955c47c55b72779802b219a39acc3ee3d0116d5080c388970b76e3"}, - {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:d8fff389528cad1618fb4b26b95550327495462cd745d879a8c7c2115248e399"}, - {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a7a9541cd308eed5e30318430a9c74d2132e9a8cb46b901326272d780bf2d423"}, - {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:da1758c76f50c39a2efd5e9859ce7d776317eb1dd34317c8152ac9251fc574a3"}, - {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c943a53e9186688b45b323602298ab727d8865d8c9ee0b17f8d62d14b56f0753"}, - {file = "multidict-6.1.0-cp311-cp311-win32.whl", hash = "sha256:90f8717cb649eea3504091e640a1b8568faad18bd4b9fcd692853a04475a4b80"}, - {file = "multidict-6.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:82176036e65644a6cc5bd619f65f6f19781e8ec2e5330f51aa9ada7504cc1926"}, - {file = "multidict-6.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b04772ed465fa3cc947db808fa306d79b43e896beb677a56fb2347ca1a49c1fa"}, - {file = "multidict-6.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6180c0ae073bddeb5a97a38c03f30c233e0a4d39cd86166251617d1bbd0af436"}, - {file = "multidict-6.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:071120490b47aa997cca00666923a83f02c7fbb44f71cf7f136df753f7fa8761"}, - {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50b3a2710631848991d0bf7de077502e8994c804bb805aeb2925a981de58ec2e"}, - {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b58c621844d55e71c1b7f7c498ce5aa6985d743a1a59034c57a905b3f153c1ef"}, - {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55b6d90641869892caa9ca42ff913f7ff1c5ece06474fbd32fb2cf6834726c95"}, - {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b820514bfc0b98a30e3d85462084779900347e4d49267f747ff54060cc33925"}, - {file = "multidict-6.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10a9b09aba0c5b48c53761b7c720aaaf7cf236d5fe394cd399c7ba662d5f9966"}, - {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1e16bf3e5fc9f44632affb159d30a437bfe286ce9e02754759be5536b169b305"}, - {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:76f364861c3bfc98cbbcbd402d83454ed9e01a5224bb3a28bf70002a230f73e2"}, - {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:820c661588bd01a0aa62a1283f20d2be4281b086f80dad9e955e690c75fb54a2"}, - {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:0e5f362e895bc5b9e67fe6e4ded2492d8124bdf817827f33c5b46c2fe3ffaca6"}, - {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3ec660d19bbc671e3a6443325f07263be452c453ac9e512f5eb935e7d4ac28b3"}, - {file = "multidict-6.1.0-cp312-cp312-win32.whl", hash = "sha256:58130ecf8f7b8112cdb841486404f1282b9c86ccb30d3519faf301b2e5659133"}, - {file = "multidict-6.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:188215fc0aafb8e03341995e7c4797860181562380f81ed0a87ff455b70bf1f1"}, - {file = "multidict-6.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d569388c381b24671589335a3be6e1d45546c2988c2ebe30fdcada8457a31008"}, - {file = "multidict-6.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:052e10d2d37810b99cc170b785945421141bf7bb7d2f8799d431e7db229c385f"}, - {file = "multidict-6.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f90c822a402cb865e396a504f9fc8173ef34212a342d92e362ca498cad308e28"}, - {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b225d95519a5bf73860323e633a664b0d85ad3d5bede6d30d95b35d4dfe8805b"}, - {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:23bfd518810af7de1116313ebd9092cb9aa629beb12f6ed631ad53356ed6b86c"}, - {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c09fcfdccdd0b57867577b719c69e347a436b86cd83747f179dbf0cc0d4c1f3"}, - {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf6bea52ec97e95560af5ae576bdac3aa3aae0b6758c6efa115236d9e07dae44"}, - {file = "multidict-6.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57feec87371dbb3520da6192213c7d6fc892d5589a93db548331954de8248fd2"}, - {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0c3f390dc53279cbc8ba976e5f8035eab997829066756d811616b652b00a23a3"}, - {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:59bfeae4b25ec05b34f1956eaa1cb38032282cd4dfabc5056d0a1ec4d696d3aa"}, - {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:b2f59caeaf7632cc633b5cf6fc449372b83bbdf0da4ae04d5be36118e46cc0aa"}, - {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:37bb93b2178e02b7b618893990941900fd25b6b9ac0fa49931a40aecdf083fe4"}, - {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4e9f48f58c2c523d5a06faea47866cd35b32655c46b443f163d08c6d0ddb17d6"}, - {file = "multidict-6.1.0-cp313-cp313-win32.whl", hash = "sha256:3a37ffb35399029b45c6cc33640a92bef403c9fd388acce75cdc88f58bd19a81"}, - {file = "multidict-6.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:e9aa71e15d9d9beaad2c6b9319edcdc0a49a43ef5c0a4c8265ca9ee7d6c67774"}, - {file = "multidict-6.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:db7457bac39421addd0c8449933ac32d8042aae84a14911a757ae6ca3eef1392"}, - {file = "multidict-6.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d094ddec350a2fb899fec68d8353c78233debde9b7d8b4beeafa70825f1c281a"}, - {file = "multidict-6.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5845c1fd4866bb5dd3125d89b90e57ed3138241540897de748cdf19de8a2fca2"}, - {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9079dfc6a70abe341f521f78405b8949f96db48da98aeb43f9907f342f627cdc"}, - {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3914f5aaa0f36d5d60e8ece6a308ee1c9784cd75ec8151062614657a114c4478"}, - {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c08be4f460903e5a9d0f76818db3250f12e9c344e79314d1d570fc69d7f4eae4"}, - {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d093be959277cb7dee84b801eb1af388b6ad3ca6a6b6bf1ed7585895789d027d"}, - {file = "multidict-6.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3702ea6872c5a2a4eeefa6ffd36b042e9773f05b1f37ae3ef7264b1163c2dcf6"}, - {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:2090f6a85cafc5b2db085124d752757c9d251548cedabe9bd31afe6363e0aff2"}, - {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:f67f217af4b1ff66c68a87318012de788dd95fcfeb24cc889011f4e1c7454dfd"}, - {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:189f652a87e876098bbc67b4da1049afb5f5dfbaa310dd67c594b01c10388db6"}, - {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:6bb5992037f7a9eff7991ebe4273ea7f51f1c1c511e6a2ce511d0e7bdb754492"}, - {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:ac10f4c2b9e770c4e393876e35a7046879d195cd123b4f116d299d442b335bcd"}, - {file = "multidict-6.1.0-cp38-cp38-win32.whl", hash = "sha256:e27bbb6d14416713a8bd7aaa1313c0fc8d44ee48d74497a0ff4c3a1b6ccb5167"}, - {file = "multidict-6.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:22f3105d4fb15c8f57ff3959a58fcab6ce36814486500cd7485651230ad4d4ef"}, - {file = "multidict-6.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:4e18b656c5e844539d506a0a06432274d7bd52a7487e6828c63a63d69185626c"}, - {file = "multidict-6.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a185f876e69897a6f3325c3f19f26a297fa058c5e456bfcff8015e9a27e83ae1"}, - {file = "multidict-6.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ab7c4ceb38d91570a650dba194e1ca87c2b543488fe9309b4212694174fd539c"}, - {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e617fb6b0b6953fffd762669610c1c4ffd05632c138d61ac7e14ad187870669c"}, - {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:16e5f4bf4e603eb1fdd5d8180f1a25f30056f22e55ce51fb3d6ad4ab29f7d96f"}, - {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f4c035da3f544b1882bac24115f3e2e8760f10a0107614fc9839fd232200b875"}, - {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:957cf8e4b6e123a9eea554fa7ebc85674674b713551de587eb318a2df3e00255"}, - {file = "multidict-6.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:483a6aea59cb89904e1ceabd2b47368b5600fb7de78a6e4a2c2987b2d256cf30"}, - {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:87701f25a2352e5bf7454caa64757642734da9f6b11384c1f9d1a8e699758057"}, - {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:682b987361e5fd7a139ed565e30d81fd81e9629acc7d925a205366877d8c8657"}, - {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ce2186a7df133a9c895dea3331ddc5ddad42cdd0d1ea2f0a51e5d161e4762f28"}, - {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:9f636b730f7e8cb19feb87094949ba54ee5357440b9658b2a32a5ce4bce53972"}, - {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:73eae06aa53af2ea5270cc066dcaf02cc60d2994bbb2c4ef5764949257d10f43"}, - {file = "multidict-6.1.0-cp39-cp39-win32.whl", hash = "sha256:1ca0083e80e791cffc6efce7660ad24af66c8d4079d2a750b29001b53ff59ada"}, - {file = "multidict-6.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:aa466da5b15ccea564bdab9c89175c762bc12825f4659c11227f515cee76fa4a"}, - {file = "multidict-6.1.0-py3-none-any.whl", hash = "sha256:48e171e52d1c4d33888e529b999e5900356b9ae588c2f09a52dcefb158b27506"}, - {file = "multidict-6.1.0.tar.gz", hash = "sha256:22ae2ebf9b0c69d206c003e2f6a914ea33f0a932d4aa16f236afc049d9958f4a"}, + {file = "multidict-6.7.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:9f474ad5acda359c8758c8accc22032c6abe6dc87a8be2440d097785e27a9349"}, + {file = "multidict-6.7.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4b7a9db5a870f780220e931d0002bbfd88fb53aceb6293251e2c839415c1b20e"}, + {file = "multidict-6.7.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:03ca744319864e92721195fa28c7a3b2bc7b686246b35e4078c1e4d0eb5466d3"}, + {file = "multidict-6.7.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f0e77e3c0008bc9316e662624535b88d360c3a5d3f81e15cf12c139a75250046"}, + {file = "multidict-6.7.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:08325c9e5367aa379a3496aa9a022fe8837ff22e00b94db256d3a1378c76ab32"}, + {file = "multidict-6.7.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e2862408c99f84aa571ab462d25236ef9cb12a602ea959ba9c9009a54902fc73"}, + {file = "multidict-6.7.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4d72a9a2d885f5c208b0cb91ff2ed43636bb7e345ec839ff64708e04f69a13cc"}, + {file = "multidict-6.7.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:478cc36476687bac1514d651cbbaa94b86b0732fb6855c60c673794c7dd2da62"}, + {file = "multidict-6.7.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6843b28b0364dc605f21481c90fadb5f60d9123b442eb8a726bb74feef588a84"}, + {file = "multidict-6.7.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:23bfeee5316266e5ee2d625df2d2c602b829435fc3a235c2ba2131495706e4a0"}, + {file = "multidict-6.7.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:680878b9f3d45c31e1f730eef731f9b0bc1da456155688c6745ee84eb818e90e"}, + {file = "multidict-6.7.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:eb866162ef2f45063acc7a53a88ef6fe8bf121d45c30ea3c9cd87ce7e191a8d4"}, + {file = "multidict-6.7.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:df0e3bf7993bdbeca5ac25aa859cf40d39019e015c9c91809ba7093967f7a648"}, + {file = "multidict-6.7.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:661709cdcd919a2ece2234f9bae7174e5220c80b034585d7d8a755632d3e2111"}, + {file = "multidict-6.7.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:096f52730c3fb8ed419db2d44391932b63891b2c5ed14850a7e215c0ba9ade36"}, + {file = "multidict-6.7.0-cp310-cp310-win32.whl", hash = "sha256:afa8a2978ec65d2336305550535c9c4ff50ee527914328c8677b3973ade52b85"}, + {file = "multidict-6.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:b15b3afff74f707b9275d5ba6a91ae8f6429c3ffb29bbfd216b0b375a56f13d7"}, + {file = "multidict-6.7.0-cp310-cp310-win_arm64.whl", hash = "sha256:4b73189894398d59131a66ff157837b1fafea9974be486d036bb3d32331fdbf0"}, + {file = "multidict-6.7.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4d409aa42a94c0b3fa617708ef5276dfe81012ba6753a0370fcc9d0195d0a1fc"}, + {file = "multidict-6.7.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:14c9e076eede3b54c636f8ce1c9c252b5f057c62131211f0ceeec273810c9721"}, + {file = "multidict-6.7.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4c09703000a9d0fa3c3404b27041e574cc7f4df4c6563873246d0e11812a94b6"}, + {file = "multidict-6.7.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:a265acbb7bb33a3a2d626afbe756371dce0279e7b17f4f4eda406459c2b5ff1c"}, + {file = "multidict-6.7.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:51cb455de290ae462593e5b1cb1118c5c22ea7f0d3620d9940bf695cea5a4bd7"}, + {file = "multidict-6.7.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:db99677b4457c7a5c5a949353e125ba72d62b35f74e26da141530fbb012218a7"}, + {file = "multidict-6.7.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f470f68adc395e0183b92a2f4689264d1ea4b40504a24d9882c27375e6662bb9"}, + {file = "multidict-6.7.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0db4956f82723cc1c270de9c6e799b4c341d327762ec78ef82bb962f79cc07d8"}, + {file = "multidict-6.7.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3e56d780c238f9e1ae66a22d2adf8d16f485381878250db8d496623cd38b22bd"}, + {file = "multidict-6.7.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9d14baca2ee12c1a64740d4531356ba50b82543017f3ad6de0deb943c5979abb"}, + {file = "multidict-6.7.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:295a92a76188917c7f99cda95858c822f9e4aae5824246bba9b6b44004ddd0a6"}, + {file = "multidict-6.7.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:39f1719f57adbb767ef592a50ae5ebb794220d1188f9ca93de471336401c34d2"}, + {file = "multidict-6.7.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:0a13fb8e748dfc94749f622de065dd5c1def7e0d2216dba72b1d8069a389c6ff"}, + {file = "multidict-6.7.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e3aa16de190d29a0ea1b48253c57d99a68492c8dd8948638073ab9e74dc9410b"}, + {file = "multidict-6.7.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a048ce45dcdaaf1defb76b2e684f997fb5abf74437b6cb7b22ddad934a964e34"}, + {file = "multidict-6.7.0-cp311-cp311-win32.whl", hash = "sha256:a90af66facec4cebe4181b9e62a68be65e45ac9b52b67de9eec118701856e7ff"}, + {file = "multidict-6.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:95b5ffa4349df2887518bb839409bcf22caa72d82beec453216802f475b23c81"}, + {file = "multidict-6.7.0-cp311-cp311-win_arm64.whl", hash = "sha256:329aa225b085b6f004a4955271a7ba9f1087e39dcb7e65f6284a988264a63912"}, + {file = "multidict-6.7.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8a3862568a36d26e650a19bb5cbbba14b71789032aebc0423f8cc5f150730184"}, + {file = "multidict-6.7.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:960c60b5849b9b4f9dcc9bea6e3626143c252c74113df2c1540aebce70209b45"}, + {file = "multidict-6.7.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2049be98fb57a31b4ccf870bf377af2504d4ae35646a19037ec271e4c07998aa"}, + {file = "multidict-6.7.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0934f3843a1860dd465d38895c17fce1f1cb37295149ab05cd1b9a03afacb2a7"}, + {file = "multidict-6.7.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b3e34f3a1b8131ba06f1a73adab24f30934d148afcd5f5de9a73565a4404384e"}, + {file = "multidict-6.7.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:efbb54e98446892590dc2458c19c10344ee9a883a79b5cec4bc34d6656e8d546"}, + {file = "multidict-6.7.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a35c5fc61d4f51eb045061e7967cfe3123d622cd500e8868e7c0c592a09fedc4"}, + {file = "multidict-6.7.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29fe6740ebccba4175af1b9b87bf553e9c15cd5868ee967e010efcf94e4fd0f1"}, + {file = "multidict-6.7.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:123e2a72e20537add2f33a79e605f6191fba2afda4cbb876e35c1a7074298a7d"}, + {file = "multidict-6.7.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b284e319754366c1aee2267a2036248b24eeb17ecd5dc16022095e747f2f4304"}, + {file = "multidict-6.7.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:803d685de7be4303b5a657b76e2f6d1240e7e0a8aa2968ad5811fa2285553a12"}, + {file = "multidict-6.7.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c04a328260dfd5db8c39538f999f02779012268f54614902d0afc775d44e0a62"}, + {file = "multidict-6.7.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8a19cdb57cd3df4cd865849d93ee14920fb97224300c88501f16ecfa2604b4e0"}, + {file = "multidict-6.7.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9b2fd74c52accced7e75de26023b7dccee62511a600e62311b918ec5c168fc2a"}, + {file = "multidict-6.7.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3e8bfdd0e487acf992407a140d2589fe598238eaeffa3da8448d63a63cd363f8"}, + {file = "multidict-6.7.0-cp312-cp312-win32.whl", hash = "sha256:dd32a49400a2c3d52088e120ee00c1e3576cbff7e10b98467962c74fdb762ed4"}, + {file = "multidict-6.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:92abb658ef2d7ef22ac9f8bb88e8b6c3e571671534e029359b6d9e845923eb1b"}, + {file = "multidict-6.7.0-cp312-cp312-win_arm64.whl", hash = "sha256:490dab541a6a642ce1a9d61a4781656b346a55c13038f0b1244653828e3a83ec"}, + {file = "multidict-6.7.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:bee7c0588aa0076ce77c0ea5d19a68d76ad81fcd9fe8501003b9a24f9d4000f6"}, + {file = "multidict-6.7.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7ef6b61cad77091056ce0e7ce69814ef72afacb150b7ac6a3e9470def2198159"}, + {file = "multidict-6.7.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9c0359b1ec12b1d6849c59f9d319610b7f20ef990a6d454ab151aa0e3b9f78ca"}, + {file = "multidict-6.7.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cd240939f71c64bd658f186330603aac1a9a81bf6273f523fca63673cb7378a8"}, + {file = "multidict-6.7.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a60a4d75718a5efa473ebd5ab685786ba0c67b8381f781d1be14da49f1a2dc60"}, + {file = "multidict-6.7.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:53a42d364f323275126aff81fb67c5ca1b7a04fda0546245730a55c8c5f24bc4"}, + {file = "multidict-6.7.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3b29b980d0ddbecb736735ee5bef69bb2ddca56eff603c86f3f29a1128299b4f"}, + {file = "multidict-6.7.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f8a93b1c0ed2d04b97a5e9336fd2d33371b9a6e29ab7dd6503d63407c20ffbaf"}, + {file = "multidict-6.7.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9ff96e8815eecacc6645da76c413eb3b3d34cfca256c70b16b286a687d013c32"}, + {file = "multidict-6.7.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7516c579652f6a6be0e266aec0acd0db80829ca305c3d771ed898538804c2036"}, + {file = "multidict-6.7.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:040f393368e63fb0f3330e70c26bfd336656bed925e5cbe17c9da839a6ab13ec"}, + {file = "multidict-6.7.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b3bc26a951007b1057a1c543af845f1c7e3e71cc240ed1ace7bf4484aa99196e"}, + {file = "multidict-6.7.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:7b022717c748dd1992a83e219587aabe45980d88969f01b316e78683e6285f64"}, + {file = "multidict-6.7.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:9600082733859f00d79dee64effc7aef1beb26adb297416a4ad2116fd61374bd"}, + {file = "multidict-6.7.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:94218fcec4d72bc61df51c198d098ce2b378e0ccbac41ddbed5ef44092913288"}, + {file = "multidict-6.7.0-cp313-cp313-win32.whl", hash = "sha256:a37bd74c3fa9d00be2d7b8eca074dc56bd8077ddd2917a839bd989612671ed17"}, + {file = "multidict-6.7.0-cp313-cp313-win_amd64.whl", hash = "sha256:30d193c6cc6d559db42b6bcec8a5d395d34d60c9877a0b71ecd7c204fcf15390"}, + {file = "multidict-6.7.0-cp313-cp313-win_arm64.whl", hash = "sha256:ea3334cabe4d41b7ccd01e4d349828678794edbc2d3ae97fc162a3312095092e"}, + {file = "multidict-6.7.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:ad9ce259f50abd98a1ca0aa6e490b58c316a0fce0617f609723e40804add2c00"}, + {file = "multidict-6.7.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:07f5594ac6d084cbb5de2df218d78baf55ef150b91f0ff8a21cc7a2e3a5a58eb"}, + {file = "multidict-6.7.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:0591b48acf279821a579282444814a2d8d0af624ae0bc600aa4d1b920b6e924b"}, + {file = "multidict-6.7.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:749a72584761531d2b9467cfbdfd29487ee21124c304c4b6cb760d8777b27f9c"}, + {file = "multidict-6.7.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b4c3d199f953acd5b446bf7c0de1fe25d94e09e79086f8dc2f48a11a129cdf1"}, + {file = "multidict-6.7.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:9fb0211dfc3b51efea2f349ec92c114d7754dd62c01f81c3e32b765b70c45c9b"}, + {file = "multidict-6.7.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a027ec240fe73a8d6281872690b988eed307cd7d91b23998ff35ff577ca688b5"}, + {file = "multidict-6.7.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1d964afecdf3a8288789df2f5751dc0a8261138c3768d9af117ed384e538fad"}, + {file = "multidict-6.7.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:caf53b15b1b7df9fbd0709aa01409000a2b4dd03a5f6f5cc548183c7c8f8b63c"}, + {file = "multidict-6.7.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:654030da3197d927f05a536a66186070e98765aa5142794c9904555d3a9d8fb5"}, + {file = "multidict-6.7.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:2090d3718829d1e484706a2f525e50c892237b2bf9b17a79b059cb98cddc2f10"}, + {file = "multidict-6.7.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:2d2cfeec3f6f45651b3d408c4acec0ebf3daa9bc8a112a084206f5db5d05b754"}, + {file = "multidict-6.7.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:4ef089f985b8c194d341eb2c24ae6e7408c9a0e2e5658699c92f497437d88c3c"}, + {file = "multidict-6.7.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e93a0617cd16998784bf4414c7e40f17a35d2350e5c6f0bd900d3a8e02bd3762"}, + {file = "multidict-6.7.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f0feece2ef8ebc42ed9e2e8c78fc4aa3cf455733b507c09ef7406364c94376c6"}, + {file = "multidict-6.7.0-cp313-cp313t-win32.whl", hash = "sha256:19a1d55338ec1be74ef62440ca9e04a2f001a04d0cc49a4983dc320ff0f3212d"}, + {file = "multidict-6.7.0-cp313-cp313t-win_amd64.whl", hash = "sha256:3da4fb467498df97e986af166b12d01f05d2e04f978a9c1c680ea1988e0bc4b6"}, + {file = "multidict-6.7.0-cp313-cp313t-win_arm64.whl", hash = "sha256:b4121773c49a0776461f4a904cdf6264c88e42218aaa8407e803ca8025872792"}, + {file = "multidict-6.7.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3bab1e4aff7adaa34410f93b1f8e57c4b36b9af0426a76003f441ee1d3c7e842"}, + {file = "multidict-6.7.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:b8512bac933afc3e45fb2b18da8e59b78d4f408399a960339598374d4ae3b56b"}, + {file = "multidict-6.7.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:79dcf9e477bc65414ebfea98ffd013cb39552b5ecd62908752e0e413d6d06e38"}, + {file = "multidict-6.7.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:31bae522710064b5cbeddaf2e9f32b1abab70ac6ac91d42572502299e9953128"}, + {file = "multidict-6.7.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a0df7ff02397bb63e2fd22af2c87dfa39e8c7f12947bc524dbdc528282c7e34"}, + {file = "multidict-6.7.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7a0222514e8e4c514660e182d5156a415c13ef0aabbd71682fc714e327b95e99"}, + {file = "multidict-6.7.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2397ab4daaf2698eb51a76721e98db21ce4f52339e535725de03ea962b5a3202"}, + {file = "multidict-6.7.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8891681594162635948a636c9fe0ff21746aeb3dd5463f6e25d9bea3a8a39ca1"}, + {file = "multidict-6.7.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:18706cc31dbf402a7945916dd5cddf160251b6dab8a2c5f3d6d5a55949f676b3"}, + {file = "multidict-6.7.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f844a1bbf1d207dd311a56f383f7eda2d0e134921d45751842d8235e7778965d"}, + {file = "multidict-6.7.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:d4393e3581e84e5645506923816b9cc81f5609a778c7e7534054091acc64d1c6"}, + {file = "multidict-6.7.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:fbd18dc82d7bf274b37aa48d664534330af744e03bccf696d6f4c6042e7d19e7"}, + {file = "multidict-6.7.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:b6234e14f9314731ec45c42fc4554b88133ad53a09092cc48a88e771c125dadb"}, + {file = "multidict-6.7.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:08d4379f9744d8f78d98c8673c06e202ffa88296f009c71bbafe8a6bf847d01f"}, + {file = "multidict-6.7.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:9fe04da3f79387f450fd0061d4dd2e45a72749d31bf634aecc9e27f24fdc4b3f"}, + {file = "multidict-6.7.0-cp314-cp314-win32.whl", hash = "sha256:fbafe31d191dfa7c4c51f7a6149c9fb7e914dcf9ffead27dcfd9f1ae382b3885"}, + {file = "multidict-6.7.0-cp314-cp314-win_amd64.whl", hash = "sha256:2f67396ec0310764b9222a1728ced1ab638f61aadc6226f17a71dd9324f9a99c"}, + {file = "multidict-6.7.0-cp314-cp314-win_arm64.whl", hash = "sha256:ba672b26069957ee369cfa7fc180dde1fc6f176eaf1e6beaf61fbebbd3d9c000"}, + {file = "multidict-6.7.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:c1dcc7524066fa918c6a27d61444d4ee7900ec635779058571f70d042d86ed63"}, + {file = "multidict-6.7.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:27e0b36c2d388dc7b6ced3406671b401e84ad7eb0656b8f3a2f46ed0ce483718"}, + {file = "multidict-6.7.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2a7baa46a22e77f0988e3b23d4ede5513ebec1929e34ee9495be535662c0dfe2"}, + {file = "multidict-6.7.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7bf77f54997a9166a2f5675d1201520586439424c2511723a7312bdb4bcc034e"}, + {file = "multidict-6.7.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e011555abada53f1578d63389610ac8a5400fc70ce71156b0aa30d326f1a5064"}, + {file = "multidict-6.7.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:28b37063541b897fd6a318007373930a75ca6d6ac7c940dbe14731ffdd8d498e"}, + {file = "multidict-6.7.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:05047ada7a2fde2631a0ed706f1fd68b169a681dfe5e4cf0f8e4cb6618bbc2cd"}, + {file = "multidict-6.7.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:716133f7d1d946a4e1b91b1756b23c088881e70ff180c24e864c26192ad7534a"}, + {file = "multidict-6.7.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d1bed1b467ef657f2a0ae62844a607909ef1c6889562de5e1d505f74457d0b96"}, + {file = "multidict-6.7.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:ca43bdfa5d37bd6aee89d85e1d0831fb86e25541be7e9d376ead1b28974f8e5e"}, + {file = "multidict-6.7.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:44b546bd3eb645fd26fb949e43c02a25a2e632e2ca21a35e2e132c8105dc8599"}, + {file = "multidict-6.7.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:a6ef16328011d3f468e7ebc326f24c1445f001ca1dec335b2f8e66bed3006394"}, + {file = "multidict-6.7.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:5aa873cbc8e593d361ae65c68f85faadd755c3295ea2c12040ee146802f23b38"}, + {file = "multidict-6.7.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:3d7b6ccce016e29df4b7ca819659f516f0bc7a4b3efa3bb2012ba06431b044f9"}, + {file = "multidict-6.7.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:171b73bd4ee683d307599b66793ac80981b06f069b62eea1c9e29c9241aa66b0"}, + {file = "multidict-6.7.0-cp314-cp314t-win32.whl", hash = "sha256:b2d7f80c4e1fd010b07cb26820aae86b7e73b681ee4889684fb8d2d4537aab13"}, + {file = "multidict-6.7.0-cp314-cp314t-win_amd64.whl", hash = "sha256:09929cab6fcb68122776d575e03c6cc64ee0b8fca48d17e135474b042ce515cd"}, + {file = "multidict-6.7.0-cp314-cp314t-win_arm64.whl", hash = "sha256:cc41db090ed742f32bd2d2c721861725e6109681eddf835d0a82bd3a5c382827"}, + {file = "multidict-6.7.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:363eb68a0a59bd2303216d2346e6c441ba10d36d1f9969fcb6f1ba700de7bb5c"}, + {file = "multidict-6.7.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d874eb056410ca05fed180b6642e680373688efafc7f077b2a2f61811e873a40"}, + {file = "multidict-6.7.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8b55d5497b51afdfde55925e04a022f1de14d4f4f25cdfd4f5d9b0aa96166851"}, + {file = "multidict-6.7.0-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f8e5c0031b90ca9ce555e2e8fd5c3b02a25f14989cbc310701823832c99eb687"}, + {file = "multidict-6.7.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9cf41880c991716f3c7cec48e2f19ae4045fc9db5fc9cff27347ada24d710bb5"}, + {file = "multidict-6.7.0-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8cfc12a8630a29d601f48d47787bd7eb730e475e83edb5d6c5084317463373eb"}, + {file = "multidict-6.7.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3996b50c3237c4aec17459217c1e7bbdead9a22a0fcd3c365564fbd16439dde6"}, + {file = "multidict-6.7.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7f5170993a0dd3ab871c74f45c0a21a4e2c37a2f2b01b5f722a2ad9c6650469e"}, + {file = "multidict-6.7.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ec81878ddf0e98817def1e77d4f50dae5ef5b0e4fe796fae3bd674304172416e"}, + {file = "multidict-6.7.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:9281bf5b34f59afbc6b1e477a372e9526b66ca446f4bf62592839c195a718b32"}, + {file = "multidict-6.7.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:68af405971779d8b37198726f2b6fe3955db846fee42db7a4286fc542203934c"}, + {file = "multidict-6.7.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3ba3ef510467abb0667421a286dc906e30eb08569365f5cdb131d7aff7c2dd84"}, + {file = "multidict-6.7.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:b61189b29081a20c7e4e0b49b44d5d44bb0dc92be3c6d06a11cc043f81bf9329"}, + {file = "multidict-6.7.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:fb287618b9c7aa3bf8d825f02d9201b2f13078a5ed3b293c8f4d953917d84d5e"}, + {file = "multidict-6.7.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:521f33e377ff64b96c4c556b81c55d0cfffb96a11c194fd0c3f1e56f3d8dd5a4"}, + {file = "multidict-6.7.0-cp39-cp39-win32.whl", hash = "sha256:ce8fdc2dca699f8dbf055a61d73eaa10482569ad20ee3c36ef9641f69afa8c91"}, + {file = "multidict-6.7.0-cp39-cp39-win_amd64.whl", hash = "sha256:7e73299c99939f089dd9b2120a04a516b95cdf8c1cd2b18c53ebf0de80b1f18f"}, + {file = "multidict-6.7.0-cp39-cp39-win_arm64.whl", hash = "sha256:6bdce131e14b04fd34a809b6380dbfd826065c3e2fe8a50dbae659fa0c390546"}, + {file = "multidict-6.7.0-py3-none-any.whl", hash = "sha256:394fc5c42a333c9ffc3e421a4c85e08580d990e08b99f6bf35b4132114c5dcb3"}, + {file = "multidict-6.7.0.tar.gz", hash = "sha256:c6e99d9a65ca282e578dfea819cfa9c0a62b2499d8677392e09feaf305e9e6f5"}, ] [[package]] @@ -1075,122 +1306,162 @@ tests = ["pytest", "pytest-cov"] [[package]] name = "packaging" -version = "24.2" +version = "25.0" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" -groups = ["main"] +groups = ["main", "dev"] files = [ - {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, - {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, + {file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"}, + {file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"}, ] +[[package]] +name = "pluggy" +version = "1.6.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746"}, + {file = "pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["coverage", "pytest", "pytest-benchmark"] + [[package]] name = "propcache" -version = "0.3.0" +version = "0.4.1" description = "Accelerated property cache" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "propcache-0.3.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:efa44f64c37cc30c9f05932c740a8b40ce359f51882c70883cc95feac842da4d"}, - {file = "propcache-0.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2383a17385d9800b6eb5855c2f05ee550f803878f344f58b6e194de08b96352c"}, - {file = "propcache-0.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d3e7420211f5a65a54675fd860ea04173cde60a7cc20ccfbafcccd155225f8bc"}, - {file = "propcache-0.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3302c5287e504d23bb0e64d2a921d1eb4a03fb93a0a0aa3b53de059f5a5d737d"}, - {file = "propcache-0.3.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7e2e068a83552ddf7a39a99488bcba05ac13454fb205c847674da0352602082f"}, - {file = "propcache-0.3.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d913d36bdaf368637b4f88d554fb9cb9d53d6920b9c5563846555938d5450bf"}, - {file = "propcache-0.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ee1983728964d6070ab443399c476de93d5d741f71e8f6e7880a065f878e0b9"}, - {file = "propcache-0.3.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:36ca5e9a21822cc1746023e88f5c0af6fce3af3b85d4520efb1ce4221bed75cc"}, - {file = "propcache-0.3.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:9ecde3671e62eeb99e977f5221abcf40c208f69b5eb986b061ccec317c82ebd0"}, - {file = "propcache-0.3.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:d383bf5e045d7f9d239b38e6acadd7b7fdf6c0087259a84ae3475d18e9a2ae8b"}, - {file = "propcache-0.3.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:8cb625bcb5add899cb8ba7bf716ec1d3e8f7cdea9b0713fa99eadf73b6d4986f"}, - {file = "propcache-0.3.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:5fa159dcee5dba00c1def3231c249cf261185189205073bde13797e57dd7540a"}, - {file = "propcache-0.3.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:a7080b0159ce05f179cfac592cda1a82898ca9cd097dacf8ea20ae33474fbb25"}, - {file = "propcache-0.3.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ed7161bccab7696a473fe7ddb619c1d75963732b37da4618ba12e60899fefe4f"}, - {file = "propcache-0.3.0-cp310-cp310-win32.whl", hash = "sha256:bf0d9a171908f32d54f651648c7290397b8792f4303821c42a74e7805bfb813c"}, - {file = "propcache-0.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:42924dc0c9d73e49908e35bbdec87adedd651ea24c53c29cac103ede0ea1d340"}, - {file = "propcache-0.3.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9ddd49258610499aab83b4f5b61b32e11fce873586282a0e972e5ab3bcadee51"}, - {file = "propcache-0.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2578541776769b500bada3f8a4eeaf944530516b6e90c089aa368266ed70c49e"}, - {file = "propcache-0.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d8074c5dd61c8a3e915fa8fc04754fa55cfa5978200d2daa1e2d4294c1f136aa"}, - {file = "propcache-0.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b58229a844931bca61b3a20efd2be2a2acb4ad1622fc026504309a6883686fbf"}, - {file = "propcache-0.3.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e45377d5d6fefe1677da2a2c07b024a6dac782088e37c0b1efea4cfe2b1be19b"}, - {file = "propcache-0.3.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ec5060592d83454e8063e487696ac3783cc48c9a329498bafae0d972bc7816c9"}, - {file = "propcache-0.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15010f29fbed80e711db272909a074dc79858c6d28e2915704cfc487a8ac89c6"}, - {file = "propcache-0.3.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a254537b9b696ede293bfdbc0a65200e8e4507bc9f37831e2a0318a9b333c85c"}, - {file = "propcache-0.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2b975528998de037dfbc10144b8aed9b8dd5a99ec547f14d1cb7c5665a43f075"}, - {file = "propcache-0.3.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:19d36bb351ad5554ff20f2ae75f88ce205b0748c38b146c75628577020351e3c"}, - {file = "propcache-0.3.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:6032231d4a5abd67c7f71168fd64a47b6b451fbcb91c8397c2f7610e67683810"}, - {file = "propcache-0.3.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:6985a593417cdbc94c7f9c3403747335e450c1599da1647a5af76539672464d3"}, - {file = "propcache-0.3.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:6a1948df1bb1d56b5e7b0553c0fa04fd0e320997ae99689488201f19fa90d2e7"}, - {file = "propcache-0.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:8319293e85feadbbfe2150a5659dbc2ebc4afdeaf7d98936fb9a2f2ba0d4c35c"}, - {file = "propcache-0.3.0-cp311-cp311-win32.whl", hash = "sha256:63f26258a163c34542c24808f03d734b338da66ba91f410a703e505c8485791d"}, - {file = "propcache-0.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:cacea77ef7a2195f04f9279297684955e3d1ae4241092ff0cfcef532bb7a1c32"}, - {file = "propcache-0.3.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e53d19c2bf7d0d1e6998a7e693c7e87300dd971808e6618964621ccd0e01fe4e"}, - {file = "propcache-0.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a61a68d630e812b67b5bf097ab84e2cd79b48c792857dc10ba8a223f5b06a2af"}, - {file = "propcache-0.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fb91d20fa2d3b13deea98a690534697742029f4fb83673a3501ae6e3746508b5"}, - {file = "propcache-0.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67054e47c01b7b349b94ed0840ccae075449503cf1fdd0a1fdd98ab5ddc2667b"}, - {file = "propcache-0.3.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:997e7b8f173a391987df40f3b52c423e5850be6f6df0dcfb5376365440b56667"}, - {file = "propcache-0.3.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d663fd71491dde7dfdfc899d13a067a94198e90695b4321084c6e450743b8c7"}, - {file = "propcache-0.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8884ba1a0fe7210b775106b25850f5e5a9dc3c840d1ae9924ee6ea2eb3acbfe7"}, - {file = "propcache-0.3.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aa806bbc13eac1ab6291ed21ecd2dd426063ca5417dd507e6be58de20e58dfcf"}, - {file = "propcache-0.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6f4d7a7c0aff92e8354cceca6fe223973ddf08401047920df0fcb24be2bd5138"}, - {file = "propcache-0.3.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:9be90eebc9842a93ef8335291f57b3b7488ac24f70df96a6034a13cb58e6ff86"}, - {file = "propcache-0.3.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:bf15fc0b45914d9d1b706f7c9c4f66f2b7b053e9517e40123e137e8ca8958b3d"}, - {file = "propcache-0.3.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5a16167118677d94bb48bfcd91e420088854eb0737b76ec374b91498fb77a70e"}, - {file = "propcache-0.3.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:41de3da5458edd5678b0f6ff66691507f9885f5fe6a0fb99a5d10d10c0fd2d64"}, - {file = "propcache-0.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:728af36011bb5d344c4fe4af79cfe186729efb649d2f8b395d1572fb088a996c"}, - {file = "propcache-0.3.0-cp312-cp312-win32.whl", hash = "sha256:6b5b7fd6ee7b54e01759f2044f936dcf7dea6e7585f35490f7ca0420fe723c0d"}, - {file = "propcache-0.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:2d15bc27163cd4df433e75f546b9ac31c1ba7b0b128bfb1b90df19082466ff57"}, - {file = "propcache-0.3.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a2b9bf8c79b660d0ca1ad95e587818c30ccdb11f787657458d6f26a1ea18c568"}, - {file = "propcache-0.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b0c1a133d42c6fc1f5fbcf5c91331657a1ff822e87989bf4a6e2e39b818d0ee9"}, - {file = "propcache-0.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bb2f144c6d98bb5cbc94adeb0447cfd4c0f991341baa68eee3f3b0c9c0e83767"}, - {file = "propcache-0.3.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1323cd04d6e92150bcc79d0174ce347ed4b349d748b9358fd2e497b121e03c8"}, - {file = "propcache-0.3.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b812b3cb6caacd072276ac0492d249f210006c57726b6484a1e1805b3cfeea0"}, - {file = "propcache-0.3.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:742840d1d0438eb7ea4280f3347598f507a199a35a08294afdcc560c3739989d"}, - {file = "propcache-0.3.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7c6e7e4f9167fddc438cd653d826f2222222564daed4116a02a184b464d3ef05"}, - {file = "propcache-0.3.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a94ffc66738da99232ddffcf7910e0f69e2bbe3a0802e54426dbf0714e1c2ffe"}, - {file = "propcache-0.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3c6ec957025bf32b15cbc6b67afe233c65b30005e4c55fe5768e4bb518d712f1"}, - {file = "propcache-0.3.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:549722908de62aa0b47a78b90531c022fa6e139f9166be634f667ff45632cc92"}, - {file = "propcache-0.3.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:5d62c4f6706bff5d8a52fd51fec6069bef69e7202ed481486c0bc3874912c787"}, - {file = "propcache-0.3.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:24c04f8fbf60094c531667b8207acbae54146661657a1b1be6d3ca7773b7a545"}, - {file = "propcache-0.3.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:7c5f5290799a3f6539cc5e6f474c3e5c5fbeba74a5e1e5be75587746a940d51e"}, - {file = "propcache-0.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4fa0e7c9c3cf7c276d4f6ab9af8adddc127d04e0fcabede315904d2ff76db626"}, - {file = "propcache-0.3.0-cp313-cp313-win32.whl", hash = "sha256:ee0bd3a7b2e184e88d25c9baa6a9dc609ba25b76daae942edfb14499ac7ec374"}, - {file = "propcache-0.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:1c8f7d896a16da9455f882870a507567d4f58c53504dc2d4b1e1d386dfe4588a"}, - {file = "propcache-0.3.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:e560fd75aaf3e5693b91bcaddd8b314f4d57e99aef8a6c6dc692f935cc1e6bbf"}, - {file = "propcache-0.3.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:65a37714b8ad9aba5780325228598a5b16c47ba0f8aeb3dc0514701e4413d7c0"}, - {file = "propcache-0.3.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:07700939b2cbd67bfb3b76a12e1412405d71019df00ca5697ce75e5ef789d829"}, - {file = "propcache-0.3.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7c0fdbdf6983526e269e5a8d53b7ae3622dd6998468821d660d0daf72779aefa"}, - {file = "propcache-0.3.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:794c3dd744fad478b6232289c866c25406ecdfc47e294618bdf1697e69bd64a6"}, - {file = "propcache-0.3.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4544699674faf66fb6b4473a1518ae4999c1b614f0b8297b1cef96bac25381db"}, - {file = "propcache-0.3.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fddb8870bdb83456a489ab67c6b3040a8d5a55069aa6f72f9d872235fbc52f54"}, - {file = "propcache-0.3.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f857034dc68d5ceb30fb60afb6ff2103087aea10a01b613985610e007053a121"}, - {file = "propcache-0.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:02df07041e0820cacc8f739510078f2aadcfd3fc57eaeeb16d5ded85c872c89e"}, - {file = "propcache-0.3.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:f47d52fd9b2ac418c4890aad2f6d21a6b96183c98021f0a48497a904199f006e"}, - {file = "propcache-0.3.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:9ff4e9ecb6e4b363430edf2c6e50173a63e0820e549918adef70515f87ced19a"}, - {file = "propcache-0.3.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:ecc2920630283e0783c22e2ac94427f8cca29a04cfdf331467d4f661f4072dac"}, - {file = "propcache-0.3.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:c441c841e82c5ba7a85ad25986014be8d7849c3cfbdb6004541873505929a74e"}, - {file = "propcache-0.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6c929916cbdb540d3407c66f19f73387f43e7c12fa318a66f64ac99da601bcdf"}, - {file = "propcache-0.3.0-cp313-cp313t-win32.whl", hash = "sha256:0c3e893c4464ebd751b44ae76c12c5f5c1e4f6cbd6fbf67e3783cd93ad221863"}, - {file = "propcache-0.3.0-cp313-cp313t-win_amd64.whl", hash = "sha256:75e872573220d1ee2305b35c9813626e620768248425f58798413e9c39741f46"}, - {file = "propcache-0.3.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:03c091bb752349402f23ee43bb2bff6bd80ccab7c9df6b88ad4322258d6960fc"}, - {file = "propcache-0.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:46ed02532cb66612d42ae5c3929b5e98ae330ea0f3900bc66ec5f4862069519b"}, - {file = "propcache-0.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:11ae6a8a01b8a4dc79093b5d3ca2c8a4436f5ee251a9840d7790dccbd96cb649"}, - {file = "propcache-0.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df03cd88f95b1b99052b52b1bb92173229d7a674df0ab06d2b25765ee8404bce"}, - {file = "propcache-0.3.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:03acd9ff19021bd0567582ac88f821b66883e158274183b9e5586f678984f8fe"}, - {file = "propcache-0.3.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd54895e4ae7d32f1e3dd91261df46ee7483a735017dc6f987904f194aa5fd14"}, - {file = "propcache-0.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26a67e5c04e3119594d8cfae517f4b9330c395df07ea65eab16f3d559b7068fe"}, - {file = "propcache-0.3.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee25f1ac091def37c4b59d192bbe3a206298feeb89132a470325bf76ad122a1e"}, - {file = "propcache-0.3.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:58e6d2a5a7cb3e5f166fd58e71e9a4ff504be9dc61b88167e75f835da5764d07"}, - {file = "propcache-0.3.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:be90c94570840939fecedf99fa72839aed70b0ced449b415c85e01ae67422c90"}, - {file = "propcache-0.3.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:49ea05212a529c2caffe411e25a59308b07d6e10bf2505d77da72891f9a05641"}, - {file = "propcache-0.3.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:119e244ab40f70a98c91906d4c1f4c5f2e68bd0b14e7ab0a06922038fae8a20f"}, - {file = "propcache-0.3.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:507c5357a8d8b4593b97fb669c50598f4e6cccbbf77e22fa9598aba78292b4d7"}, - {file = "propcache-0.3.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:8526b0941ec5a40220fc4dfde76aed58808e2b309c03e9fa8e2260083ef7157f"}, - {file = "propcache-0.3.0-cp39-cp39-win32.whl", hash = "sha256:7cedd25e5f678f7738da38037435b340694ab34d424938041aa630d8bac42663"}, - {file = "propcache-0.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:bf4298f366ca7e1ad1d21bbb58300a6985015909964077afd37559084590c929"}, - {file = "propcache-0.3.0-py3-none-any.whl", hash = "sha256:67dda3c7325691c2081510e92c561f465ba61b975f481735aefdfc845d2cd043"}, - {file = "propcache-0.3.0.tar.gz", hash = "sha256:a8fd93de4e1d278046345f49e2238cdb298589325849b2645d4a94c53faeffc5"}, + {file = "propcache-0.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c2d1fa3201efaf55d730400d945b5b3ab6e672e100ba0f9a409d950ab25d7db"}, + {file = "propcache-0.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1eb2994229cc8ce7fe9b3db88f5465f5fd8651672840b2e426b88cdb1a30aac8"}, + {file = "propcache-0.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:66c1f011f45a3b33d7bcb22daed4b29c0c9e2224758b6be00686731e1b46f925"}, + {file = "propcache-0.4.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9a52009f2adffe195d0b605c25ec929d26b36ef986ba85244891dee3b294df21"}, + {file = "propcache-0.4.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5d4e2366a9c7b837555cf02fb9be2e3167d333aff716332ef1b7c3a142ec40c5"}, + {file = "propcache-0.4.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:9d2b6caef873b4f09e26ea7e33d65f42b944837563a47a94719cc3544319a0db"}, + {file = "propcache-0.4.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2b16ec437a8c8a965ecf95739448dd938b5c7f56e67ea009f4300d8df05f32b7"}, + {file = "propcache-0.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:296f4c8ed03ca7476813fe666c9ea97869a8d7aec972618671b33a38a5182ef4"}, + {file = "propcache-0.4.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:1f0978529a418ebd1f49dad413a2b68af33f85d5c5ca5c6ca2a3bed375a7ac60"}, + {file = "propcache-0.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fd138803047fb4c062b1c1dd95462f5209456bfab55c734458f15d11da288f8f"}, + {file = "propcache-0.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8c9b3cbe4584636d72ff556d9036e0c9317fa27b3ac1f0f558e7e84d1c9c5900"}, + {file = "propcache-0.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f93243fdc5657247533273ac4f86ae106cc6445a0efacb9a1bfe982fcfefd90c"}, + {file = "propcache-0.4.1-cp310-cp310-win32.whl", hash = "sha256:a0ee98db9c5f80785b266eb805016e36058ac72c51a064040f2bc43b61101cdb"}, + {file = "propcache-0.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:1cdb7988c4e5ac7f6d175a28a9aa0c94cb6f2ebe52756a3c0cda98d2809a9e37"}, + {file = "propcache-0.4.1-cp310-cp310-win_arm64.whl", hash = "sha256:d82ad62b19645419fe79dd63b3f9253e15b30e955c0170e5cebc350c1844e581"}, + {file = "propcache-0.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:60a8fda9644b7dfd5dece8c61d8a85e271cb958075bfc4e01083c148b61a7caf"}, + {file = "propcache-0.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c30b53e7e6bda1d547cabb47c825f3843a0a1a42b0496087bb58d8fedf9f41b5"}, + {file = "propcache-0.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6918ecbd897443087a3b7cd978d56546a812517dcaaca51b49526720571fa93e"}, + {file = "propcache-0.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3d902a36df4e5989763425a8ab9e98cd8ad5c52c823b34ee7ef307fd50582566"}, + {file = "propcache-0.4.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a9695397f85973bb40427dedddf70d8dc4a44b22f1650dd4af9eedf443d45165"}, + {file = "propcache-0.4.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2bb07ffd7eaad486576430c89f9b215f9e4be68c4866a96e97db9e97fead85dc"}, + {file = "propcache-0.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fd6f30fdcf9ae2a70abd34da54f18da086160e4d7d9251f81f3da0ff84fc5a48"}, + {file = "propcache-0.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fc38cba02d1acba4e2869eef1a57a43dfbd3d49a59bf90dda7444ec2be6a5570"}, + {file = "propcache-0.4.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:67fad6162281e80e882fb3ec355398cf72864a54069d060321f6cd0ade95fe85"}, + {file = "propcache-0.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f10207adf04d08bec185bae14d9606a1444715bc99180f9331c9c02093e1959e"}, + {file = "propcache-0.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e9b0d8d0845bbc4cfcdcbcdbf5086886bc8157aa963c31c777ceff7846c77757"}, + {file = "propcache-0.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:981333cb2f4c1896a12f4ab92a9cc8f09ea664e9b7dbdc4eff74627af3a11c0f"}, + {file = "propcache-0.4.1-cp311-cp311-win32.whl", hash = "sha256:f1d2f90aeec838a52f1c1a32fe9a619fefd5e411721a9117fbf82aea638fe8a1"}, + {file = "propcache-0.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:364426a62660f3f699949ac8c621aad6977be7126c5807ce48c0aeb8e7333ea6"}, + {file = "propcache-0.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:e53f3a38d3510c11953f3e6a33f205c6d1b001129f972805ca9b42fc308bc239"}, + {file = "propcache-0.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e153e9cd40cc8945138822807139367f256f89c6810c2634a4f6902b52d3b4e2"}, + {file = "propcache-0.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cd547953428f7abb73c5ad82cbb32109566204260d98e41e5dfdc682eb7f8403"}, + {file = "propcache-0.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f048da1b4f243fc44f205dfd320933a951b8d89e0afd4c7cacc762a8b9165207"}, + {file = "propcache-0.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ec17c65562a827bba85e3872ead335f95405ea1674860d96483a02f5c698fa72"}, + {file = "propcache-0.4.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:405aac25c6394ef275dee4c709be43745d36674b223ba4eb7144bf4d691b7367"}, + {file = "propcache-0.4.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0013cb6f8dde4b2a2f66903b8ba740bdfe378c943c4377a200551ceb27f379e4"}, + {file = "propcache-0.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:15932ab57837c3368b024473a525e25d316d8353016e7cc0e5ba9eb343fbb1cf"}, + {file = "propcache-0.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:031dce78b9dc099f4c29785d9cf5577a3faf9ebf74ecbd3c856a7b92768c3df3"}, + {file = "propcache-0.4.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ab08df6c9a035bee56e31af99be621526bd237bea9f32def431c656b29e41778"}, + {file = "propcache-0.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4d7af63f9f93fe593afbf104c21b3b15868efb2c21d07d8732c0c4287e66b6a6"}, + {file = "propcache-0.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:cfc27c945f422e8b5071b6e93169679e4eb5bf73bbcbf1ba3ae3a83d2f78ebd9"}, + {file = "propcache-0.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:35c3277624a080cc6ec6f847cbbbb5b49affa3598c4535a0a4682a697aaa5c75"}, + {file = "propcache-0.4.1-cp312-cp312-win32.whl", hash = "sha256:671538c2262dadb5ba6395e26c1731e1d52534bfe9ae56d0b5573ce539266aa8"}, + {file = "propcache-0.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:cb2d222e72399fcf5890d1d5cc1060857b9b236adff2792ff48ca2dfd46c81db"}, + {file = "propcache-0.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:204483131fb222bdaaeeea9f9e6c6ed0cac32731f75dfc1d4a567fc1926477c1"}, + {file = "propcache-0.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:43eedf29202c08550aac1d14e0ee619b0430aaef78f85864c1a892294fbc28cf"}, + {file = "propcache-0.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d62cdfcfd89ccb8de04e0eda998535c406bf5e060ffd56be6c586cbcc05b3311"}, + {file = "propcache-0.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cae65ad55793da34db5f54e4029b89d3b9b9490d8abe1b4c7ab5d4b8ec7ebf74"}, + {file = "propcache-0.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:333ddb9031d2704a301ee3e506dc46b1fe5f294ec198ed6435ad5b6a085facfe"}, + {file = "propcache-0.4.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:fd0858c20f078a32cf55f7e81473d96dcf3b93fd2ccdb3d40fdf54b8573df3af"}, + {file = "propcache-0.4.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:678ae89ebc632c5c204c794f8dab2837c5f159aeb59e6ed0539500400577298c"}, + {file = "propcache-0.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d472aeb4fbf9865e0c6d622d7f4d54a4e101a89715d8904282bb5f9a2f476c3f"}, + {file = "propcache-0.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4d3df5fa7e36b3225954fba85589da77a0fe6a53e3976de39caf04a0db4c36f1"}, + {file = "propcache-0.4.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:ee17f18d2498f2673e432faaa71698032b0127ebf23ae5974eeaf806c279df24"}, + {file = "propcache-0.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:580e97762b950f993ae618e167e7be9256b8353c2dcd8b99ec100eb50f5286aa"}, + {file = "propcache-0.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:501d20b891688eb8e7aa903021f0b72d5a55db40ffaab27edefd1027caaafa61"}, + {file = "propcache-0.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a0bd56e5b100aef69bd8562b74b46254e7c8812918d3baa700c8a8009b0af66"}, + {file = "propcache-0.4.1-cp313-cp313-win32.whl", hash = "sha256:bcc9aaa5d80322bc2fb24bb7accb4a30f81e90ab8d6ba187aec0744bc302ad81"}, + {file = "propcache-0.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:381914df18634f5494334d201e98245c0596067504b9372d8cf93f4bb23e025e"}, + {file = "propcache-0.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:8873eb4460fd55333ea49b7d189749ecf6e55bf85080f11b1c4530ed3034cba1"}, + {file = "propcache-0.4.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:92d1935ee1f8d7442da9c0c4fa7ac20d07e94064184811b685f5c4fada64553b"}, + {file = "propcache-0.4.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:473c61b39e1460d386479b9b2f337da492042447c9b685f28be4f74d3529e566"}, + {file = "propcache-0.4.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:c0ef0aaafc66fbd87842a3fe3902fd889825646bc21149eafe47be6072725835"}, + {file = "propcache-0.4.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f95393b4d66bfae908c3ca8d169d5f79cd65636ae15b5e7a4f6e67af675adb0e"}, + {file = "propcache-0.4.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c07fda85708bc48578467e85099645167a955ba093be0a2dcba962195676e859"}, + {file = "propcache-0.4.1-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:af223b406d6d000830c6f65f1e6431783fc3f713ba3e6cc8c024d5ee96170a4b"}, + {file = "propcache-0.4.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a78372c932c90ee474559c5ddfffd718238e8673c340dc21fe45c5b8b54559a0"}, + {file = "propcache-0.4.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:564d9f0d4d9509e1a870c920a89b2fec951b44bf5ba7d537a9e7c1ccec2c18af"}, + {file = "propcache-0.4.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:17612831fda0138059cc5546f4d12a2aacfb9e47068c06af35c400ba58ba7393"}, + {file = "propcache-0.4.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:41a89040cb10bd345b3c1a873b2bf36413d48da1def52f268a055f7398514874"}, + {file = "propcache-0.4.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e35b88984e7fa64aacecea39236cee32dd9bd8c55f57ba8a75cf2399553f9bd7"}, + {file = "propcache-0.4.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6f8b465489f927b0df505cbe26ffbeed4d6d8a2bbc61ce90eb074ff129ef0ab1"}, + {file = "propcache-0.4.1-cp313-cp313t-win32.whl", hash = "sha256:2ad890caa1d928c7c2965b48f3a3815c853180831d0e5503d35cf00c472f4717"}, + {file = "propcache-0.4.1-cp313-cp313t-win_amd64.whl", hash = "sha256:f7ee0e597f495cf415bcbd3da3caa3bd7e816b74d0d52b8145954c5e6fd3ff37"}, + {file = "propcache-0.4.1-cp313-cp313t-win_arm64.whl", hash = "sha256:929d7cbe1f01bb7baffb33dc14eb5691c95831450a26354cd210a8155170c93a"}, + {file = "propcache-0.4.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3f7124c9d820ba5548d431afb4632301acf965db49e666aa21c305cbe8c6de12"}, + {file = "propcache-0.4.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:c0d4b719b7da33599dfe3b22d3db1ef789210a0597bc650b7cee9c77c2be8c5c"}, + {file = "propcache-0.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9f302f4783709a78240ebc311b793f123328716a60911d667e0c036bc5dcbded"}, + {file = "propcache-0.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c80ee5802e3fb9ea37938e7eecc307fb984837091d5fd262bb37238b1ae97641"}, + {file = "propcache-0.4.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ed5a841e8bb29a55fb8159ed526b26adc5bdd7e8bd7bf793ce647cb08656cdf4"}, + {file = "propcache-0.4.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:55c72fd6ea2da4c318e74ffdf93c4fe4e926051133657459131a95c846d16d44"}, + {file = "propcache-0.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8326e144341460402713f91df60ade3c999d601e7eb5ff8f6f7862d54de0610d"}, + {file = "propcache-0.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:060b16ae65bc098da7f6d25bf359f1f31f688384858204fe5d652979e0015e5b"}, + {file = "propcache-0.4.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:89eb3fa9524f7bec9de6e83cf3faed9d79bffa560672c118a96a171a6f55831e"}, + {file = "propcache-0.4.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:dee69d7015dc235f526fe80a9c90d65eb0039103fe565776250881731f06349f"}, + {file = "propcache-0.4.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:5558992a00dfd54ccbc64a32726a3357ec93825a418a401f5cc67df0ac5d9e49"}, + {file = "propcache-0.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c9b822a577f560fbd9554812526831712c1436d2c046cedee4c3796d3543b144"}, + {file = "propcache-0.4.1-cp314-cp314-win32.whl", hash = "sha256:ab4c29b49d560fe48b696cdcb127dd36e0bc2472548f3bf56cc5cb3da2b2984f"}, + {file = "propcache-0.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:5a103c3eb905fcea0ab98be99c3a9a5ab2de60228aa5aceedc614c0281cf6153"}, + {file = "propcache-0.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:74c1fb26515153e482e00177a1ad654721bf9207da8a494a0c05e797ad27b992"}, + {file = "propcache-0.4.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:824e908bce90fb2743bd6b59db36eb4f45cd350a39637c9f73b1c1ea66f5b75f"}, + {file = "propcache-0.4.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:c2b5e7db5328427c57c8e8831abda175421b709672f6cfc3d630c3b7e2146393"}, + {file = "propcache-0.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6f6ff873ed40292cd4969ef5310179afd5db59fdf055897e282485043fc80ad0"}, + {file = "propcache-0.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:49a2dc67c154db2c1463013594c458881a069fcf98940e61a0569016a583020a"}, + {file = "propcache-0.4.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:005f08e6a0529984491e37d8dbc3dd86f84bd78a8ceb5fa9a021f4c48d4984be"}, + {file = "propcache-0.4.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5c3310452e0d31390da9035c348633b43d7e7feb2e37be252be6da45abd1abcc"}, + {file = "propcache-0.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c3c70630930447f9ef1caac7728c8ad1c56bc5015338b20fed0d08ea2480b3a"}, + {file = "propcache-0.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8e57061305815dfc910a3634dcf584f08168a8836e6999983569f51a8544cd89"}, + {file = "propcache-0.4.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:521a463429ef54143092c11a77e04056dd00636f72e8c45b70aaa3140d639726"}, + {file = "propcache-0.4.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:120c964da3fdc75e3731aa392527136d4ad35868cc556fd09bb6d09172d9a367"}, + {file = "propcache-0.4.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:d8f353eb14ee3441ee844ade4277d560cdd68288838673273b978e3d6d2c8f36"}, + {file = "propcache-0.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ab2943be7c652f09638800905ee1bab2c544e537edb57d527997a24c13dc1455"}, + {file = "propcache-0.4.1-cp314-cp314t-win32.whl", hash = "sha256:05674a162469f31358c30bcaa8883cb7829fa3110bf9c0991fe27d7896c42d85"}, + {file = "propcache-0.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:990f6b3e2a27d683cb7602ed6c86f15ee6b43b1194736f9baaeb93d0016633b1"}, + {file = "propcache-0.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:ecef2343af4cc68e05131e45024ba34f6095821988a9d0a02aa7c73fcc448aa9"}, + {file = "propcache-0.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3d233076ccf9e450c8b3bc6720af226b898ef5d051a2d145f7d765e6e9f9bcff"}, + {file = "propcache-0.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:357f5bb5c377a82e105e44bd3d52ba22b616f7b9773714bff93573988ef0a5fb"}, + {file = "propcache-0.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cbc3b6dfc728105b2a57c06791eb07a94229202ea75c59db644d7d496b698cac"}, + {file = "propcache-0.4.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:182b51b421f0501952d938dc0b0eb45246a5b5153c50d42b495ad5fb7517c888"}, + {file = "propcache-0.4.1-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4b536b39c5199b96fc6245eb5fb796c497381d3942f169e44e8e392b29c9ebcc"}, + {file = "propcache-0.4.1-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:db65d2af507bbfbdcedb254a11149f894169d90488dd3e7190f7cdcb2d6cd57a"}, + {file = "propcache-0.4.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fd2dbc472da1f772a4dae4fa24be938a6c544671a912e30529984dd80400cd88"}, + {file = "propcache-0.4.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:daede9cd44e0f8bdd9e6cc9a607fc81feb80fae7a5fc6cecaff0e0bb32e42d00"}, + {file = "propcache-0.4.1-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:71b749281b816793678ae7f3d0d84bd36e694953822eaad408d682efc5ca18e0"}, + {file = "propcache-0.4.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:0002004213ee1f36cfb3f9a42b5066100c44276b9b72b4e1504cddd3d692e86e"}, + {file = "propcache-0.4.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:fe49d0a85038f36ba9e3ffafa1103e61170b28e95b16622e11be0a0ea07c6781"}, + {file = "propcache-0.4.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:99d43339c83aaf4d32bda60928231848eee470c6bda8d02599cc4cebe872d183"}, + {file = "propcache-0.4.1-cp39-cp39-win32.whl", hash = "sha256:a129e76735bc792794d5177069691c3217898b9f5cee2b2661471e52ffe13f19"}, + {file = "propcache-0.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:948dab269721ae9a87fd16c514a0a2c2a1bdb23a9a61b969b0f9d9ee2968546f"}, + {file = "propcache-0.4.1-cp39-cp39-win_arm64.whl", hash = "sha256:5fd37c406dd6dc85aa743e214cef35dc54bbdd1419baac4f6ae5e5b1a2976938"}, + {file = "propcache-0.4.1-py3-none-any.whl", hash = "sha256:af2a6052aeb6cf17d3e46ee169099044fd8224cbaf75c76a2ef596e8163e2237"}, + {file = "propcache-0.4.1.tar.gz", hash = "sha256:f48107a8c637e80362555f37ecf49abe20370e557cc4ab374f04ec4423c97c3d"}, ] [[package]] @@ -1214,98 +1485,125 @@ files = [ [[package]] name = "psycopg" -version = "3.2.5" +version = "3.2.10" description = "PostgreSQL database adapter for Python" optional = false python-versions = ">=3.8" groups = ["main"] files = [ - {file = "psycopg-3.2.5-py3-none-any.whl", hash = "sha256:b782130983e5b3de30b4c529623d3687033b4dafa05bb661fc6bf45837ca5879"}, - {file = "psycopg-3.2.5.tar.gz", hash = "sha256:f5f750611c67cb200e85b408882f29265c66d1de7f813add4f8125978bfd70e8"}, + {file = "psycopg-3.2.10-py3-none-any.whl", hash = "sha256:ab5caf09a9ec42e314a21f5216dbcceac528e0e05142e42eea83a3b28b320ac3"}, + {file = "psycopg-3.2.10.tar.gz", hash = "sha256:0bce99269d16ed18401683a8569b2c5abd94f72f8364856d56c0389bcd50972a"}, ] [package.dependencies] +psycopg-pool = {version = "*", optional = true, markers = "extra == \"pool\""} typing-extensions = {version = ">=4.6", markers = "python_version < \"3.13\""} tzdata = {version = "*", markers = "sys_platform == \"win32\""} [package.extras] -binary = ["psycopg-binary (==3.2.5)"] -c = ["psycopg-c (==3.2.5)"] -dev = ["ast-comments (>=1.1.2)", "black (>=24.1.0)", "codespell (>=2.2)", "dnspython (>=2.1)", "flake8 (>=4.0)", "isort-psycopg", "isort[colors] (>=6.0)", "mypy (>=1.14)", "pre-commit (>=4.0.1)", "types-setuptools (>=57.4)", "wheel (>=0.37)"] +binary = ["psycopg-binary (==3.2.10)"] +c = ["psycopg-c (==3.2.10)"] +dev = ["ast-comments (>=1.1.2)", "black (>=24.1.0)", "codespell (>=2.2)", "dnspython (>=2.1)", "flake8 (>=4.0)", "isort-psycopg", "isort[colors] (>=6.0)", "mypy (>=1.14)", "pre-commit (>=4.0.1)", "types-setuptools (>=57.4)", "types-shapely (>=2.0)", "wheel (>=0.37)"] docs = ["Sphinx (>=5.0)", "furo (==2022.6.21)", "sphinx-autobuild (>=2021.3.14)", "sphinx-autodoc-typehints (>=1.12)"] pool = ["psycopg-pool"] test = ["anyio (>=4.0)", "mypy (>=1.14)", "pproxy (>=2.7)", "pytest (>=6.2.5)", "pytest-cov (>=3.0)", "pytest-randomly (>=3.5)"] +[[package]] +name = "psycopg-pool" +version = "3.2.6" +description = "Connection Pool for Psycopg" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "psycopg_pool-3.2.6-py3-none-any.whl", hash = "sha256:5887318a9f6af906d041a0b1dc1c60f8f0dda8340c2572b74e10907b51ed5da7"}, + {file = "psycopg_pool-3.2.6.tar.gz", hash = "sha256:0f92a7817719517212fbfe2fd58b8c35c1850cdd2a80d36b581ba2085d9148e5"}, +] + +[package.dependencies] +typing-extensions = ">=4.6" + [[package]] name = "pycparser" -version = "2.22" +version = "2.23" description = "C parser in Python" optional = false python-versions = ">=3.8" groups = ["main"] +markers = "implementation_name != \"PyPy\"" files = [ - {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, - {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, + {file = "pycparser-2.23-py3-none-any.whl", hash = "sha256:e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934"}, + {file = "pycparser-2.23.tar.gz", hash = "sha256:78816d4f24add8f10a06d6f05b4d424ad9e96cfebf68a4ddc99c65c0720d00c2"}, ] [[package]] name = "pycryptodome" -version = "3.21.0" +version = "3.23.0" description = "Cryptographic library for Python" optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" groups = ["main"] files = [ - {file = "pycryptodome-3.21.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:dad9bf36eda068e89059d1f07408e397856be9511d7113ea4b586642a429a4fd"}, - {file = "pycryptodome-3.21.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:a1752eca64c60852f38bb29e2c86fca30d7672c024128ef5d70cc15868fa10f4"}, - {file = "pycryptodome-3.21.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:3ba4cc304eac4d4d458f508d4955a88ba25026890e8abff9b60404f76a62c55e"}, - {file = "pycryptodome-3.21.0-cp27-cp27m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7cb087b8612c8a1a14cf37dd754685be9a8d9869bed2ffaaceb04850a8aeef7e"}, - {file = "pycryptodome-3.21.0-cp27-cp27m-musllinux_1_1_aarch64.whl", hash = "sha256:26412b21df30b2861424a6c6d5b1d8ca8107612a4cfa4d0183e71c5d200fb34a"}, - {file = "pycryptodome-3.21.0-cp27-cp27m-win32.whl", hash = "sha256:cc2269ab4bce40b027b49663d61d816903a4bd90ad88cb99ed561aadb3888dd3"}, - {file = "pycryptodome-3.21.0-cp27-cp27m-win_amd64.whl", hash = "sha256:0fa0a05a6a697ccbf2a12cec3d6d2650b50881899b845fac6e87416f8cb7e87d"}, - {file = "pycryptodome-3.21.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:6cce52e196a5f1d6797ff7946cdff2038d3b5f0aba4a43cb6bf46b575fd1b5bb"}, - {file = "pycryptodome-3.21.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:a915597ffccabe902e7090e199a7bf7a381c5506a747d5e9d27ba55197a2c568"}, - {file = "pycryptodome-3.21.0-cp27-cp27mu-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a4e74c522d630766b03a836c15bff77cb657c5fdf098abf8b1ada2aebc7d0819"}, - {file = "pycryptodome-3.21.0-cp27-cp27mu-musllinux_1_1_aarch64.whl", hash = "sha256:a3804675283f4764a02db05f5191eb8fec2bb6ca34d466167fc78a5f05bbe6b3"}, - {file = "pycryptodome-3.21.0-cp36-abi3-macosx_10_9_universal2.whl", hash = "sha256:2480ec2c72438430da9f601ebc12c518c093c13111a5c1644c82cdfc2e50b1e4"}, - {file = "pycryptodome-3.21.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:de18954104667f565e2fbb4783b56667f30fb49c4d79b346f52a29cb198d5b6b"}, - {file = "pycryptodome-3.21.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2de4b7263a33947ff440412339cb72b28a5a4c769b5c1ca19e33dd6cd1dcec6e"}, - {file = "pycryptodome-3.21.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0714206d467fc911042d01ea3a1847c847bc10884cf674c82e12915cfe1649f8"}, - {file = "pycryptodome-3.21.0-cp36-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7d85c1b613121ed3dbaa5a97369b3b757909531a959d229406a75b912dd51dd1"}, - {file = "pycryptodome-3.21.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:8898a66425a57bcf15e25fc19c12490b87bd939800f39a03ea2de2aea5e3611a"}, - {file = "pycryptodome-3.21.0-cp36-abi3-musllinux_1_2_i686.whl", hash = "sha256:932c905b71a56474bff8a9c014030bc3c882cee696b448af920399f730a650c2"}, - {file = "pycryptodome-3.21.0-cp36-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:18caa8cfbc676eaaf28613637a89980ad2fd96e00c564135bf90bc3f0b34dd93"}, - {file = "pycryptodome-3.21.0-cp36-abi3-win32.whl", hash = "sha256:280b67d20e33bb63171d55b1067f61fbd932e0b1ad976b3a184303a3dad22764"}, - {file = "pycryptodome-3.21.0-cp36-abi3-win_amd64.whl", hash = "sha256:b7aa25fc0baa5b1d95b7633af4f5f1838467f1815442b22487426f94e0d66c53"}, - {file = "pycryptodome-3.21.0-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:2cb635b67011bc147c257e61ce864879ffe6d03342dc74b6045059dfbdedafca"}, - {file = "pycryptodome-3.21.0-pp27-pypy_73-win32.whl", hash = "sha256:4c26a2f0dc15f81ea3afa3b0c87b87e501f235d332b7f27e2225ecb80c0b1cdd"}, - {file = "pycryptodome-3.21.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:d5ebe0763c982f069d3877832254f64974139f4f9655058452603ff559c482e8"}, - {file = "pycryptodome-3.21.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ee86cbde706be13f2dec5a42b52b1c1d1cbb90c8e405c68d0755134735c8dc6"}, - {file = "pycryptodome-3.21.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0fd54003ec3ce4e0f16c484a10bc5d8b9bd77fa662a12b85779a2d2d85d67ee0"}, - {file = "pycryptodome-3.21.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:5dfafca172933506773482b0e18f0cd766fd3920bd03ec85a283df90d8a17bc6"}, - {file = "pycryptodome-3.21.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:590ef0898a4b0a15485b05210b4a1c9de8806d3ad3d47f74ab1dc07c67a6827f"}, - {file = "pycryptodome-3.21.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f35e442630bc4bc2e1878482d6f59ea22e280d7121d7adeaedba58c23ab6386b"}, - {file = "pycryptodome-3.21.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff99f952db3db2fbe98a0b355175f93ec334ba3d01bbde25ad3a5a33abc02b58"}, - {file = "pycryptodome-3.21.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:8acd7d34af70ee63f9a849f957558e49a98f8f1634f86a59d2be62bb8e93f71c"}, - {file = "pycryptodome-3.21.0.tar.gz", hash = "sha256:f7787e0d469bdae763b876174cf2e6c0f7be79808af26b1da96f1a64bcf47297"}, + {file = "pycryptodome-3.23.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a176b79c49af27d7f6c12e4b178b0824626f40a7b9fed08f712291b6d54bf566"}, + {file = "pycryptodome-3.23.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:573a0b3017e06f2cffd27d92ef22e46aa3be87a2d317a5abf7cc0e84e321bd75"}, + {file = "pycryptodome-3.23.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:63dad881b99ca653302b2c7191998dd677226222a3f2ea79999aa51ce695f720"}, + {file = "pycryptodome-3.23.0-cp27-cp27m-win32.whl", hash = "sha256:b34e8e11d97889df57166eda1e1ddd7676da5fcd4d71a0062a760e75060514b4"}, + {file = "pycryptodome-3.23.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:7ac1080a8da569bde76c0a104589c4f414b8ba296c0b3738cf39a466a9fb1818"}, + {file = "pycryptodome-3.23.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:6fe8258e2039eceb74dfec66b3672552b6b7d2c235b2dfecc05d16b8921649a8"}, + {file = "pycryptodome-3.23.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:0011f7f00cdb74879142011f95133274741778abba114ceca229adbf8e62c3e4"}, + {file = "pycryptodome-3.23.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:90460fc9e088ce095f9ee8356722d4f10f86e5be06e2354230a9880b9c549aae"}, + {file = "pycryptodome-3.23.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4764e64b269fc83b00f682c47443c2e6e85b18273712b98aa43bcb77f8570477"}, + {file = "pycryptodome-3.23.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb8f24adb74984aa0e5d07a2368ad95276cf38051fe2dc6605cbcf482e04f2a7"}, + {file = "pycryptodome-3.23.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d97618c9c6684a97ef7637ba43bdf6663a2e2e77efe0f863cce97a76af396446"}, + {file = "pycryptodome-3.23.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9a53a4fe5cb075075d515797d6ce2f56772ea7e6a1e5e4b96cf78a14bac3d265"}, + {file = "pycryptodome-3.23.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:763d1d74f56f031788e5d307029caef067febf890cd1f8bf61183ae142f1a77b"}, + {file = "pycryptodome-3.23.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:954af0e2bd7cea83ce72243b14e4fb518b18f0c1649b576d114973e2073b273d"}, + {file = "pycryptodome-3.23.0-cp313-cp313t-win32.whl", hash = "sha256:257bb3572c63ad8ba40b89f6fc9d63a2a628e9f9708d31ee26560925ebe0210a"}, + {file = "pycryptodome-3.23.0-cp313-cp313t-win_amd64.whl", hash = "sha256:6501790c5b62a29fcb227bd6b62012181d886a767ce9ed03b303d1f22eb5c625"}, + {file = "pycryptodome-3.23.0-cp313-cp313t-win_arm64.whl", hash = "sha256:9a77627a330ab23ca43b48b130e202582e91cc69619947840ea4d2d1be21eb39"}, + {file = "pycryptodome-3.23.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:187058ab80b3281b1de11c2e6842a357a1f71b42cb1e15bce373f3d238135c27"}, + {file = "pycryptodome-3.23.0-cp37-abi3-macosx_10_9_x86_64.whl", hash = "sha256:cfb5cd445280c5b0a4e6187a7ce8de5a07b5f3f897f235caa11f1f435f182843"}, + {file = "pycryptodome-3.23.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67bd81fcbe34f43ad9422ee8fd4843c8e7198dd88dd3d40e6de42ee65fbe1490"}, + {file = "pycryptodome-3.23.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8987bd3307a39bc03df5c8e0e3d8be0c4c3518b7f044b0f4c15d1aa78f52575"}, + {file = "pycryptodome-3.23.0-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aa0698f65e5b570426fc31b8162ed4603b0c2841cbb9088e2b01641e3065915b"}, + {file = "pycryptodome-3.23.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:53ecbafc2b55353edcebd64bf5da94a2a2cdf5090a6915bcca6eca6cc452585a"}, + {file = "pycryptodome-3.23.0-cp37-abi3-musllinux_1_2_i686.whl", hash = "sha256:156df9667ad9f2ad26255926524e1c136d6664b741547deb0a86a9acf5ea631f"}, + {file = "pycryptodome-3.23.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:dea827b4d55ee390dc89b2afe5927d4308a8b538ae91d9c6f7a5090f397af1aa"}, + {file = "pycryptodome-3.23.0-cp37-abi3-win32.whl", hash = "sha256:507dbead45474b62b2bbe318eb1c4c8ee641077532067fec9c1aa82c31f84886"}, + {file = "pycryptodome-3.23.0-cp37-abi3-win_amd64.whl", hash = "sha256:c75b52aacc6c0c260f204cbdd834f76edc9fb0d8e0da9fbf8352ef58202564e2"}, + {file = "pycryptodome-3.23.0-cp37-abi3-win_arm64.whl", hash = "sha256:11eeeb6917903876f134b56ba11abe95c0b0fd5e3330def218083c7d98bbcb3c"}, + {file = "pycryptodome-3.23.0-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:350ebc1eba1da729b35ab7627a833a1a355ee4e852d8ba0447fafe7b14504d56"}, + {file = "pycryptodome-3.23.0-pp27-pypy_73-win32.whl", hash = "sha256:93837e379a3e5fd2bb00302a47aee9fdf7940d83595be3915752c74033d17ca7"}, + {file = "pycryptodome-3.23.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ddb95b49df036ddd264a0ad246d1be5b672000f12d6961ea2c267083a5e19379"}, + {file = "pycryptodome-3.23.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e95564beb8782abfd9e431c974e14563a794a4944c29d6d3b7b5ea042110b4"}, + {file = "pycryptodome-3.23.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14e15c081e912c4b0d75632acd8382dfce45b258667aa3c67caf7a4d4c13f630"}, + {file = "pycryptodome-3.23.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7fc76bf273353dc7e5207d172b83f569540fc9a28d63171061c42e361d22353"}, + {file = "pycryptodome-3.23.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:45c69ad715ca1a94f778215a11e66b7ff989d792a4d63b68dc586a1da1392ff5"}, + {file = "pycryptodome-3.23.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:865d83c906b0fc6a59b510deceee656b6bc1c4fa0d82176e2b77e97a420a996a"}, + {file = "pycryptodome-3.23.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89d4d56153efc4d81defe8b65fd0821ef8b2d5ddf8ed19df31ba2f00872b8002"}, + {file = "pycryptodome-3.23.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3f2d0aaf8080bda0587d58fc9fe4766e012441e2eed4269a77de6aea981c8be"}, + {file = "pycryptodome-3.23.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:64093fc334c1eccfd3933c134c4457c34eaca235eeae49d69449dc4728079339"}, + {file = "pycryptodome-3.23.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:ce64e84a962b63a47a592690bdc16a7eaf709d2c2697ababf24a0def566899a6"}, + {file = "pycryptodome-3.23.0.tar.gz", hash = "sha256:447700a657182d60338bab09fdb27518f8856aecd80ae4c6bdddb67ff5da44ef"}, ] [[package]] name = "pydantic" -version = "2.10.6" +version = "2.12.0" description = "Data validation using Python type hints" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["main"] files = [ - {file = "pydantic-2.10.6-py3-none-any.whl", hash = "sha256:427d664bf0b8a2b34ff5dd0f5a18df00591adcee7198fbd71981054cef37b584"}, - {file = "pydantic-2.10.6.tar.gz", hash = "sha256:ca5daa827cce33de7a42be142548b0096bf05a7e7b365aebfa5f8eeec7128236"}, + {file = "pydantic-2.12.0-py3-none-any.whl", hash = "sha256:f6a1da352d42790537e95e83a8bdfb91c7efbae63ffd0b86fa823899e807116f"}, + {file = "pydantic-2.12.0.tar.gz", hash = "sha256:c1a077e6270dbfb37bfd8b498b3981e2bb18f68103720e51fa6c306a5a9af563"}, ] [package.dependencies] annotated-types = ">=0.6.0" -pydantic-core = "2.27.2" -typing-extensions = ">=4.12.2" +pydantic-core = "2.41.1" +typing-extensions = ">=4.14.1" +typing-inspection = ">=0.4.2" [package.extras] email = ["email-validator (>=2.0.0)"] @@ -1313,116 +1611,144 @@ timezone = ["tzdata"] [[package]] name = "pydantic-core" -version = "2.27.2" +version = "2.41.1" description = "Core functionality for Pydantic validation and serialization" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["main"] files = [ - {file = "pydantic_core-2.27.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2d367ca20b2f14095a8f4fa1210f5a7b78b8a20009ecced6b12818f455b1e9fa"}, - {file = "pydantic_core-2.27.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:491a2b73db93fab69731eaee494f320faa4e093dbed776be1a829c2eb222c34c"}, - {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7969e133a6f183be60e9f6f56bfae753585680f3b7307a8e555a948d443cc05a"}, - {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3de9961f2a346257caf0aa508a4da705467f53778e9ef6fe744c038119737ef5"}, - {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e2bb4d3e5873c37bb3dd58714d4cd0b0e6238cebc4177ac8fe878f8b3aa8e74c"}, - {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:280d219beebb0752699480fe8f1dc61ab6615c2046d76b7ab7ee38858de0a4e7"}, - {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47956ae78b6422cbd46f772f1746799cbb862de838fd8d1fbd34a82e05b0983a"}, - {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:14d4a5c49d2f009d62a2a7140d3064f686d17a5d1a268bc641954ba181880236"}, - {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:337b443af21d488716f8d0b6164de833e788aa6bd7e3a39c005febc1284f4962"}, - {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:03d0f86ea3184a12f41a2d23f7ccb79cdb5a18e06993f8a45baa8dfec746f0e9"}, - {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7041c36f5680c6e0f08d922aed302e98b3745d97fe1589db0a3eebf6624523af"}, - {file = "pydantic_core-2.27.2-cp310-cp310-win32.whl", hash = "sha256:50a68f3e3819077be2c98110c1f9dcb3817e93f267ba80a2c05bb4f8799e2ff4"}, - {file = "pydantic_core-2.27.2-cp310-cp310-win_amd64.whl", hash = "sha256:e0fd26b16394ead34a424eecf8a31a1f5137094cabe84a1bcb10fa6ba39d3d31"}, - {file = "pydantic_core-2.27.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:8e10c99ef58cfdf2a66fc15d66b16c4a04f62bca39db589ae8cba08bc55331bc"}, - {file = "pydantic_core-2.27.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:26f32e0adf166a84d0cb63be85c562ca8a6fa8de28e5f0d92250c6b7e9e2aff7"}, - {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c19d1ea0673cd13cc2f872f6c9ab42acc4e4f492a7ca9d3795ce2b112dd7e15"}, - {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5e68c4446fe0810e959cdff46ab0a41ce2f2c86d227d96dc3847af0ba7def306"}, - {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d9640b0059ff4f14d1f37321b94061c6db164fbe49b334b31643e0528d100d99"}, - {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:40d02e7d45c9f8af700f3452f329ead92da4c5f4317ca9b896de7ce7199ea459"}, - {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c1fd185014191700554795c99b347d64f2bb637966c4cfc16998a0ca700d048"}, - {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d81d2068e1c1228a565af076598f9e7451712700b673de8f502f0334f281387d"}, - {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1a4207639fb02ec2dbb76227d7c751a20b1a6b4bc52850568e52260cae64ca3b"}, - {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:3de3ce3c9ddc8bbd88f6e0e304dea0e66d843ec9de1b0042b0911c1663ffd474"}, - {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:30c5f68ded0c36466acede341551106821043e9afaad516adfb6e8fa80a4e6a6"}, - {file = "pydantic_core-2.27.2-cp311-cp311-win32.whl", hash = "sha256:c70c26d2c99f78b125a3459f8afe1aed4d9687c24fd677c6a4436bc042e50d6c"}, - {file = "pydantic_core-2.27.2-cp311-cp311-win_amd64.whl", hash = "sha256:08e125dbdc505fa69ca7d9c499639ab6407cfa909214d500897d02afb816e7cc"}, - {file = "pydantic_core-2.27.2-cp311-cp311-win_arm64.whl", hash = "sha256:26f0d68d4b235a2bae0c3fc585c585b4ecc51382db0e3ba402a22cbc440915e4"}, - {file = "pydantic_core-2.27.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9e0c8cfefa0ef83b4da9588448b6d8d2a2bf1a53c3f1ae5fca39eb3061e2f0b0"}, - {file = "pydantic_core-2.27.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:83097677b8e3bd7eaa6775720ec8e0405f1575015a463285a92bfdfe254529ef"}, - {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:172fce187655fece0c90d90a678424b013f8fbb0ca8b036ac266749c09438cb7"}, - {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:519f29f5213271eeeeb3093f662ba2fd512b91c5f188f3bb7b27bc5973816934"}, - {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05e3a55d124407fffba0dd6b0c0cd056d10e983ceb4e5dbd10dda135c31071d6"}, - {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c3ed807c7b91de05e63930188f19e921d1fe90de6b4f5cd43ee7fcc3525cb8c"}, - {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fb4aadc0b9a0c063206846d603b92030eb6f03069151a625667f982887153e2"}, - {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:28ccb213807e037460326424ceb8b5245acb88f32f3d2777427476e1b32c48c4"}, - {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:de3cd1899e2c279b140adde9357c4495ed9d47131b4a4eaff9052f23398076b3"}, - {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:220f892729375e2d736b97d0e51466252ad84c51857d4d15f5e9692f9ef12be4"}, - {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a0fcd29cd6b4e74fe8ddd2c90330fd8edf2e30cb52acda47f06dd615ae72da57"}, - {file = "pydantic_core-2.27.2-cp312-cp312-win32.whl", hash = "sha256:1e2cb691ed9834cd6a8be61228471d0a503731abfb42f82458ff27be7b2186fc"}, - {file = "pydantic_core-2.27.2-cp312-cp312-win_amd64.whl", hash = "sha256:cc3f1a99a4f4f9dd1de4fe0312c114e740b5ddead65bb4102884b384c15d8bc9"}, - {file = "pydantic_core-2.27.2-cp312-cp312-win_arm64.whl", hash = "sha256:3911ac9284cd8a1792d3cb26a2da18f3ca26c6908cc434a18f730dc0db7bfa3b"}, - {file = "pydantic_core-2.27.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7d14bd329640e63852364c306f4d23eb744e0f8193148d4044dd3dacdaacbd8b"}, - {file = "pydantic_core-2.27.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:82f91663004eb8ed30ff478d77c4d1179b3563df6cdb15c0817cd1cdaf34d154"}, - {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71b24c7d61131bb83df10cc7e687433609963a944ccf45190cfc21e0887b08c9"}, - {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fa8e459d4954f608fa26116118bb67f56b93b209c39b008277ace29937453dc9"}, - {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce8918cbebc8da707ba805b7fd0b382816858728ae7fe19a942080c24e5b7cd1"}, - {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eda3f5c2a021bbc5d976107bb302e0131351c2ba54343f8a496dc8783d3d3a6a"}, - {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd8086fa684c4775c27f03f062cbb9eaa6e17f064307e86b21b9e0abc9c0f02e"}, - {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8d9b3388db186ba0c099a6d20f0604a44eabdeef1777ddd94786cdae158729e4"}, - {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7a66efda2387de898c8f38c0cf7f14fca0b51a8ef0b24bfea5849f1b3c95af27"}, - {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:18a101c168e4e092ab40dbc2503bdc0f62010e95d292b27827871dc85450d7ee"}, - {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ba5dd002f88b78a4215ed2f8ddbdf85e8513382820ba15ad5ad8955ce0ca19a1"}, - {file = "pydantic_core-2.27.2-cp313-cp313-win32.whl", hash = "sha256:1ebaf1d0481914d004a573394f4be3a7616334be70261007e47c2a6fe7e50130"}, - {file = "pydantic_core-2.27.2-cp313-cp313-win_amd64.whl", hash = "sha256:953101387ecf2f5652883208769a79e48db18c6df442568a0b5ccd8c2723abee"}, - {file = "pydantic_core-2.27.2-cp313-cp313-win_arm64.whl", hash = "sha256:ac4dbfd1691affb8f48c2c13241a2e3b60ff23247cbcf981759c768b6633cf8b"}, - {file = "pydantic_core-2.27.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d3e8d504bdd3f10835468f29008d72fc8359d95c9c415ce6e767203db6127506"}, - {file = "pydantic_core-2.27.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:521eb9b7f036c9b6187f0b47318ab0d7ca14bd87f776240b90b21c1f4f149320"}, - {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85210c4d99a0114f5a9481b44560d7d1e35e32cc5634c656bc48e590b669b145"}, - {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d716e2e30c6f140d7560ef1538953a5cd1a87264c737643d481f2779fc247fe1"}, - {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f66d89ba397d92f840f8654756196d93804278457b5fbede59598a1f9f90b228"}, - {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:669e193c1c576a58f132e3158f9dfa9662969edb1a250c54d8fa52590045f046"}, - {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdbe7629b996647b99c01b37f11170a57ae675375b14b8c13b8518b8320ced5"}, - {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d262606bf386a5ba0b0af3b97f37c83d7011439e3dc1a9298f21efb292e42f1a"}, - {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:cabb9bcb7e0d97f74df8646f34fc76fbf793b7f6dc2438517d7a9e50eee4f14d"}, - {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_armv7l.whl", hash = "sha256:d2d63f1215638d28221f664596b1ccb3944f6e25dd18cd3b86b0a4c408d5ebb9"}, - {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bca101c00bff0adb45a833f8451b9105d9df18accb8743b08107d7ada14bd7da"}, - {file = "pydantic_core-2.27.2-cp38-cp38-win32.whl", hash = "sha256:f6f8e111843bbb0dee4cb6594cdc73e79b3329b526037ec242a3e49012495b3b"}, - {file = "pydantic_core-2.27.2-cp38-cp38-win_amd64.whl", hash = "sha256:fd1aea04935a508f62e0d0ef1f5ae968774a32afc306fb8545e06f5ff5cdf3ad"}, - {file = "pydantic_core-2.27.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:c10eb4f1659290b523af58fa7cffb452a61ad6ae5613404519aee4bfbf1df993"}, - {file = "pydantic_core-2.27.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ef592d4bad47296fb11f96cd7dc898b92e795032b4894dfb4076cfccd43a9308"}, - {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c61709a844acc6bf0b7dce7daae75195a10aac96a596ea1b776996414791ede4"}, - {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42c5f762659e47fdb7b16956c71598292f60a03aa92f8b6351504359dbdba6cf"}, - {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4c9775e339e42e79ec99c441d9730fccf07414af63eac2f0e48e08fd38a64d76"}, - {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:57762139821c31847cfb2df63c12f725788bd9f04bc2fb392790959b8f70f118"}, - {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d1e85068e818c73e048fe28cfc769040bb1f475524f4745a5dc621f75ac7630"}, - {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:097830ed52fd9e427942ff3b9bc17fab52913b2f50f2880dc4a5611446606a54"}, - {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:044a50963a614ecfae59bb1eaf7ea7efc4bc62f49ed594e18fa1e5d953c40e9f"}, - {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:4e0b4220ba5b40d727c7f879eac379b822eee5d8fff418e9d3381ee45b3b0362"}, - {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5e4f4bb20d75e9325cc9696c6802657b58bc1dbbe3022f32cc2b2b632c3fbb96"}, - {file = "pydantic_core-2.27.2-cp39-cp39-win32.whl", hash = "sha256:cca63613e90d001b9f2f9a9ceb276c308bfa2a43fafb75c8031c4f66039e8c6e"}, - {file = "pydantic_core-2.27.2-cp39-cp39-win_amd64.whl", hash = "sha256:77d1bca19b0f7021b3a982e6f903dcd5b2b06076def36a652e3907f596e29f67"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2bf14caea37e91198329b828eae1618c068dfb8ef17bb33287a7ad4b61ac314e"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:b0cb791f5b45307caae8810c2023a184c74605ec3bcbb67d13846c28ff731ff8"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:688d3fd9fcb71f41c4c015c023d12a79d1c4c0732ec9eb35d96e3388a120dcf3"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d591580c34f4d731592f0e9fe40f9cc1b430d297eecc70b962e93c5c668f15f"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:82f986faf4e644ffc189a7f1aafc86e46ef70372bb153e7001e8afccc6e54133"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:bec317a27290e2537f922639cafd54990551725fc844249e64c523301d0822fc"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:0296abcb83a797db256b773f45773da397da75a08f5fcaef41f2044adec05f50"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:0d75070718e369e452075a6017fbf187f788e17ed67a3abd47fa934d001863d9"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:7e17b560be3c98a8e3aa66ce828bdebb9e9ac6ad5466fba92eb74c4c95cb1151"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c33939a82924da9ed65dab5a65d427205a73181d8098e79b6b426bdf8ad4e656"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:00bad2484fa6bda1e216e7345a798bd37c68fb2d97558edd584942aa41b7d278"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c817e2b40aba42bac6f457498dacabc568c3b7a986fc9ba7c8d9d260b71485fb"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:251136cdad0cb722e93732cb45ca5299fb56e1344a833640bf93b2803f8d1bfd"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d2088237af596f0a524d3afc39ab3b036e8adb054ee57cbb1dcf8e09da5b29cc"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d4041c0b966a84b4ae7a09832eb691a35aec90910cd2dbe7a208de59be77965b"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:8083d4e875ebe0b864ffef72a4304827015cff328a1be6e22cc850753bfb122b"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f141ee28a0ad2123b6611b6ceff018039df17f32ada8b534e6aa039545a3efb2"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7d0c8399fcc1848491f00e0314bd59fb34a9c008761bcb422a057670c3f65e35"}, - {file = "pydantic_core-2.27.2.tar.gz", hash = "sha256:eb026e5a4c1fee05726072337ff51d1efb6f59090b7da90d30ea58625b1ffb39"}, + {file = "pydantic_core-2.41.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:e63036298322e9aea1c8b7c0a6c1204d615dbf6ec0668ce5b83ff27f07404a61"}, + {file = "pydantic_core-2.41.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:241299ca91fc77ef64f11ed909d2d9220a01834e8e6f8de61275c4dd16b7c936"}, + {file = "pydantic_core-2.41.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ab7e594a2a5c24ab8013a7dc8cfe5f2260e80e490685814122081705c2cf2b0"}, + {file = "pydantic_core-2.41.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b054ef1a78519cb934b58e9c90c09e93b837c935dcd907b891f2b265b129eb6e"}, + {file = "pydantic_core-2.41.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f2ab7d10d0ab2ed6da54c757233eb0f48ebfb4f86e9b88ccecb3f92bbd61a538"}, + {file = "pydantic_core-2.41.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2757606b7948bb853a27e4040820306eaa0ccb9e8f9f8a0fa40cb674e170f350"}, + {file = "pydantic_core-2.41.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cec0e75eb61f606bad0a32f2be87507087514e26e8c73db6cbdb8371ccd27917"}, + {file = "pydantic_core-2.41.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0234236514f44a5bf552105cfe2543a12f48203397d9d0f866affa569345a5b5"}, + {file = "pydantic_core-2.41.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:1b974e41adfbb4ebb0f65fc4ca951347b17463d60893ba7d5f7b9bb087c83897"}, + {file = "pydantic_core-2.41.1-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:248dafb3204136113c383e91a4d815269f51562b6659b756cf3df14eefc7d0bb"}, + {file = "pydantic_core-2.41.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:678f9d76a91d6bcedd7568bbf6beb77ae8447f85d1aeebaab7e2f0829cfc3a13"}, + {file = "pydantic_core-2.41.1-cp310-cp310-win32.whl", hash = "sha256:dff5bee1d21ee58277900692a641925d2dddfde65182c972569b1a276d2ac8fb"}, + {file = "pydantic_core-2.41.1-cp310-cp310-win_amd64.whl", hash = "sha256:5042da12e5d97d215f91567110fdfa2e2595a25f17c19b9ff024f31c34f9b53e"}, + {file = "pydantic_core-2.41.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4f276a6134fe1fc1daa692642a3eaa2b7b858599c49a7610816388f5e37566a1"}, + {file = "pydantic_core-2.41.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:07588570a805296ece009c59d9a679dc08fab72fb337365afb4f3a14cfbfc176"}, + {file = "pydantic_core-2.41.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28527e4b53400cd60ffbd9812ccb2b5135d042129716d71afd7e45bf42b855c0"}, + {file = "pydantic_core-2.41.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:46a1c935c9228bad738c8a41de06478770927baedf581d172494ab36a6b96575"}, + {file = "pydantic_core-2.41.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:447ddf56e2b7d28d200d3e9eafa936fe40485744b5a824b67039937580b3cb20"}, + {file = "pydantic_core-2.41.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:63892ead40c1160ac860b5debcc95c95c5a0035e543a8b5a4eac70dd22e995f4"}, + {file = "pydantic_core-2.41.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f4a9543ca355e6df8fbe9c83e9faab707701e9103ae857ecb40f1c0cf8b0e94d"}, + {file = "pydantic_core-2.41.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f2611bdb694116c31e551ed82e20e39a90bea9b7ad9e54aaf2d045ad621aa7a1"}, + {file = "pydantic_core-2.41.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fecc130893a9b5f7bfe230be1bb8c61fe66a19db8ab704f808cb25a82aad0bc9"}, + {file = "pydantic_core-2.41.1-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:1e2df5f8344c99b6ea5219f00fdc8950b8e6f2c422fbc1cc122ec8641fac85a1"}, + {file = "pydantic_core-2.41.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:35291331e9d8ed94c257bab6be1cb3a380b5eee570a2784bffc055e18040a2ea"}, + {file = "pydantic_core-2.41.1-cp311-cp311-win32.whl", hash = "sha256:2876a095292668d753f1a868c4a57c4ac9f6acbd8edda8debe4218d5848cf42f"}, + {file = "pydantic_core-2.41.1-cp311-cp311-win_amd64.whl", hash = "sha256:b92d6c628e9a338846a28dfe3fcdc1a3279388624597898b105e078cdfc59298"}, + {file = "pydantic_core-2.41.1-cp311-cp311-win_arm64.whl", hash = "sha256:7d82ae99409eb69d507a89835488fb657faa03ff9968a9379567b0d2e2e56bc5"}, + {file = "pydantic_core-2.41.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:db2f82c0ccbce8f021ad304ce35cbe02aa2f95f215cac388eed542b03b4d5eb4"}, + {file = "pydantic_core-2.41.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:47694a31c710ced9205d5f1e7e8af3ca57cbb8a503d98cb9e33e27c97a501601"}, + {file = "pydantic_core-2.41.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:93e9decce94daf47baf9e9d392f5f2557e783085f7c5e522011545d9d6858e00"}, + {file = "pydantic_core-2.41.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ab0adafdf2b89c8b84f847780a119437a0931eca469f7b44d356f2b426dd9741"}, + {file = "pydantic_core-2.41.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5da98cc81873f39fd56882e1569c4677940fbc12bce6213fad1ead784192d7c8"}, + {file = "pydantic_core-2.41.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:209910e88afb01fd0fd403947b809ba8dba0e08a095e1f703294fda0a8fdca51"}, + {file = "pydantic_core-2.41.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:365109d1165d78d98e33c5bfd815a9b5d7d070f578caefaabcc5771825b4ecb5"}, + {file = "pydantic_core-2.41.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:706abf21e60a2857acdb09502bc853ee5bce732955e7b723b10311114f033115"}, + {file = "pydantic_core-2.41.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:bf0bd5417acf7f6a7ec3b53f2109f587be176cb35f9cf016da87e6017437a72d"}, + {file = "pydantic_core-2.41.1-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:2e71b1c6ceb9c78424ae9f63a07292fb769fb890a4e7efca5554c47f33a60ea5"}, + {file = "pydantic_core-2.41.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:80745b9770b4a38c25015b517451c817799bfb9d6499b0d13d8227ec941cb513"}, + {file = "pydantic_core-2.41.1-cp312-cp312-win32.whl", hash = "sha256:83b64d70520e7890453f1aa21d66fda44e7b35f1cfea95adf7b4289a51e2b479"}, + {file = "pydantic_core-2.41.1-cp312-cp312-win_amd64.whl", hash = "sha256:377defd66ee2003748ee93c52bcef2d14fde48fe28a0b156f88c3dbf9bc49a50"}, + {file = "pydantic_core-2.41.1-cp312-cp312-win_arm64.whl", hash = "sha256:c95caff279d49c1d6cdfe2996e6c2ad712571d3b9caaa209a404426c326c4bde"}, + {file = "pydantic_core-2.41.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:70e790fce5f05204ef4403159857bfcd587779da78627b0babb3654f75361ebf"}, + {file = "pydantic_core-2.41.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9cebf1ca35f10930612d60bd0f78adfacee824c30a880e3534ba02c207cceceb"}, + {file = "pydantic_core-2.41.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:170406a37a5bc82c22c3274616bf6f17cc7df9c4a0a0a50449e559cb755db669"}, + {file = "pydantic_core-2.41.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:12d4257fc9187a0ccd41b8b327d6a4e57281ab75e11dda66a9148ef2e1fb712f"}, + {file = "pydantic_core-2.41.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a75a33b4db105dd1c8d57839e17ee12db8d5ad18209e792fa325dbb4baeb00f4"}, + {file = "pydantic_core-2.41.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:08a589f850803a74e0fcb16a72081cafb0d72a3cdda500106942b07e76b7bf62"}, + {file = "pydantic_core-2.41.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a97939d6ea44763c456bd8a617ceada2c9b96bb5b8ab3dfa0d0827df7619014"}, + {file = "pydantic_core-2.41.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d2ae423c65c556f09569524b80ffd11babff61f33055ef9773d7c9fabc11ed8d"}, + {file = "pydantic_core-2.41.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:4dc703015fbf8764d6a8001c327a87f1823b7328d40b47ce6000c65918ad2b4f"}, + {file = "pydantic_core-2.41.1-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:968e4ffdfd35698a5fe659e5e44c508b53664870a8e61c8f9d24d3d145d30257"}, + {file = "pydantic_core-2.41.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:fff2b76c8e172d34771cd4d4f0ade08072385310f214f823b5a6ad4006890d32"}, + {file = "pydantic_core-2.41.1-cp313-cp313-win32.whl", hash = "sha256:a38a5263185407ceb599f2f035faf4589d57e73c7146d64f10577f6449e8171d"}, + {file = "pydantic_core-2.41.1-cp313-cp313-win_amd64.whl", hash = "sha256:b42ae7fd6760782c975897e1fdc810f483b021b32245b0105d40f6e7a3803e4b"}, + {file = "pydantic_core-2.41.1-cp313-cp313-win_arm64.whl", hash = "sha256:ad4111acc63b7384e205c27a2f15e23ac0ee21a9d77ad6f2e9cb516ec90965fb"}, + {file = "pydantic_core-2.41.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:440d0df7415b50084a4ba9d870480c16c5f67c0d1d4d5119e3f70925533a0edc"}, + {file = "pydantic_core-2.41.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:71eaa38d342099405dae6484216dcf1e8e4b0bebd9b44a4e08c9b43db6a2ab67"}, + {file = "pydantic_core-2.41.1-cp313-cp313t-win_amd64.whl", hash = "sha256:555ecf7e50f1161d3f693bc49f23c82cf6cdeafc71fa37a06120772a09a38795"}, + {file = "pydantic_core-2.41.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:05226894a26f6f27e1deb735d7308f74ef5fa3a6de3e0135bb66cdcaee88f64b"}, + {file = "pydantic_core-2.41.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:85ff7911c6c3e2fd8d3779c50925f6406d770ea58ea6dde9c230d35b52b16b4a"}, + {file = "pydantic_core-2.41.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47f1f642a205687d59b52dc1a9a607f45e588f5a2e9eeae05edd80c7a8c47674"}, + {file = "pydantic_core-2.41.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:df11c24e138876ace5ec6043e5cae925e34cf38af1a1b3d63589e8f7b5f5cdc4"}, + {file = "pydantic_core-2.41.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7f0bf7f5c8f7bf345c527e8a0d72d6b26eda99c1227b0c34e7e59e181260de31"}, + {file = "pydantic_core-2.41.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:82b887a711d341c2c47352375d73b029418f55b20bd7815446d175a70effa706"}, + {file = "pydantic_core-2.41.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b5f1d5d6bbba484bdf220c72d8ecd0be460f4bd4c5e534a541bb2cd57589fb8b"}, + {file = "pydantic_core-2.41.1-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2bf1917385ebe0f968dc5c6ab1375886d56992b93ddfe6bf52bff575d03662be"}, + {file = "pydantic_core-2.41.1-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:4f94f3ab188f44b9a73f7295663f3ecb8f2e2dd03a69c8f2ead50d37785ecb04"}, + {file = "pydantic_core-2.41.1-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:3925446673641d37c30bd84a9d597e49f72eacee8b43322c8999fa17d5ae5bc4"}, + {file = "pydantic_core-2.41.1-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:49bd51cc27adb980c7b97357ae036ce9b3c4d0bb406e84fbe16fb2d368b602a8"}, + {file = "pydantic_core-2.41.1-cp314-cp314-win32.whl", hash = "sha256:a31ca0cd0e4d12ea0df0077df2d487fc3eb9d7f96bbb13c3c5b88dcc21d05159"}, + {file = "pydantic_core-2.41.1-cp314-cp314-win_amd64.whl", hash = "sha256:1b5c4374a152e10a22175d7790e644fbd8ff58418890e07e2073ff9d4414efae"}, + {file = "pydantic_core-2.41.1-cp314-cp314-win_arm64.whl", hash = "sha256:4fee76d757639b493eb600fba668f1e17475af34c17dd61db7a47e824d464ca9"}, + {file = "pydantic_core-2.41.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f9b9c968cfe5cd576fdd7361f47f27adeb120517e637d1b189eea1c3ece573f4"}, + {file = "pydantic_core-2.41.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1ebc7ab67b856384aba09ed74e3e977dded40e693de18a4f197c67d0d4e6d8e"}, + {file = "pydantic_core-2.41.1-cp314-cp314t-win_amd64.whl", hash = "sha256:8ae0dc57b62a762985bc7fbf636be3412394acc0ddb4ade07fe104230f1b9762"}, + {file = "pydantic_core-2.41.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:10ce489cf09a4956a1549af839b983edc59b0f60e1b068c21b10154e58f54f80"}, + {file = "pydantic_core-2.41.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ff548c908caffd9455fd1342366bcf8a1ec8a3fca42f35c7fc60883d6a901074"}, + {file = "pydantic_core-2.41.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d43bf082025082bda13be89a5f876cc2386b7727c7b322be2d2b706a45cea8e"}, + {file = "pydantic_core-2.41.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:666aee751faf1c6864b2db795775dd67b61fdcf646abefa309ed1da039a97209"}, + {file = "pydantic_core-2.41.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b83aaeff0d7bde852c32e856f3ee410842ebc08bc55c510771d87dcd1c01e1ed"}, + {file = "pydantic_core-2.41.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:055c7931b0329cb8acde20cdde6d9c2cbc2a02a0a8e54a792cddd91e2ea92c65"}, + {file = "pydantic_core-2.41.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:530bbb1347e3e5ca13a91ac087c4971d7da09630ef8febd27a20a10800c2d06d"}, + {file = "pydantic_core-2.41.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:65a0ea16cfea7bfa9e43604c8bd726e63a3788b61c384c37664b55209fcb1d74"}, + {file = "pydantic_core-2.41.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:8fa93fadff794c6d15c345c560513b160197342275c6d104cc879f932b978afc"}, + {file = "pydantic_core-2.41.1-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:c8a1af9ac51969a494c6a82b563abae6859dc082d3b999e8fa7ba5ee1b05e8e8"}, + {file = "pydantic_core-2.41.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:30edab28829703f876897c9471a857e43d847b8799c3c9e2fbce644724b50aa4"}, + {file = "pydantic_core-2.41.1-cp39-cp39-win32.whl", hash = "sha256:84d0ff869f98be2e93efdf1ae31e5a15f0926d22af8677d51676e373abbfe57a"}, + {file = "pydantic_core-2.41.1-cp39-cp39-win_amd64.whl", hash = "sha256:b5674314987cdde5a5511b029fa5fb1556b3d147a367e01dd583b19cfa8e35df"}, + {file = "pydantic_core-2.41.1-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:68f2251559b8efa99041bb63571ec7cdd2d715ba74cc82b3bc9eff824ebc8bf0"}, + {file = "pydantic_core-2.41.1-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:c7bc140c596097cb53b30546ca257dbe3f19282283190b1b5142928e5d5d3a20"}, + {file = "pydantic_core-2.41.1-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2896510fce8f4725ec518f8b9d7f015a00db249d2fd40788f442af303480063d"}, + {file = "pydantic_core-2.41.1-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ced20e62cfa0f496ba68fa5d6c7ee71114ea67e2a5da3114d6450d7f4683572a"}, + {file = "pydantic_core-2.41.1-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:b04fa9ed049461a7398138c604b00550bc89e3e1151d84b81ad6dc93e39c4c06"}, + {file = "pydantic_core-2.41.1-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:b3b7d9cfbfdc43c80a16638c6dc2768e3956e73031fca64e8e1a3ae744d1faeb"}, + {file = "pydantic_core-2.41.1-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eec83fc6abef04c7f9bec616e2d76ee9a6a4ae2a359b10c21d0f680e24a247ca"}, + {file = "pydantic_core-2.41.1-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6771a2d9f83c4038dfad5970a3eef215940682b2175e32bcc817bdc639019b28"}, + {file = "pydantic_core-2.41.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:fabcbdb12de6eada8d6e9a759097adb3c15440fafc675b3e94ae5c9cb8d678a0"}, + {file = "pydantic_core-2.41.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:80e97ccfaf0aaf67d55de5085b0ed0d994f57747d9d03f2de5cc9847ca737b08"}, + {file = "pydantic_core-2.41.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34df1fe8fea5d332484a763702e8b6a54048a9d4fe6ccf41e34a128238e01f52"}, + {file = "pydantic_core-2.41.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:421b5595f845842fc093f7250e24ee395f54ca62d494fdde96f43ecf9228ae01"}, + {file = "pydantic_core-2.41.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:dce8b22663c134583aaad24827863306a933f576c79da450be3984924e2031d1"}, + {file = "pydantic_core-2.41.1-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:300a9c162fea9906cc5c103893ca2602afd84f0ec90d3be36f4cc360125d22e1"}, + {file = "pydantic_core-2.41.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e019167628f6e6161ae7ab9fb70f6d076a0bf0d55aa9b20833f86a320c70dd65"}, + {file = "pydantic_core-2.41.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:13ab9cc2de6f9d4ab645a050ae5aee61a2424ac4d3a16ba23d4c2027705e0301"}, + {file = "pydantic_core-2.41.1-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:af2385d3f98243fb733862f806c5bb9122e5fba05b373e3af40e3c82d711cef1"}, + {file = "pydantic_core-2.41.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:6550617a0c2115be56f90c31a5370261d8ce9dbf051c3ed53b51172dd34da696"}, + {file = "pydantic_core-2.41.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc17b6ecf4983d298686014c92ebc955a9f9baf9f57dad4065e7906e7bee6222"}, + {file = "pydantic_core-2.41.1-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:42ae9352cf211f08b04ea110563d6b1e415878eea5b4c70f6bdb17dca3b932d2"}, + {file = "pydantic_core-2.41.1-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:e82947de92068b0a21681a13dd2102387197092fbe7defcfb8453e0913866506"}, + {file = "pydantic_core-2.41.1-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:e244c37d5471c9acdcd282890c6c4c83747b77238bfa19429b8473586c907656"}, + {file = "pydantic_core-2.41.1-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:1e798b4b304a995110d41ec93653e57975620ccb2842ba9420037985e7d7284e"}, + {file = "pydantic_core-2.41.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:f1fc716c0eb1663c59699b024428ad5ec2bcc6b928527b8fe28de6cb89f47efb"}, + {file = "pydantic_core-2.41.1.tar.gz", hash = "sha256:1ad375859a6d8c356b7704ec0f547a58e82ee80bb41baa811ad710e124bc8f2f"}, ] [package.dependencies] -typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" +typing-extensions = ">=4.14.1" + +[[package]] +name = "pygments" +version = "2.19.2" +description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=3.8" +groups = ["dev"] +files = [ + {file = "pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b"}, + {file = "pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887"}, +] + +[package.extras] +windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pypdf2" @@ -1443,6 +1769,48 @@ docs = ["myst_parser", "sphinx", "sphinx_rtd_theme"] full = ["Pillow", "PyCryptodome"] image = ["Pillow"] +[[package]] +name = "pytest" +version = "8.4.2" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79"}, + {file = "pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01"}, +] + +[package.dependencies] +colorama = {version = ">=0.4", markers = "sys_platform == \"win32\""} +iniconfig = ">=1" +packaging = ">=20" +pluggy = ">=1.5,<2" +pygments = ">=2.7.2" + +[package.extras] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "pytest-asyncio" +version = "1.2.0" +description = "Pytest support for asyncio" +optional = false +python-versions = ">=3.9" +groups = ["dev"] +files = [ + {file = "pytest_asyncio-1.2.0-py3-none-any.whl", hash = "sha256:8e17ae5e46d8e7efe51ab6494dd2010f4ca8dae51652aa3c8d55acf50bfb2e99"}, + {file = "pytest_asyncio-1.2.0.tar.gz", hash = "sha256:c609a64a2a8768462d0c99811ddb8bd2583c33fd33cf7f21af1c142e824ffb57"}, +] + +[package.dependencies] +pytest = ">=8.2,<9" +typing-extensions = {version = ">=4.12", markers = "python_version < \"3.13\""} + +[package.extras] +docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1)"] +testing = ["coverage (>=6.2)", "hypothesis (>=5.7.1)"] + [[package]] name = "python-dateutil" version = "2.9.0.post0" @@ -1460,19 +1828,19 @@ six = ">=1.5" [[package]] name = "requests" -version = "2.32.3" +version = "2.32.5" description = "Python HTTP for Humans." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["main"] files = [ - {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, - {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, + {file = "requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6"}, + {file = "requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf"}, ] [package.dependencies] certifi = ">=2017.4.17" -charset-normalizer = ">=2,<4" +charset_normalizer = ">=2,<4" idna = ">=2.5,<4" urllib3 = ">=1.21.1,<3" @@ -1482,30 +1850,30 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] name = "ruff" -version = "0.9.9" +version = "0.9.10" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" groups = ["dev"] files = [ - {file = "ruff-0.9.9-py3-none-linux_armv6l.whl", hash = "sha256:628abb5ea10345e53dff55b167595a159d3e174d6720bf19761f5e467e68d367"}, - {file = "ruff-0.9.9-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b6cd1428e834b35d7493354723543b28cc11dc14d1ce19b685f6e68e07c05ec7"}, - {file = "ruff-0.9.9-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5ee162652869120ad260670706f3cd36cd3f32b0c651f02b6da142652c54941d"}, - {file = "ruff-0.9.9-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3aa0f6b75082c9be1ec5a1db78c6d4b02e2375c3068438241dc19c7c306cc61a"}, - {file = "ruff-0.9.9-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:584cc66e89fb5f80f84b05133dd677a17cdd86901d6479712c96597a3f28e7fe"}, - {file = "ruff-0.9.9-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abf3369325761a35aba75cd5c55ba1b5eb17d772f12ab168fbfac54be85cf18c"}, - {file = "ruff-0.9.9-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:3403a53a32a90ce929aa2f758542aca9234befa133e29f4933dcef28a24317be"}, - {file = "ruff-0.9.9-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:18454e7fa4e4d72cffe28a37cf6a73cb2594f81ec9f4eca31a0aaa9ccdfb1590"}, - {file = "ruff-0.9.9-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fadfe2c88724c9617339f62319ed40dcdadadf2888d5afb88bf3adee7b35bfb"}, - {file = "ruff-0.9.9-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6df104d08c442a1aabcfd254279b8cc1e2cbf41a605aa3e26610ba1ec4acf0b0"}, - {file = "ruff-0.9.9-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:d7c62939daf5b2a15af48abbd23bea1efdd38c312d6e7c4cedf5a24e03207e17"}, - {file = "ruff-0.9.9-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:9494ba82a37a4b81b6a798076e4a3251c13243fc37967e998efe4cce58c8a8d1"}, - {file = "ruff-0.9.9-py3-none-musllinux_1_2_i686.whl", hash = "sha256:4efd7a96ed6d36ef011ae798bf794c5501a514be369296c672dab7921087fa57"}, - {file = "ruff-0.9.9-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:ab90a7944c5a1296f3ecb08d1cbf8c2da34c7e68114b1271a431a3ad30cb660e"}, - {file = "ruff-0.9.9-py3-none-win32.whl", hash = "sha256:6b4c376d929c25ecd6d87e182a230fa4377b8e5125a4ff52d506ee8c087153c1"}, - {file = "ruff-0.9.9-py3-none-win_amd64.whl", hash = "sha256:837982ea24091d4c1700ddb2f63b7070e5baec508e43b01de013dc7eff974ff1"}, - {file = "ruff-0.9.9-py3-none-win_arm64.whl", hash = "sha256:3ac78f127517209fe6d96ab00f3ba97cafe38718b23b1db3e96d8b2d39e37ddf"}, - {file = "ruff-0.9.9.tar.gz", hash = "sha256:0062ed13f22173e85f8f7056f9a24016e692efeea8704d1a5e8011b8aa850933"}, + {file = "ruff-0.9.10-py3-none-linux_armv6l.whl", hash = "sha256:eb4d25532cfd9fe461acc83498361ec2e2252795b4f40b17e80692814329e42d"}, + {file = "ruff-0.9.10-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:188a6638dab1aa9bb6228a7302387b2c9954e455fb25d6b4470cb0641d16759d"}, + {file = "ruff-0.9.10-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5284dcac6b9dbc2fcb71fdfc26a217b2ca4ede6ccd57476f52a587451ebe450d"}, + {file = "ruff-0.9.10-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47678f39fa2a3da62724851107f438c8229a3470f533894b5568a39b40029c0c"}, + {file = "ruff-0.9.10-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:99713a6e2766b7a17147b309e8c915b32b07a25c9efd12ada79f217c9c778b3e"}, + {file = "ruff-0.9.10-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:524ee184d92f7c7304aa568e2db20f50c32d1d0caa235d8ddf10497566ea1a12"}, + {file = "ruff-0.9.10-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:df92aeac30af821f9acf819fc01b4afc3dfb829d2782884f8739fb52a8119a16"}, + {file = "ruff-0.9.10-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de42e4edc296f520bb84954eb992a07a0ec5a02fecb834498415908469854a52"}, + {file = "ruff-0.9.10-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d257f95b65806104b6b1ffca0ea53f4ef98454036df65b1eda3693534813ecd1"}, + {file = "ruff-0.9.10-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b60dec7201c0b10d6d11be00e8f2dbb6f40ef1828ee75ed739923799513db24c"}, + {file = "ruff-0.9.10-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:d838b60007da7a39c046fcdd317293d10b845001f38bcb55ba766c3875b01e43"}, + {file = "ruff-0.9.10-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:ccaf903108b899beb8e09a63ffae5869057ab649c1e9231c05ae354ebc62066c"}, + {file = "ruff-0.9.10-py3-none-musllinux_1_2_i686.whl", hash = "sha256:f9567d135265d46e59d62dc60c0bfad10e9a6822e231f5b24032dba5a55be6b5"}, + {file = "ruff-0.9.10-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5f202f0d93738c28a89f8ed9eaba01b7be339e5d8d642c994347eaa81c6d75b8"}, + {file = "ruff-0.9.10-py3-none-win32.whl", hash = "sha256:bfb834e87c916521ce46b1788fbb8484966e5113c02df216680102e9eb960029"}, + {file = "ruff-0.9.10-py3-none-win_amd64.whl", hash = "sha256:f2160eeef3031bf4b17df74e307d4c5fb689a6f3a26a2de3f7ef4044e3c484f1"}, + {file = "ruff-0.9.10-py3-none-win_arm64.whl", hash = "sha256:5fd804c0327a5e5ea26615550e706942f348b197d5475ff34c19733aee4b2e69"}, + {file = "ruff-0.9.10.tar.gz", hash = "sha256:9bacb735d7bada9cfb0f2c227d3658fc443d90a727b47f206fb33f52f3c0eac7"}, ] [[package]] @@ -1534,14 +1902,14 @@ files = [ [[package]] name = "starlette" -version = "0.46.0" +version = "0.46.2" description = "The little ASGI library that shines." optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "starlette-0.46.0-py3-none-any.whl", hash = "sha256:913f0798bd90ba90a9156383bcf1350a17d6259451d0d8ee27fc0cf2db609038"}, - {file = "starlette-0.46.0.tar.gz", hash = "sha256:b359e4567456b28d473d0193f34c0de0ed49710d75ef183a74a5ce0499324f50"}, + {file = "starlette-0.46.2-py3-none-any.whl", hash = "sha256:595633ce89f8ffa71a015caed34a5b2dc1c0cdb3f0f1fbd1e69339cf2abeec35"}, + {file = "starlette-0.46.2.tar.gz", hash = "sha256:7f7361f34eed179294600af672f565727419830b54b7b084efe44bb82d2fccd5"}, ] [package.dependencies] @@ -1552,57 +1920,66 @@ full = ["httpx (>=0.27.0,<0.29.0)", "itsdangerous", "jinja2", "python-multipart [[package]] name = "structlog" -version = "25.1.0" +version = "25.4.0" description = "Structured Logging for Python" optional = false python-versions = ">=3.8" groups = ["main"] files = [ - {file = "structlog-25.1.0-py3-none-any.whl", hash = "sha256:843fe4f254540329f380812cbe612e1af5ec5b8172205ae634679cd35a6d6321"}, - {file = "structlog-25.1.0.tar.gz", hash = "sha256:2ef2a572e0e27f09664965d31a576afe64e46ac6084ef5cec3c2b8cd6e4e3ad3"}, + {file = "structlog-25.4.0-py3-none-any.whl", hash = "sha256:fe809ff5c27e557d14e613f45ca441aabda051d119ee5a0102aaba6ce40eed2c"}, + {file = "structlog-25.4.0.tar.gz", hash = "sha256:186cd1b0a8ae762e29417095664adf1d6a31702160a46dacb7796ea82f7409e4"}, ] -[package.extras] -dev = ["freezegun (>=0.2.8)", "mypy (>=1.4)", "pretend", "pytest (>=6.0)", "pytest-asyncio (>=0.17)", "rich", "simplejson", "twisted"] -docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-mermaid", "sphinxext-opengraph", "twisted"] -tests = ["freezegun (>=0.2.8)", "pretend", "pytest (>=6.0)", "pytest-asyncio (>=0.17)", "simplejson"] -typing = ["mypy (>=1.4)", "rich", "twisted"] - [[package]] name = "typing-extensions" -version = "4.12.2" -description = "Backported and Experimental Type Hints for Python 3.8+" +version = "4.15.0" +description = "Backported and Experimental Type Hints for Python 3.9+" optional = false -python-versions = ">=3.8" -groups = ["main"] +python-versions = ">=3.9" +groups = ["main", "dev"] files = [ - {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, - {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, + {file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"}, + {file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"}, ] +[[package]] +name = "typing-inspection" +version = "0.4.2" +description = "Runtime typing introspection tools" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7"}, + {file = "typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464"}, +] + +[package.dependencies] +typing-extensions = ">=4.12.0" + [[package]] name = "tzdata" -version = "2025.1" +version = "2025.2" description = "Provider of IANA time zone data" optional = false python-versions = ">=2" groups = ["main"] markers = "sys_platform == \"win32\"" files = [ - {file = "tzdata-2025.1-py2.py3-none-any.whl", hash = "sha256:7e127113816800496f027041c570f50bcd464a020098a3b6b199517772303639"}, - {file = "tzdata-2025.1.tar.gz", hash = "sha256:24894909e88cdb28bd1636c6887801df64cb485bd593f2fd83ef29075a81d694"}, + {file = "tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8"}, + {file = "tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9"}, ] [[package]] name = "urllib3" -version = "2.3.0" +version = "2.5.0" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df"}, - {file = "urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d"}, + {file = "urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc"}, + {file = "urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760"}, ] [package.extras] @@ -1613,14 +1990,14 @@ zstd = ["zstandard (>=0.18.0)"] [[package]] name = "uvicorn" -version = "0.34.0" +version = "0.34.3" description = "The lightning-fast ASGI server." optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "uvicorn-0.34.0-py3-none-any.whl", hash = "sha256:023dc038422502fa28a09c7a30bf2b6991512da7dcdb8fd35fe57cfc154126f4"}, - {file = "uvicorn-0.34.0.tar.gz", hash = "sha256:404051050cd7e905de2c9a7e61790943440b3416f49cb409f965d9dcd0fa73e9"}, + {file = "uvicorn-0.34.3-py3-none-any.whl", hash = "sha256:16246631db62bdfbf069b0645177d6e8a77ba950cfedbfd093acef9444e4d885"}, + {file = "uvicorn-0.34.3.tar.gz", hash = "sha256:35919a9a979d7a59334b6b10e05d77c1d0d574c50e0fc98b8b1a0f165708b55a"}, ] [package.dependencies] @@ -1628,106 +2005,154 @@ click = ">=7.0" h11 = ">=0.8" [package.extras] -standard = ["colorama (>=0.4)", "httptools (>=0.6.3)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"] +standard = ["colorama (>=0.4)", "httptools (>=0.6.3)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"] [[package]] name = "yarl" -version = "1.18.3" +version = "1.22.0" description = "Yet another URL library" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "yarl-1.18.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7df647e8edd71f000a5208fe6ff8c382a1de8edfbccdbbfe649d263de07d8c34"}, - {file = "yarl-1.18.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c69697d3adff5aa4f874b19c0e4ed65180ceed6318ec856ebc423aa5850d84f7"}, - {file = "yarl-1.18.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:602d98f2c2d929f8e697ed274fbadc09902c4025c5a9963bf4e9edfc3ab6f7ed"}, - {file = "yarl-1.18.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c654d5207c78e0bd6d749f6dae1dcbbfde3403ad3a4b11f3c5544d9906969dde"}, - {file = "yarl-1.18.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5094d9206c64181d0f6e76ebd8fb2f8fe274950a63890ee9e0ebfd58bf9d787b"}, - {file = "yarl-1.18.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:35098b24e0327fc4ebdc8ffe336cee0a87a700c24ffed13161af80124b7dc8e5"}, - {file = "yarl-1.18.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3236da9272872443f81fedc389bace88408f64f89f75d1bdb2256069a8730ccc"}, - {file = "yarl-1.18.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e2c08cc9b16f4f4bc522771d96734c7901e7ebef70c6c5c35dd0f10845270bcd"}, - {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:80316a8bd5109320d38eef8833ccf5f89608c9107d02d2a7f985f98ed6876990"}, - {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:c1e1cc06da1491e6734f0ea1e6294ce00792193c463350626571c287c9a704db"}, - {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fea09ca13323376a2fdfb353a5fa2e59f90cd18d7ca4eaa1fd31f0a8b4f91e62"}, - {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:e3b9fd71836999aad54084906f8663dffcd2a7fb5cdafd6c37713b2e72be1760"}, - {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:757e81cae69244257d125ff31663249b3013b5dc0a8520d73694aed497fb195b"}, - {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b1771de9944d875f1b98a745bc547e684b863abf8f8287da8466cf470ef52690"}, - {file = "yarl-1.18.3-cp310-cp310-win32.whl", hash = "sha256:8874027a53e3aea659a6d62751800cf6e63314c160fd607489ba5c2edd753cf6"}, - {file = "yarl-1.18.3-cp310-cp310-win_amd64.whl", hash = "sha256:93b2e109287f93db79210f86deb6b9bbb81ac32fc97236b16f7433db7fc437d8"}, - {file = "yarl-1.18.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8503ad47387b8ebd39cbbbdf0bf113e17330ffd339ba1144074da24c545f0069"}, - {file = "yarl-1.18.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:02ddb6756f8f4517a2d5e99d8b2f272488e18dd0bfbc802f31c16c6c20f22193"}, - {file = "yarl-1.18.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:67a283dd2882ac98cc6318384f565bffc751ab564605959df4752d42483ad889"}, - {file = "yarl-1.18.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d980e0325b6eddc81331d3f4551e2a333999fb176fd153e075c6d1c2530aa8a8"}, - {file = "yarl-1.18.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b643562c12680b01e17239be267bc306bbc6aac1f34f6444d1bded0c5ce438ca"}, - {file = "yarl-1.18.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c017a3b6df3a1bd45b9fa49a0f54005e53fbcad16633870104b66fa1a30a29d8"}, - {file = "yarl-1.18.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75674776d96d7b851b6498f17824ba17849d790a44d282929c42dbb77d4f17ae"}, - {file = "yarl-1.18.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ccaa3a4b521b780a7e771cc336a2dba389a0861592bbce09a476190bb0c8b4b3"}, - {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2d06d3005e668744e11ed80812e61efd77d70bb7f03e33c1598c301eea20efbb"}, - {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:9d41beda9dc97ca9ab0b9888cb71f7539124bc05df02c0cff6e5acc5a19dcc6e"}, - {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ba23302c0c61a9999784e73809427c9dbedd79f66a13d84ad1b1943802eaaf59"}, - {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:6748dbf9bfa5ba1afcc7556b71cda0d7ce5f24768043a02a58846e4a443d808d"}, - {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0b0cad37311123211dc91eadcb322ef4d4a66008d3e1bdc404808992260e1a0e"}, - {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0fb2171a4486bb075316ee754c6d8382ea6eb8b399d4ec62fde2b591f879778a"}, - {file = "yarl-1.18.3-cp311-cp311-win32.whl", hash = "sha256:61b1a825a13bef4a5f10b1885245377d3cd0bf87cba068e1d9a88c2ae36880e1"}, - {file = "yarl-1.18.3-cp311-cp311-win_amd64.whl", hash = "sha256:b9d60031cf568c627d028239693fd718025719c02c9f55df0a53e587aab951b5"}, - {file = "yarl-1.18.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1dd4bdd05407ced96fed3d7f25dbbf88d2ffb045a0db60dbc247f5b3c5c25d50"}, - {file = "yarl-1.18.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7c33dd1931a95e5d9a772d0ac5e44cac8957eaf58e3c8da8c1414de7dd27c576"}, - {file = "yarl-1.18.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:25b411eddcfd56a2f0cd6a384e9f4f7aa3efee14b188de13048c25b5e91f1640"}, - {file = "yarl-1.18.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:436c4fc0a4d66b2badc6c5fc5ef4e47bb10e4fd9bf0c79524ac719a01f3607c2"}, - {file = "yarl-1.18.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e35ef8683211db69ffe129a25d5634319a677570ab6b2eba4afa860f54eeaf75"}, - {file = "yarl-1.18.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:84b2deecba4a3f1a398df819151eb72d29bfeb3b69abb145a00ddc8d30094512"}, - {file = "yarl-1.18.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00e5a1fea0fd4f5bfa7440a47eff01d9822a65b4488f7cff83155a0f31a2ecba"}, - {file = "yarl-1.18.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d0e883008013c0e4aef84dcfe2a0b172c4d23c2669412cf5b3371003941f72bb"}, - {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5a3f356548e34a70b0172d8890006c37be92995f62d95a07b4a42e90fba54272"}, - {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ccd17349166b1bee6e529b4add61727d3f55edb7babbe4069b5764c9587a8cc6"}, - {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b958ddd075ddba5b09bb0be8a6d9906d2ce933aee81100db289badbeb966f54e"}, - {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c7d79f7d9aabd6011004e33b22bc13056a3e3fb54794d138af57f5ee9d9032cb"}, - {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:4891ed92157e5430874dad17b15eb1fda57627710756c27422200c52d8a4e393"}, - {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ce1af883b94304f493698b00d0f006d56aea98aeb49d75ec7d98cd4a777e9285"}, - {file = "yarl-1.18.3-cp312-cp312-win32.whl", hash = "sha256:f91c4803173928a25e1a55b943c81f55b8872f0018be83e3ad4938adffb77dd2"}, - {file = "yarl-1.18.3-cp312-cp312-win_amd64.whl", hash = "sha256:7e2ee16578af3b52ac2f334c3b1f92262f47e02cc6193c598502bd46f5cd1477"}, - {file = "yarl-1.18.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:90adb47ad432332d4f0bc28f83a5963f426ce9a1a8809f5e584e704b82685dcb"}, - {file = "yarl-1.18.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:913829534200eb0f789d45349e55203a091f45c37a2674678744ae52fae23efa"}, - {file = "yarl-1.18.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ef9f7768395923c3039055c14334ba4d926f3baf7b776c923c93d80195624782"}, - {file = "yarl-1.18.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88a19f62ff30117e706ebc9090b8ecc79aeb77d0b1f5ec10d2d27a12bc9f66d0"}, - {file = "yarl-1.18.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e17c9361d46a4d5addf777c6dd5eab0715a7684c2f11b88c67ac37edfba6c482"}, - {file = "yarl-1.18.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1a74a13a4c857a84a845505fd2d68e54826a2cd01935a96efb1e9d86c728e186"}, - {file = "yarl-1.18.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:41f7ce59d6ee7741af71d82020346af364949314ed3d87553763a2df1829cc58"}, - {file = "yarl-1.18.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f52a265001d830bc425f82ca9eabda94a64a4d753b07d623a9f2863fde532b53"}, - {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:82123d0c954dc58db301f5021a01854a85bf1f3bb7d12ae0c01afc414a882ca2"}, - {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:2ec9bbba33b2d00999af4631a3397d1fd78290c48e2a3e52d8dd72db3a067ac8"}, - {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:fbd6748e8ab9b41171bb95c6142faf068f5ef1511935a0aa07025438dd9a9bc1"}, - {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:877d209b6aebeb5b16c42cbb377f5f94d9e556626b1bfff66d7b0d115be88d0a"}, - {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b464c4ab4bfcb41e3bfd3f1c26600d038376c2de3297760dfe064d2cb7ea8e10"}, - {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8d39d351e7faf01483cc7ff7c0213c412e38e5a340238826be7e0e4da450fdc8"}, - {file = "yarl-1.18.3-cp313-cp313-win32.whl", hash = "sha256:61ee62ead9b68b9123ec24bc866cbef297dd266175d53296e2db5e7f797f902d"}, - {file = "yarl-1.18.3-cp313-cp313-win_amd64.whl", hash = "sha256:578e281c393af575879990861823ef19d66e2b1d0098414855dd367e234f5b3c"}, - {file = "yarl-1.18.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:61e5e68cb65ac8f547f6b5ef933f510134a6bf31bb178be428994b0cb46c2a04"}, - {file = "yarl-1.18.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fe57328fbc1bfd0bd0514470ac692630f3901c0ee39052ae47acd1d90a436719"}, - {file = "yarl-1.18.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a440a2a624683108a1b454705ecd7afc1c3438a08e890a1513d468671d90a04e"}, - {file = "yarl-1.18.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09c7907c8548bcd6ab860e5f513e727c53b4a714f459b084f6580b49fa1b9cee"}, - {file = "yarl-1.18.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b4f6450109834af88cb4cc5ecddfc5380ebb9c228695afc11915a0bf82116789"}, - {file = "yarl-1.18.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9ca04806f3be0ac6d558fffc2fdf8fcef767e0489d2684a21912cc4ed0cd1b8"}, - {file = "yarl-1.18.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77a6e85b90a7641d2e07184df5557132a337f136250caafc9ccaa4a2a998ca2c"}, - {file = "yarl-1.18.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6333c5a377c8e2f5fae35e7b8f145c617b02c939d04110c76f29ee3676b5f9a5"}, - {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0b3c92fa08759dbf12b3a59579a4096ba9af8dd344d9a813fc7f5070d86bbab1"}, - {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:4ac515b860c36becb81bb84b667466885096b5fc85596948548b667da3bf9f24"}, - {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:045b8482ce9483ada4f3f23b3774f4e1bf4f23a2d5c912ed5170f68efb053318"}, - {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:a4bb030cf46a434ec0225bddbebd4b89e6471814ca851abb8696170adb163985"}, - {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:54d6921f07555713b9300bee9c50fb46e57e2e639027089b1d795ecd9f7fa910"}, - {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1d407181cfa6e70077df3377938c08012d18893f9f20e92f7d2f314a437c30b1"}, - {file = "yarl-1.18.3-cp39-cp39-win32.whl", hash = "sha256:ac36703a585e0929b032fbaab0707b75dc12703766d0b53486eabd5139ebadd5"}, - {file = "yarl-1.18.3-cp39-cp39-win_amd64.whl", hash = "sha256:ba87babd629f8af77f557b61e49e7c7cac36f22f871156b91e10a6e9d4f829e9"}, - {file = "yarl-1.18.3-py3-none-any.whl", hash = "sha256:b57f4f58099328dfb26c6a771d09fb20dbbae81d20cfb66141251ea063bd101b"}, - {file = "yarl-1.18.3.tar.gz", hash = "sha256:ac1801c45cbf77b6c99242eeff4fffb5e4e73a800b5c4ad4fc0be5def634d2e1"}, + {file = "yarl-1.22.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c7bd6683587567e5a49ee6e336e0612bec8329be1b7d4c8af5687dcdeb67ee1e"}, + {file = "yarl-1.22.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5cdac20da754f3a723cceea5b3448e1a2074866406adeb4ef35b469d089adb8f"}, + {file = "yarl-1.22.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:07a524d84df0c10f41e3ee918846e1974aba4ec017f990dc735aad487a0bdfdf"}, + {file = "yarl-1.22.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e1b329cb8146d7b736677a2440e422eadd775d1806a81db2d4cded80a48efc1a"}, + {file = "yarl-1.22.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:75976c6945d85dbb9ee6308cd7ff7b1fb9409380c82d6119bd778d8fcfe2931c"}, + {file = "yarl-1.22.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:80ddf7a5f8c86cb3eb4bc9028b07bbbf1f08a96c5c0bc1244be5e8fefcb94147"}, + {file = "yarl-1.22.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d332fc2e3c94dad927f2112395772a4e4fedbcf8f80efc21ed7cdfae4d574fdb"}, + {file = "yarl-1.22.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0cf71bf877efeac18b38d3930594c0948c82b64547c1cf420ba48722fe5509f6"}, + {file = "yarl-1.22.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:663e1cadaddae26be034a6ab6072449a8426ddb03d500f43daf952b74553bba0"}, + {file = "yarl-1.22.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:6dcbb0829c671f305be48a7227918cfcd11276c2d637a8033a99a02b67bf9eda"}, + {file = "yarl-1.22.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f0d97c18dfd9a9af4490631905a3f131a8e4c9e80a39353919e2cfed8f00aedc"}, + {file = "yarl-1.22.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:437840083abe022c978470b942ff832c3940b2ad3734d424b7eaffcd07f76737"}, + {file = "yarl-1.22.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a899cbd98dce6f5d8de1aad31cb712ec0a530abc0a86bd6edaa47c1090138467"}, + {file = "yarl-1.22.0-cp310-cp310-win32.whl", hash = "sha256:595697f68bd1f0c1c159fcb97b661fc9c3f5db46498043555d04805430e79bea"}, + {file = "yarl-1.22.0-cp310-cp310-win_amd64.whl", hash = "sha256:cb95a9b1adaa48e41815a55ae740cfda005758104049a640a398120bf02515ca"}, + {file = "yarl-1.22.0-cp310-cp310-win_arm64.whl", hash = "sha256:b85b982afde6df99ecc996990d4ad7ccbdbb70e2a4ba4de0aecde5922ba98a0b"}, + {file = "yarl-1.22.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1ab72135b1f2db3fed3997d7e7dc1b80573c67138023852b6efb336a5eae6511"}, + {file = "yarl-1.22.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:669930400e375570189492dc8d8341301578e8493aec04aebc20d4717f899dd6"}, + {file = "yarl-1.22.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:792a2af6d58177ef7c19cbf0097aba92ca1b9cb3ffdd9c7470e156c8f9b5e028"}, + {file = "yarl-1.22.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3ea66b1c11c9150f1372f69afb6b8116f2dd7286f38e14ea71a44eee9ec51b9d"}, + {file = "yarl-1.22.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3e2daa88dc91870215961e96a039ec73e4937da13cf77ce17f9cad0c18df3503"}, + {file = "yarl-1.22.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ba440ae430c00eee41509353628600212112cd5018d5def7e9b05ea7ac34eb65"}, + {file = "yarl-1.22.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e6438cc8f23a9c1478633d216b16104a586b9761db62bfacb6425bac0a36679e"}, + {file = "yarl-1.22.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c52a6e78aef5cf47a98ef8e934755abf53953379b7d53e68b15ff4420e6683d"}, + {file = "yarl-1.22.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3b06bcadaac49c70f4c88af4ffcfbe3dc155aab3163e75777818092478bcbbe7"}, + {file = "yarl-1.22.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:6944b2dc72c4d7f7052683487e3677456050ff77fcf5e6204e98caf785ad1967"}, + {file = "yarl-1.22.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:d5372ca1df0f91a86b047d1277c2aaf1edb32d78bbcefffc81b40ffd18f027ed"}, + {file = "yarl-1.22.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:51af598701f5299012b8416486b40fceef8c26fc87dc6d7d1f6fc30609ea0aa6"}, + {file = "yarl-1.22.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b266bd01fedeffeeac01a79ae181719ff848a5a13ce10075adbefc8f1daee70e"}, + {file = "yarl-1.22.0-cp311-cp311-win32.whl", hash = "sha256:a9b1ba5610a4e20f655258d5a1fdc7ebe3d837bb0e45b581398b99eb98b1f5ca"}, + {file = "yarl-1.22.0-cp311-cp311-win_amd64.whl", hash = "sha256:078278b9b0b11568937d9509b589ee83ef98ed6d561dfe2020e24a9fd08eaa2b"}, + {file = "yarl-1.22.0-cp311-cp311-win_arm64.whl", hash = "sha256:b6a6f620cfe13ccec221fa312139135166e47ae169f8253f72a0abc0dae94376"}, + {file = "yarl-1.22.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e340382d1afa5d32b892b3ff062436d592ec3d692aeea3bef3a5cfe11bbf8c6f"}, + {file = "yarl-1.22.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f1e09112a2c31ffe8d80be1b0988fa6a18c5d5cad92a9ffbb1c04c91bfe52ad2"}, + {file = "yarl-1.22.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:939fe60db294c786f6b7c2d2e121576628468f65453d86b0fe36cb52f987bd74"}, + {file = "yarl-1.22.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e1651bf8e0398574646744c1885a41198eba53dc8a9312b954073f845c90a8df"}, + {file = "yarl-1.22.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b8a0588521a26bf92a57a1705b77b8b59044cdceccac7151bd8d229e66b8dedb"}, + {file = "yarl-1.22.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:42188e6a615c1a75bcaa6e150c3fe8f3e8680471a6b10150c5f7e83f47cc34d2"}, + {file = "yarl-1.22.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f6d2cb59377d99718913ad9a151030d6f83ef420a2b8f521d94609ecc106ee82"}, + {file = "yarl-1.22.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50678a3b71c751d58d7908edc96d332af328839eea883bb554a43f539101277a"}, + {file = "yarl-1.22.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1e8fbaa7cec507aa24ea27a01456e8dd4b6fab829059b69844bd348f2d467124"}, + {file = "yarl-1.22.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:433885ab5431bc3d3d4f2f9bd15bfa1614c522b0f1405d62c4f926ccd69d04fa"}, + {file = "yarl-1.22.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:b790b39c7e9a4192dc2e201a282109ed2985a1ddbd5ac08dc56d0e121400a8f7"}, + {file = "yarl-1.22.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:31f0b53913220599446872d757257be5898019c85e7971599065bc55065dc99d"}, + {file = "yarl-1.22.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a49370e8f711daec68d09b821a34e1167792ee2d24d405cbc2387be4f158b520"}, + {file = "yarl-1.22.0-cp312-cp312-win32.whl", hash = "sha256:70dfd4f241c04bd9239d53b17f11e6ab672b9f1420364af63e8531198e3f5fe8"}, + {file = "yarl-1.22.0-cp312-cp312-win_amd64.whl", hash = "sha256:8884d8b332a5e9b88e23f60bb166890009429391864c685e17bd73a9eda9105c"}, + {file = "yarl-1.22.0-cp312-cp312-win_arm64.whl", hash = "sha256:ea70f61a47f3cc93bdf8b2f368ed359ef02a01ca6393916bc8ff877427181e74"}, + {file = "yarl-1.22.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8dee9c25c74997f6a750cd317b8ca63545169c098faee42c84aa5e506c819b53"}, + {file = "yarl-1.22.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:01e73b85a5434f89fc4fe27dcda2aff08ddf35e4d47bbbea3bdcd25321af538a"}, + {file = "yarl-1.22.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:22965c2af250d20c873cdbee8ff958fb809940aeb2e74ba5f20aaf6b7ac8c70c"}, + {file = "yarl-1.22.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b4f15793aa49793ec8d1c708ab7f9eded1aa72edc5174cae703651555ed1b601"}, + {file = "yarl-1.22.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5542339dcf2747135c5c85f68680353d5cb9ffd741c0f2e8d832d054d41f35a"}, + {file = "yarl-1.22.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5c401e05ad47a75869c3ab3e35137f8468b846770587e70d71e11de797d113df"}, + {file = "yarl-1.22.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:243dda95d901c733f5b59214d28b0120893d91777cb8aa043e6ef059d3cddfe2"}, + {file = "yarl-1.22.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bec03d0d388060058f5d291a813f21c011041938a441c593374da6077fe21b1b"}, + {file = "yarl-1.22.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b0748275abb8c1e1e09301ee3cf90c8a99678a4e92e4373705f2a2570d581273"}, + {file = "yarl-1.22.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:47fdb18187e2a4e18fda2c25c05d8251a9e4a521edaed757fef033e7d8498d9a"}, + {file = "yarl-1.22.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c7044802eec4524fde550afc28edda0dd5784c4c45f0be151a2d3ba017daca7d"}, + {file = "yarl-1.22.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:139718f35149ff544caba20fce6e8a2f71f1e39b92c700d8438a0b1d2a631a02"}, + {file = "yarl-1.22.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e1b51bebd221006d3d2f95fbe124b22b247136647ae5dcc8c7acafba66e5ee67"}, + {file = "yarl-1.22.0-cp313-cp313-win32.whl", hash = "sha256:d3e32536234a95f513bd374e93d717cf6b2231a791758de6c509e3653f234c95"}, + {file = "yarl-1.22.0-cp313-cp313-win_amd64.whl", hash = "sha256:47743b82b76d89a1d20b83e60d5c20314cbd5ba2befc9cda8f28300c4a08ed4d"}, + {file = "yarl-1.22.0-cp313-cp313-win_arm64.whl", hash = "sha256:5d0fcda9608875f7d052eff120c7a5da474a6796fe4d83e152e0e4d42f6d1a9b"}, + {file = "yarl-1.22.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:719ae08b6972befcba4310e49edb1161a88cdd331e3a694b84466bd938a6ab10"}, + {file = "yarl-1.22.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:47d8a5c446df1c4db9d21b49619ffdba90e77c89ec6e283f453856c74b50b9e3"}, + {file = "yarl-1.22.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:cfebc0ac8333520d2d0423cbbe43ae43c8838862ddb898f5ca68565e395516e9"}, + {file = "yarl-1.22.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4398557cbf484207df000309235979c79c4356518fd5c99158c7d38203c4da4f"}, + {file = "yarl-1.22.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2ca6fd72a8cd803be290d42f2dec5cdcd5299eeb93c2d929bf060ad9efaf5de0"}, + {file = "yarl-1.22.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ca1f59c4e1ab6e72f0a23c13fca5430f889634166be85dbf1013683e49e3278e"}, + {file = "yarl-1.22.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6c5010a52015e7c70f86eb967db0f37f3c8bd503a695a49f8d45700144667708"}, + {file = "yarl-1.22.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d7672ecf7557476642c88497c2f8d8542f8e36596e928e9bcba0e42e1e7d71f"}, + {file = "yarl-1.22.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:3b7c88eeef021579d600e50363e0b6ee4f7f6f728cd3486b9d0f3ee7b946398d"}, + {file = "yarl-1.22.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:f4afb5c34f2c6fecdcc182dfcfc6af6cccf1aa923eed4d6a12e9d96904e1a0d8"}, + {file = "yarl-1.22.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:59c189e3e99a59cf8d83cbb31d4db02d66cda5a1a4374e8a012b51255341abf5"}, + {file = "yarl-1.22.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:5a3bf7f62a289fa90f1990422dc8dff5a458469ea71d1624585ec3a4c8d6960f"}, + {file = "yarl-1.22.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:de6b9a04c606978fdfe72666fa216ffcf2d1a9f6a381058d4378f8d7b1e5de62"}, + {file = "yarl-1.22.0-cp313-cp313t-win32.whl", hash = "sha256:1834bb90991cc2999f10f97f5f01317f99b143284766d197e43cd5b45eb18d03"}, + {file = "yarl-1.22.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ff86011bd159a9d2dfc89c34cfd8aff12875980e3bd6a39ff097887520e60249"}, + {file = "yarl-1.22.0-cp313-cp313t-win_arm64.whl", hash = "sha256:7861058d0582b847bc4e3a4a4c46828a410bca738673f35a29ba3ca5db0b473b"}, + {file = "yarl-1.22.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:34b36c2c57124530884d89d50ed2c1478697ad7473efd59cfd479945c95650e4"}, + {file = "yarl-1.22.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:0dd9a702591ca2e543631c2a017e4a547e38a5c0f29eece37d9097e04a7ac683"}, + {file = "yarl-1.22.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:594fcab1032e2d2cc3321bb2e51271e7cd2b516c7d9aee780ece81b07ff8244b"}, + {file = "yarl-1.22.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f3d7a87a78d46a2e3d5b72587ac14b4c16952dd0887dbb051451eceac774411e"}, + {file = "yarl-1.22.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:852863707010316c973162e703bddabec35e8757e67fcb8ad58829de1ebc8590"}, + {file = "yarl-1.22.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:131a085a53bfe839a477c0845acf21efc77457ba2bcf5899618136d64f3303a2"}, + {file = "yarl-1.22.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:078a8aefd263f4d4f923a9677b942b445a2be970ca24548a8102689a3a8ab8da"}, + {file = "yarl-1.22.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bca03b91c323036913993ff5c738d0842fc9c60c4648e5c8d98331526df89784"}, + {file = "yarl-1.22.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:68986a61557d37bb90d3051a45b91fa3d5c516d177dfc6dd6f2f436a07ff2b6b"}, + {file = "yarl-1.22.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:4792b262d585ff0dff6bcb787f8492e40698443ec982a3568c2096433660c694"}, + {file = "yarl-1.22.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:ebd4549b108d732dba1d4ace67614b9545b21ece30937a63a65dd34efa19732d"}, + {file = "yarl-1.22.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f87ac53513d22240c7d59203f25cc3beac1e574c6cd681bbfd321987b69f95fd"}, + {file = "yarl-1.22.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:22b029f2881599e2f1b06f8f1db2ee63bd309e2293ba2d566e008ba12778b8da"}, + {file = "yarl-1.22.0-cp314-cp314-win32.whl", hash = "sha256:6a635ea45ba4ea8238463b4f7d0e721bad669f80878b7bfd1f89266e2ae63da2"}, + {file = "yarl-1.22.0-cp314-cp314-win_amd64.whl", hash = "sha256:0d6e6885777af0f110b0e5d7e5dda8b704efed3894da26220b7f3d887b839a79"}, + {file = "yarl-1.22.0-cp314-cp314-win_arm64.whl", hash = "sha256:8218f4e98d3c10d683584cb40f0424f4b9fd6e95610232dd75e13743b070ee33"}, + {file = "yarl-1.22.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:45c2842ff0e0d1b35a6bf1cd6c690939dacb617a70827f715232b2e0494d55d1"}, + {file = "yarl-1.22.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:d947071e6ebcf2e2bee8fce76e10faca8f7a14808ca36a910263acaacef08eca"}, + {file = "yarl-1.22.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:334b8721303e61b00019474cc103bdac3d7b1f65e91f0bfedeec2d56dfe74b53"}, + {file = "yarl-1.22.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e7ce67c34138a058fd092f67d07a72b8e31ff0c9236e751957465a24b28910c"}, + {file = "yarl-1.22.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d77e1b2c6d04711478cb1c4ab90db07f1609ccf06a287d5607fcd90dc9863acf"}, + {file = "yarl-1.22.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4647674b6150d2cae088fc07de2738a84b8bcedebef29802cf0b0a82ab6face"}, + {file = "yarl-1.22.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:efb07073be061c8f79d03d04139a80ba33cbd390ca8f0297aae9cce6411e4c6b"}, + {file = "yarl-1.22.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e51ac5435758ba97ad69617e13233da53908beccc6cfcd6c34bbed8dcbede486"}, + {file = "yarl-1.22.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:33e32a0dd0c8205efa8e83d04fc9f19313772b78522d1bdc7d9aed706bfd6138"}, + {file = "yarl-1.22.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:bf4a21e58b9cde0e401e683ebd00f6ed30a06d14e93f7c8fd059f8b6e8f87b6a"}, + {file = "yarl-1.22.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:e4b582bab49ac33c8deb97e058cd67c2c50dac0dd134874106d9c774fd272529"}, + {file = "yarl-1.22.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:0b5bcc1a9c4839e7e30b7b30dd47fe5e7e44fb7054ec29b5bb8d526aa1041093"}, + {file = "yarl-1.22.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:c0232bce2170103ec23c454e54a57008a9a72b5d1c3105dc2496750da8cfa47c"}, + {file = "yarl-1.22.0-cp314-cp314t-win32.whl", hash = "sha256:8009b3173bcd637be650922ac455946197d858b3630b6d8787aa9e5c4564533e"}, + {file = "yarl-1.22.0-cp314-cp314t-win_amd64.whl", hash = "sha256:9fb17ea16e972c63d25d4a97f016d235c78dd2344820eb35bc034bc32012ee27"}, + {file = "yarl-1.22.0-cp314-cp314t-win_arm64.whl", hash = "sha256:9f6d73c1436b934e3f01df1e1b21ff765cd1d28c77dfb9ace207f746d4610ee1"}, + {file = "yarl-1.22.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3aa27acb6de7a23785d81557577491f6c38a5209a254d1191519d07d8fe51748"}, + {file = "yarl-1.22.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:af74f05666a5e531289cb1cc9c883d1de2088b8e5b4de48004e5ca8a830ac859"}, + {file = "yarl-1.22.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:62441e55958977b8167b2709c164c91a6363e25da322d87ae6dd9c6019ceecf9"}, + {file = "yarl-1.22.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b580e71cac3f8113d3135888770903eaf2f507e9421e5697d6ee6d8cd1c7f054"}, + {file = "yarl-1.22.0-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e81fda2fb4a07eda1a2252b216aa0df23ebcd4d584894e9612e80999a78fd95b"}, + {file = "yarl-1.22.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:99b6fc1d55782461b78221e95fc357b47ad98b041e8e20f47c1411d0aacddc60"}, + {file = "yarl-1.22.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:088e4e08f033db4be2ccd1f34cf29fe994772fb54cfe004bbf54db320af56890"}, + {file = "yarl-1.22.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e4e1f6f0b4da23e61188676e3ed027ef0baa833a2e633c29ff8530800edccba"}, + {file = "yarl-1.22.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:84fc3ec96fce86ce5aa305eb4aa9358279d1aa644b71fab7b8ed33fe3ba1a7ca"}, + {file = "yarl-1.22.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:5dbeefd6ca588b33576a01b0ad58aa934bc1b41ef89dee505bf2932b22ddffba"}, + {file = "yarl-1.22.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:14291620375b1060613f4aab9ebf21850058b6b1b438f386cc814813d901c60b"}, + {file = "yarl-1.22.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:a4fcfc8eb2c34148c118dfa02e6427ca278bfd0f3df7c5f99e33d2c0e81eae3e"}, + {file = "yarl-1.22.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:029866bde8d7b0878b9c160e72305bbf0a7342bcd20b9999381704ae03308dc8"}, + {file = "yarl-1.22.0-cp39-cp39-win32.whl", hash = "sha256:4dcc74149ccc8bba31ce1944acee24813e93cfdee2acda3c172df844948ddf7b"}, + {file = "yarl-1.22.0-cp39-cp39-win_amd64.whl", hash = "sha256:10619d9fdee46d20edc49d3479e2f8269d0779f1b031e6f7c2aa1c76be04b7ed"}, + {file = "yarl-1.22.0-cp39-cp39-win_arm64.whl", hash = "sha256:dd7afd3f8b0bfb4e0d9fc3c31bfe8a4ec7debe124cfd90619305def3c8ca8cd2"}, + {file = "yarl-1.22.0-py3-none-any.whl", hash = "sha256:1380560bdba02b6b6c90de54133c81c9f2a453dee9912fe58c1dcced1edb7cff"}, + {file = "yarl-1.22.0.tar.gz", hash = "sha256:bebf8557577d4401ba8bd9ff33906f1376c877aa78d1fe216ad01b4d6745af71"}, ] [package.dependencies] idna = ">=2.0" multidict = ">=4.0" -propcache = ">=0.2.0" +propcache = ">=0.2.1" [metadata] lock-version = "2.1" python-versions = ">=3.12,<4.0" -content-hash = "19d6061498c0797546947abe4c68d58644fc03764587fa313efdb6badae13ec9" +content-hash = "726aba1b7598a00fb78d05ccc3a8972bf7ad6840a27badf7baaa119fafd71ca8" diff --git a/projects/document_conversion/pyproject.toml b/projects/document_conversion/pyproject.toml index 1de3a1d..355237a 100644 --- a/projects/document_conversion/pyproject.toml +++ b/projects/document_conversion/pyproject.toml @@ -8,9 +8,9 @@ readme = "README.md" python = ">=3.12,<4.0" fastapi = "^0.115.6" uvicorn = "^0.34.0" -dapr = "^1.15.0" -dapr-ext-fastapi = "^1.15.0" -dapr-ext-workflow = "^1.15.0" +dapr = "1.16.0" +dapr-ext-fastapi = "1.16.0" +dapr-ext-workflow = "1.16.0" structlog = "^25.1.0" pydantic = "^2.10.4" typing-extensions = "^4.12.2" @@ -18,13 +18,17 @@ minio = "^7.2.14" common = { path = "../../libs/common", develop = true } jpype1 = "^1.5.2" requests = "^2.32.3" -psycopg = "^3.2.4" +psycopg = {extras = ["pool"], version = "^3.2.9"} pypdf2 = "^3.0.1" olefile = "^0.47" msoffcrypto-tool = "^5.4.2" +protobuf = "6.31.1" +asyncpg = "^0.30.0" [tool.poetry.group.dev.dependencies] ruff = "^0.9.2" +pytest = "^8.4.2" +pytest-asyncio = "^1.2.0" [build-system] requires = ["poetry-core"] @@ -33,71 +37,3 @@ build-backend = "poetry.core.masonry.api" [tool.poetry.scripts] document_conversion = "document_conversion.main:main" -[tool.ruff] -select = [ - "E", # pycodestyle errors - "W", # pycodestyle warnings - "F", # pyflakes - "I", # isort - "C", # flake8-comprehensions - "B", # flake8-bugbear - "UP", # pyupgrade - "NPY", # numpydoc - "A", # flake8-annotations - "TCH001", # Move application-only imports into TYPE_CHECKING block - "TCH002", # Move third-party imports into TYPE_CHECKING block - "TCH003", # Move standard library imports into TYPE_CHECKING block -] -ignore = [ - "E501", # line too long, handled by black - "B008", # do not perform function calls in argument defaults - "C901", # too complex - "W191", # indentation contains tabs - "F722", # syntax error in forward annotation - "UP007", # X | Y syntax while we're still supporting 3.9 - "UP038", # isinstance() X | Y instance ^ - "B905", # zip() without strict (isn't supported in 3.9) -] - -exclude = [ - ".bzr", - ".direnv", - ".eggs", - ".git", - ".git-rewrite", - ".hg", - ".mypy_cache", - ".nox", - ".pants.d", - ".pytype", - ".ruff_cache", - ".svn", - ".tox", - ".venv", - "__pypackages__", - "_build", - "buck-out", - "build", - "dist", - "node_modules", - "venv", -] - -line-length = 120 -indent-width = 4 -target-version = "py312" - -[tool.ruff.lint] -fixable = ["ALL"] -unfixable = ["B"] - -dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" -extend-select = [ - "I", # isort, added per instructions in the VS code extension (https://marketplace.visualstudio.com/items?itemName=charliermarsh.ruff) -] - -[tool.ruff.format] -quote-style = "double" -indent-style = "space" -skip-magic-trailing-comma = false -line-ending = "auto" \ No newline at end of file diff --git a/projects/document_conversion/tests/test_example.py b/projects/document_conversion/tests/test_example.py new file mode 100644 index 0000000..b497045 --- /dev/null +++ b/projects/document_conversion/tests/test_example.py @@ -0,0 +1,3 @@ +def test_example(): + """Simple example test to verify pytest is working.""" + assert True diff --git a/projects/dotnet_api/.vscode/launch.json b/projects/dotnet_api/.vscode/launch.json deleted file mode 100644 index fcc4cef..0000000 --- a/projects/dotnet_api/.vscode/launch.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "name": "Python Debugger: dotnet_api", - "type": "debugpy", - "request": "launch", - "module": "uvicorn", - "args": [ - "dotnet_api.main:app", - "--reload" - ], - "env": { - "MINIO_ENDPOINT": "localhost:9000" - }, - "jinja": true - } - ] -} \ No newline at end of file diff --git a/projects/dotnet_api/Dockerfile b/projects/dotnet_api/Dockerfile deleted file mode 100644 index 167387e..0000000 --- a/projects/dotnet_api/Dockerfile +++ /dev/null @@ -1,148 +0,0 @@ -# FROM nemesis-python-base-dev AS base -ARG PYTHON_BASE_DEV_IMAGE=nemesis-python-base-dev -ARG PYTHON_BASE_PROD_IMAGE=nemesis-python-base-prod -ARG INSPECT_ASSEMBLY_IMAGE=nemesis-inspect-assembly - -FROM ${INSPECT_ASSEMBLY_IMAGE} AS inspect-assembly - -FROM ${PYTHON_BASE_DEV_IMAGE} AS base - -ENV DOTNET_CLI_TELEMETRY_OPTOUT=1 - -# Install dotnet 6.0 -# - libicu72 is required for dotnet 6.0 (see https://stackoverflow.com/a/61661331) -RUN apt-get update \ - && apt-get install -y --no-install-recommends \ - ca-certificates \ - apt-transport-https \ - gnupg \ - libicu72 \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* - -# Install .NET SDK using Microsoft's script -RUN wget https://dot.net/v1/dotnet-install.sh -RUN chmod +x dotnet-install.sh -RUN ./dotnet-install.sh --version 6.0.418 --install-dir /usr/share/dotnet -RUN ln -s /usr/share/dotnet/dotnet /usr/bin/dotnet -RUN rm dotnet-install.sh - -# Install ilspy where any user can access it -RUN mkdir -p /opt/dotnet/tools && \ - chmod 755 /opt/dotnet/tools - -ENV DOTNET_TOOL_PATH=/opt/dotnet/tools -RUN dotnet tool install --no-cache ilspycmd --tool-path /opt/dotnet/tools --version 7.2.1.6856 - -ENV PATH="${PATH}:/opt/dotnet/tools" - -# If dependencies change, re-copy all the dependencies -# In the future we can be more efficient with this an only copy the lib folders -# that this project uses -COPY ./projects/dotnet_api/poetry.lock ./projects/dotnet_api/pyproject.toml /src/projects/dotnet_api/ - -COPY ./libs /src/libs -COPY ./projects/dotnet_api /src/projects/dotnet_api/ - -WORKDIR /src/projects/dotnet_api - -######################## -# Development -######################## -FROM base AS dev - -# First copy source files -COPY --from=base /src /src - -# Now copy from the separately defined inspect-assembly stage -COPY --from=inspect-assembly /app/ /opt/InspectAssembly - -WORKDIR /src/projects/dotnet_api -RUN poetry install - -# Immediate output (no buffering) -ENV PYTHONUNBUFFERED=1 -# No .pyc/pycache files -ENV PYTHONDONTWRITEBYTECODE=1 - -ENV LOG_LEVEL=DEBUG - -ENV UVICORN_HOST="0.0.0.0" -ENV UVICORN_PORT=1337 -ENV UVICORN_RELOAD_DIR="/src/" - -ENTRYPOINT ["/bin/sh", "-c", " \ - poetry run uvicorn dotnet_api.main:app \ - --host ${UVICORN_HOST} \ - --port ${UVICORN_PORT} \ - --reload \ - --reload-dir ${UVICORN_RELOAD_DIR} \ -"] - -######################## -# Runtime -######################## -# Bundle the app -FROM base AS bundle -COPY --from=base /src /src - -WORKDIR /src/projects/dotnet_api -RUN poetry bundle venv --python=/usr/bin/python3 --only=main /venv - -######################## -# Make the final image -######################## -FROM ${PYTHON_BASE_PROD_IMAGE} AS prod - -# Copy from the inspect-assembly stage defined at the top -COPY --from=inspect-assembly /app/ /opt/InspectAssembly - -ENV DOTNET_CLI_TELEMETRY_OPTOUT=1 - -# Install dotnet 6.0 -# - libicu72 is required for dotnet 6.0 (see https://stackoverflow.com/a/61661331) -RUN apt-get update \ - && apt-get install -y --no-install-recommends \ - ca-certificates \ - apt-transport-https \ - gnupg \ - libicu72 \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* - -# Install .NET SDK using Microsoft's script -RUN wget https://dot.net/v1/dotnet-install.sh -RUN chmod +x dotnet-install.sh -RUN ./dotnet-install.sh --version 6.0.418 --install-dir /usr/share/dotnet -RUN ln -s /usr/share/dotnet/dotnet /usr/bin/dotnet -RUN rm dotnet-install.sh - -# Install ilspy where any user can access it -RUN mkdir -p /opt/dotnet/tools && \ - chmod 755 /opt/dotnet/tools - -ENV DOTNET_TOOL_PATH=/opt/dotnet/tools -RUN dotnet tool install --no-cache ilspycmd --tool-path /opt/dotnet/tools --version 7.2.1.6856 - -ENV PATH="${PATH}:/opt/dotnet/tools" - -# TODO: Re-enable when we're ready for release -# USER nemesis - -COPY --from=bundle /venv /venv - -# Uvicorn production settings -ENV UVICORN_HOST=0.0.0.0 \ - UVICORN_PORT=1337 \ - UVICORN_WORKERS=1 \ - UVICORN_PROXY_HEADERS=1 \ - UVICORN_ACCESS_LOG=false - -ENTRYPOINT ["/bin/sh", "-c", "\ - /venv/bin/uvicorn \"dotnet_api.main:app\" \ - --host ${UVICORN_HOST} \ - --port ${UVICORN_PORT} \ - --workers ${UVICORN_WORKERS} \ - --proxy-headers \ - --no-access-log \ -"] \ No newline at end of file diff --git a/projects/dotnet_api/README.md b/projects/dotnet_api/README.md deleted file mode 100644 index e95abec..0000000 --- a/projects/dotnet_api/README.md +++ /dev/null @@ -1,59 +0,0 @@ -# .NET API Service - -A microservice for the Nemesis platform that provides comprehensive analysis of .NET assemblies including decompilation and metadata extraction. - -## Purpose - -This service processes .NET executable files and libraries to extract source code, analyze assembly structure, and provide insights into .NET applications for security assessment and reverse engineering purposes. - -## Features - -- **.NET decompilation**: Convert compiled assemblies back to readable C# source code using ILSpy -- **Assembly analysis**: Extract detailed metadata including types, methods, dependencies, and security attributes -- **Source code packaging**: Generate downloadable archives of decompiled source code -- **Cross-platform support**: Process .NET Framework, .NET Core, and .NET 5+ assemblies - -## Analysis Capabilities - -The service performs two primary analysis functions: - -### 1. Source Code Decompilation -- Uses ILSpy command-line tool to decompile .NET assemblies -- Reconstructs C# source code from IL bytecode -- Packages decompiled source into ZIP archives for download -- Preserves project structure and namespace organization - -### 2. Assembly Inspection -- Leverages custom InspectAssembly tool for metadata extraction -- Analyzes assembly manifest, types, and dependencies -- Extracts security permissions and attributes -- Identifies obfuscation and protection mechanisms - -## Supported File Types - -- .NET executables (.exe) -- .NET libraries (.dll) -- .NET Framework assemblies -- .NET Core/5+ assemblies -- Managed C++/CLI assemblies - -## Configuration - -- `INSPECT_ASSEMBLY_PATH`: Path to the InspectAssembly analysis tool (default: `/opt/InspectAssembly/InspectAssembly.dll`) - -## Dependencies - -- **ILSpy**: Command-line decompiler for .NET assemblies -- **.NET Runtime**: Required for executing analysis tools -- **InspectAssembly**: Custom tool for detailed assembly metadata extraction - -## Endpoints - -- `GET /file/{object_id}`: Analyze a .NET assembly by object ID, returns decompilation results and assembly metadata -- `GET /healthz`: Health check endpoint for service monitoring - -## Output Format - -Returns JSON containing: -- `decompilation`: Object ID of the ZIP archive containing decompiled source code -- `inspect_assembly`: Detailed assembly metadata including types, methods, and security information \ No newline at end of file diff --git a/projects/dotnet_api/dotnet_api/main.py b/projects/dotnet_api/dotnet_api/main.py deleted file mode 100644 index 8e8d0a7..0000000 --- a/projects/dotnet_api/dotnet_api/main.py +++ /dev/null @@ -1,143 +0,0 @@ -import json -import os -import shutil -import subprocess -import uuid - -import structlog -from common.dependency_checks import check_file_exists, find_missing_path_dependencies -from common.storage import StorageMinio -from fastapi import FastAPI, HTTPException -from fastapi.responses import JSONResponse - -INSPECT_ASSEMBLY_PATH = os.getenv("INSPECT_ASSEMBLY_PATH", "/opt/InspectAssembly/InspectAssembly.dll") - -logger = structlog.get_logger(module=__name__) - -find_missing_path_dependencies(["ilspycmd", "dotnet"], raise_error=True) -check_file_exists(INSPECT_ASSEMBLY_PATH) - -app = FastAPI() -storage = StorageMinio() - - -def process_dotnet(path: str) -> dict: - """Processes a .NET assembly file by decompiling it and analyzing it.""" - # Create unique temporary directories for decompilation and analysis - temp_dir_decompilation = f"/tmp/{uuid.uuid4()}" - temp_dir_analysis = f"/tmp/{uuid.uuid4()}" - - try: - # Create necessary directories - os.makedirs(f"{temp_dir_decompilation}/source/", exist_ok=True) - os.makedirs(temp_dir_analysis, exist_ok=True) - - # Copy the file to both directories - filename = os.path.basename(path) - shutil.copy(path, temp_dir_decompilation) - shutil.copy(path, temp_dir_analysis) - - results = {} - - # Decompile the assembly using ilSpy - decompile_result = subprocess.run( - [ - "ilspycmd", - f"{temp_dir_decompilation}/{filename}", - "-p", - "-o", - f"{temp_dir_decompilation}/source/", - ], - capture_output=True, - check=True, - ) - logger.debug(f"Decompilation result: {decompile_result}") - - try: - # Analyze the assembly - logger.debug("Calling InspectAssembly", target=f"{temp_dir_analysis}/{filename}") - - logger.debug(f"inspect_assembly_path: {INSPECT_ASSEMBLY_PATH}") - - analysis_result = subprocess.run( - [ - "dotnet", - INSPECT_ASSEMBLY_PATH, - f"{temp_dir_analysis}/{filename}", - ], - capture_output=True, - check=True, - ) - logger.info(f"InspectAssembly result: {analysis_result}") - except Exception as e: - logger.exception("Exception running InspectAssembly", error=str(e)) - raise - - # Parse analysis output - analysis_output = analysis_result.stdout.decode("utf-8") - results["inspect_assembly"] = json.loads(analysis_output) - - # Create zip of decompiled source - shutil.make_archive(f"{temp_dir_decompilation}/source", "zip", f"{temp_dir_decompilation}/source/") - - # Prepare the source archive - shutil.move(f"{temp_dir_decompilation}/source.zip", f"{temp_dir_decompilation}/{filename}") - - # Upload the decompiled source archive - with open(f"{temp_dir_decompilation}/{filename}", "rb") as f: - file_uuid = storage.upload_file(f"{temp_dir_decompilation}/{filename}") - results["decompilation"] = {"object_id": file_uuid} - - return results - - except subprocess.CalledProcessError as e: - # Access the captured output - stderr_output = e.stderr - stdout_output = e.stdout - return_code = e.returncode - - import traceback - - logger.error( - "CalledProcessError in process_dotnet", - exception=str(e), - traceback=traceback.format_exc(), - stderr=stderr_output, - stdout=stdout_output, - return_code=return_code, - ) - - raise HTTPException(status_code=500, detail=f"Dotnet processing failed: {e.stderr.decode('utf-8')}") from e - except json.JSONDecodeError as e: - raise HTTPException(status_code=500, detail=f"Failed to parse analysis output: {str(e)}") from e - finally: - # Clean up temporary directories - if os.path.exists(temp_dir_decompilation): - shutil.rmtree(temp_dir_decompilation, ignore_errors=True) - if os.path.exists(temp_dir_analysis): - shutil.rmtree(temp_dir_analysis, ignore_errors=True) - - -@app.get("/file/{object_id}") -async def analyze_file(object_id: str): - try: - logger.info("Processing .NET assembly", object_id=object_id) - with storage.download(object_id) as temp_file: - # Run dotnet processing - results = process_dotnet(temp_file.name) - logger.info("Completed .NET processing", object_id=object_id) - return JSONResponse(content=results) - - except Exception as e: - import traceback - - logger.error( - "Error in analyze_file endpoint", error=str(e), traceback=traceback.format_exc(), object_id=object_id - ) - raise HTTPException(status_code=500, detail=f"Error processing file: {str(e)}") from e - - -@app.api_route("/healthz", methods=["GET", "HEAD"]) -async def healthcheck(): - """Health check endpoint for Docker healthcheck.""" - return {"status": "healthy"} diff --git a/projects/dotnet_api/poetry.lock b/projects/dotnet_api/poetry.lock deleted file mode 100644 index 578afef..0000000 --- a/projects/dotnet_api/poetry.lock +++ /dev/null @@ -1,1337 +0,0 @@ -# This file is automatically @generated by Poetry 2.0.1 and should not be changed by hand. - -[[package]] -name = "aiohappyeyeballs" -version = "2.6.1" -description = "Happy Eyeballs for asyncio" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8"}, - {file = "aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558"}, -] - -[[package]] -name = "aiohttp" -version = "3.12.13" -description = "Async http client/server framework (asyncio)" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "aiohttp-3.12.13-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5421af8f22a98f640261ee48aae3a37f0c41371e99412d55eaf2f8a46d5dad29"}, - {file = "aiohttp-3.12.13-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0fcda86f6cb318ba36ed8f1396a6a4a3fd8f856f84d426584392083d10da4de0"}, - {file = "aiohttp-3.12.13-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4cd71c9fb92aceb5a23c4c39d8ecc80389c178eba9feab77f19274843eb9412d"}, - {file = "aiohttp-3.12.13-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34ebf1aca12845066c963016655dac897651e1544f22a34c9b461ac3b4b1d3aa"}, - {file = "aiohttp-3.12.13-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:893a4639694c5b7edd4bdd8141be296042b6806e27cc1d794e585c43010cc294"}, - {file = "aiohttp-3.12.13-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:663d8ee3ffb3494502ebcccb49078faddbb84c1d870f9c1dd5a29e85d1f747ce"}, - {file = "aiohttp-3.12.13-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f0f8f6a85a0006ae2709aa4ce05749ba2cdcb4b43d6c21a16c8517c16593aabe"}, - {file = "aiohttp-3.12.13-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1582745eb63df267c92d8b61ca655a0ce62105ef62542c00a74590f306be8cb5"}, - {file = "aiohttp-3.12.13-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d59227776ee2aa64226f7e086638baa645f4b044f2947dbf85c76ab11dcba073"}, - {file = "aiohttp-3.12.13-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:06b07c418bde1c8e737d8fa67741072bd3f5b0fb66cf8c0655172188c17e5fa6"}, - {file = "aiohttp-3.12.13-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:9445c1842680efac0f81d272fd8db7163acfcc2b1436e3f420f4c9a9c5a50795"}, - {file = "aiohttp-3.12.13-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:09c4767af0b0b98c724f5d47f2bf33395c8986995b0a9dab0575ca81a554a8c0"}, - {file = "aiohttp-3.12.13-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f3854fbde7a465318ad8d3fc5bef8f059e6d0a87e71a0d3360bb56c0bf87b18a"}, - {file = "aiohttp-3.12.13-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2332b4c361c05ecd381edb99e2a33733f3db906739a83a483974b3df70a51b40"}, - {file = "aiohttp-3.12.13-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:1561db63fa1b658cd94325d303933553ea7d89ae09ff21cc3bcd41b8521fbbb6"}, - {file = "aiohttp-3.12.13-cp310-cp310-win32.whl", hash = "sha256:a0be857f0b35177ba09d7c472825d1b711d11c6d0e8a2052804e3b93166de1ad"}, - {file = "aiohttp-3.12.13-cp310-cp310-win_amd64.whl", hash = "sha256:fcc30ad4fb5cb41a33953292d45f54ef4066746d625992aeac33b8c681173178"}, - {file = "aiohttp-3.12.13-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7c229b1437aa2576b99384e4be668af1db84b31a45305d02f61f5497cfa6f60c"}, - {file = "aiohttp-3.12.13-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:04076d8c63471e51e3689c93940775dc3d12d855c0c80d18ac5a1c68f0904358"}, - {file = "aiohttp-3.12.13-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:55683615813ce3601640cfaa1041174dc956d28ba0511c8cbd75273eb0587014"}, - {file = "aiohttp-3.12.13-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:921bc91e602d7506d37643e77819cb0b840d4ebb5f8d6408423af3d3bf79a7b7"}, - {file = "aiohttp-3.12.13-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e72d17fe0974ddeae8ed86db297e23dba39c7ac36d84acdbb53df2e18505a013"}, - {file = "aiohttp-3.12.13-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0653d15587909a52e024a261943cf1c5bdc69acb71f411b0dd5966d065a51a47"}, - {file = "aiohttp-3.12.13-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a77b48997c66722c65e157c06c74332cdf9c7ad00494b85ec43f324e5c5a9b9a"}, - {file = "aiohttp-3.12.13-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d6946bae55fd36cfb8e4092c921075cde029c71c7cb571d72f1079d1e4e013bc"}, - {file = "aiohttp-3.12.13-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f95db8c8b219bcf294a53742c7bda49b80ceb9d577c8e7aa075612b7f39ffb7"}, - {file = "aiohttp-3.12.13-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:03d5eb3cfb4949ab4c74822fb3326cd9655c2b9fe22e4257e2100d44215b2e2b"}, - {file = "aiohttp-3.12.13-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:6383dd0ffa15515283c26cbf41ac8e6705aab54b4cbb77bdb8935a713a89bee9"}, - {file = "aiohttp-3.12.13-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:6548a411bc8219b45ba2577716493aa63b12803d1e5dc70508c539d0db8dbf5a"}, - {file = "aiohttp-3.12.13-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:81b0fcbfe59a4ca41dc8f635c2a4a71e63f75168cc91026c61be665945739e2d"}, - {file = "aiohttp-3.12.13-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:6a83797a0174e7995e5edce9dcecc517c642eb43bc3cba296d4512edf346eee2"}, - {file = "aiohttp-3.12.13-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a5734d8469a5633a4e9ffdf9983ff7cdb512524645c7a3d4bc8a3de45b935ac3"}, - {file = "aiohttp-3.12.13-cp311-cp311-win32.whl", hash = "sha256:fef8d50dfa482925bb6b4c208b40d8e9fa54cecba923dc65b825a72eed9a5dbd"}, - {file = "aiohttp-3.12.13-cp311-cp311-win_amd64.whl", hash = "sha256:9a27da9c3b5ed9d04c36ad2df65b38a96a37e9cfba6f1381b842d05d98e6afe9"}, - {file = "aiohttp-3.12.13-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0aa580cf80558557285b49452151b9c69f2fa3ad94c5c9e76e684719a8791b73"}, - {file = "aiohttp-3.12.13-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b103a7e414b57e6939cc4dece8e282cfb22043efd0c7298044f6594cf83ab347"}, - {file = "aiohttp-3.12.13-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:78f64e748e9e741d2eccff9597d09fb3cd962210e5b5716047cbb646dc8fe06f"}, - {file = "aiohttp-3.12.13-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29c955989bf4c696d2ededc6b0ccb85a73623ae6e112439398935362bacfaaf6"}, - {file = "aiohttp-3.12.13-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d640191016763fab76072c87d8854a19e8e65d7a6fcfcbf017926bdbbb30a7e5"}, - {file = "aiohttp-3.12.13-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4dc507481266b410dede95dd9f26c8d6f5a14315372cc48a6e43eac652237d9b"}, - {file = "aiohttp-3.12.13-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8a94daa873465d518db073bd95d75f14302e0208a08e8c942b2f3f1c07288a75"}, - {file = "aiohttp-3.12.13-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:177f52420cde4ce0bb9425a375d95577fe082cb5721ecb61da3049b55189e4e6"}, - {file = "aiohttp-3.12.13-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0f7df1f620ec40f1a7fbcb99ea17d7326ea6996715e78f71a1c9a021e31b96b8"}, - {file = "aiohttp-3.12.13-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3062d4ad53b36e17796dce1c0d6da0ad27a015c321e663657ba1cc7659cfc710"}, - {file = "aiohttp-3.12.13-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:8605e22d2a86b8e51ffb5253d9045ea73683d92d47c0b1438e11a359bdb94462"}, - {file = "aiohttp-3.12.13-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:54fbbe6beafc2820de71ece2198458a711e224e116efefa01b7969f3e2b3ddae"}, - {file = "aiohttp-3.12.13-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:050bd277dfc3768b606fd4eae79dd58ceda67d8b0b3c565656a89ae34525d15e"}, - {file = "aiohttp-3.12.13-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:2637a60910b58f50f22379b6797466c3aa6ae28a6ab6404e09175ce4955b4e6a"}, - {file = "aiohttp-3.12.13-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e986067357550d1aaa21cfe9897fa19e680110551518a5a7cf44e6c5638cb8b5"}, - {file = "aiohttp-3.12.13-cp312-cp312-win32.whl", hash = "sha256:ac941a80aeea2aaae2875c9500861a3ba356f9ff17b9cb2dbfb5cbf91baaf5bf"}, - {file = "aiohttp-3.12.13-cp312-cp312-win_amd64.whl", hash = "sha256:671f41e6146a749b6c81cb7fd07f5a8356d46febdaaaf07b0e774ff04830461e"}, - {file = "aiohttp-3.12.13-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d4a18e61f271127465bdb0e8ff36e8f02ac4a32a80d8927aa52371e93cd87938"}, - {file = "aiohttp-3.12.13-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:532542cb48691179455fab429cdb0d558b5e5290b033b87478f2aa6af5d20ace"}, - {file = "aiohttp-3.12.13-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d7eea18b52f23c050ae9db5d01f3d264ab08f09e7356d6f68e3f3ac2de9dfabb"}, - {file = "aiohttp-3.12.13-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad7c8e5c25f2a26842a7c239de3f7b6bfb92304593ef997c04ac49fb703ff4d7"}, - {file = "aiohttp-3.12.13-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6af355b483e3fe9d7336d84539fef460120c2f6e50e06c658fe2907c69262d6b"}, - {file = "aiohttp-3.12.13-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a95cf9f097498f35c88e3609f55bb47b28a5ef67f6888f4390b3d73e2bac6177"}, - {file = "aiohttp-3.12.13-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b8ed8c38a1c584fe99a475a8f60eefc0b682ea413a84c6ce769bb19a7ff1c5ef"}, - {file = "aiohttp-3.12.13-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a0b9170d5d800126b5bc89d3053a2363406d6e327afb6afaeda2d19ee8bb103"}, - {file = "aiohttp-3.12.13-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:372feeace612ef8eb41f05ae014a92121a512bd5067db8f25101dd88a8db11da"}, - {file = "aiohttp-3.12.13-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a946d3702f7965d81f7af7ea8fb03bb33fe53d311df48a46eeca17e9e0beed2d"}, - {file = "aiohttp-3.12.13-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:a0c4725fae86555bbb1d4082129e21de7264f4ab14baf735278c974785cd2041"}, - {file = "aiohttp-3.12.13-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:9b28ea2f708234f0a5c44eb6c7d9eb63a148ce3252ba0140d050b091b6e842d1"}, - {file = "aiohttp-3.12.13-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d4f5becd2a5791829f79608c6f3dc745388162376f310eb9c142c985f9441cc1"}, - {file = "aiohttp-3.12.13-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:60f2ce6b944e97649051d5f5cc0f439360690b73909230e107fd45a359d3e911"}, - {file = "aiohttp-3.12.13-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:69fc1909857401b67bf599c793f2183fbc4804717388b0b888f27f9929aa41f3"}, - {file = "aiohttp-3.12.13-cp313-cp313-win32.whl", hash = "sha256:7d7e68787a2046b0e44ba5587aa723ce05d711e3a3665b6b7545328ac8e3c0dd"}, - {file = "aiohttp-3.12.13-cp313-cp313-win_amd64.whl", hash = "sha256:5a178390ca90419bfd41419a809688c368e63c86bd725e1186dd97f6b89c2706"}, - {file = "aiohttp-3.12.13-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:36f6c973e003dc9b0bb4e8492a643641ea8ef0e97ff7aaa5c0f53d68839357b4"}, - {file = "aiohttp-3.12.13-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6cbfc73179bd67c229eb171e2e3745d2afd5c711ccd1e40a68b90427f282eab1"}, - {file = "aiohttp-3.12.13-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1e8b27b2d414f7e3205aa23bb4a692e935ef877e3a71f40d1884f6e04fd7fa74"}, - {file = "aiohttp-3.12.13-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eabded0c2b2ef56243289112c48556c395d70150ce4220d9008e6b4b3dd15690"}, - {file = "aiohttp-3.12.13-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:003038e83f1a3ff97409999995ec02fe3008a1d675478949643281141f54751d"}, - {file = "aiohttp-3.12.13-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1b6f46613031dbc92bdcaad9c4c22c7209236ec501f9c0c5f5f0b6a689bf50f3"}, - {file = "aiohttp-3.12.13-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c332c6bb04650d59fb94ed96491f43812549a3ba6e7a16a218e612f99f04145e"}, - {file = "aiohttp-3.12.13-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3fea41a2c931fb582cb15dc86a3037329e7b941df52b487a9f8b5aa960153cbd"}, - {file = "aiohttp-3.12.13-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:846104f45d18fb390efd9b422b27d8f3cf8853f1218c537f36e71a385758c896"}, - {file = "aiohttp-3.12.13-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5d6c85ac7dd350f8da2520bac8205ce99df4435b399fa7f4dc4a70407073e390"}, - {file = "aiohttp-3.12.13-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:5a1ecce0ed281bec7da8550da052a6b89552db14d0a0a45554156f085a912f48"}, - {file = "aiohttp-3.12.13-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:5304d74867028cca8f64f1cc1215eb365388033c5a691ea7aa6b0dc47412f495"}, - {file = "aiohttp-3.12.13-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:64d1f24ee95a2d1e094a4cd7a9b7d34d08db1bbcb8aa9fb717046b0a884ac294"}, - {file = "aiohttp-3.12.13-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:119c79922a7001ca6a9e253228eb39b793ea994fd2eccb79481c64b5f9d2a055"}, - {file = "aiohttp-3.12.13-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:bb18f00396d22e2f10cd8825d671d9f9a3ba968d708a559c02a627536b36d91c"}, - {file = "aiohttp-3.12.13-cp39-cp39-win32.whl", hash = "sha256:0022de47ef63fd06b065d430ac79c6b0bd24cdae7feaf0e8c6bac23b805a23a8"}, - {file = "aiohttp-3.12.13-cp39-cp39-win_amd64.whl", hash = "sha256:29e08111ccf81b2734ae03f1ad1cb03b9615e7d8f616764f22f71209c094f122"}, - {file = "aiohttp-3.12.13.tar.gz", hash = "sha256:47e2da578528264a12e4e3dd8dd72a7289e5f812758fe086473fab037a10fcce"}, -] - -[package.dependencies] -aiohappyeyeballs = ">=2.5.0" -aiosignal = ">=1.1.2" -attrs = ">=17.3.0" -frozenlist = ">=1.1.1" -multidict = ">=4.5,<7.0" -propcache = ">=0.2.0" -yarl = ">=1.17.0,<2.0" - -[package.extras] -speedups = ["Brotli", "aiodns (>=3.3.0)", "brotlicffi"] - -[[package]] -name = "aiosignal" -version = "1.3.2" -description = "aiosignal: a list of registered asynchronous callbacks" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "aiosignal-1.3.2-py2.py3-none-any.whl", hash = "sha256:45cde58e409a301715980c2b01d0c28bdde3770d8290b5eb2173759d9acb31a5"}, - {file = "aiosignal-1.3.2.tar.gz", hash = "sha256:a8c255c66fafb1e499c9351d0bf32ff2d8a0321595ebac3b93713656d2436f54"}, -] - -[package.dependencies] -frozenlist = ">=1.1.0" - -[[package]] -name = "annotated-types" -version = "0.7.0" -description = "Reusable constraint types to use with typing.Annotated" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, - {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, -] - -[[package]] -name = "anyio" -version = "4.8.0" -description = "High level compatibility layer for multiple asynchronous event loop implementations" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "anyio-4.8.0-py3-none-any.whl", hash = "sha256:b5011f270ab5eb0abf13385f851315585cc37ef330dd88e27ec3d34d651fd47a"}, - {file = "anyio-4.8.0.tar.gz", hash = "sha256:1d9fe889df5212298c0c0723fa20479d1b94883a2df44bd3897aa91083316f7a"}, -] - -[package.dependencies] -idna = ">=2.8" -sniffio = ">=1.1" -typing_extensions = {version = ">=4.5", markers = "python_version < \"3.13\""} - -[package.extras] -doc = ["Sphinx (>=7.4,<8.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx_rtd_theme"] -test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "trustme", "truststore (>=0.9.1)", "uvloop (>=0.21)"] -trio = ["trio (>=0.26.1)"] - -[[package]] -name = "argon2-cffi" -version = "23.1.0" -description = "Argon2 for Python" -optional = false -python-versions = ">=3.7" -groups = ["main"] -files = [ - {file = "argon2_cffi-23.1.0-py3-none-any.whl", hash = "sha256:c670642b78ba29641818ab2e68bd4e6a78ba53b7eff7b4c3815ae16abf91c7ea"}, - {file = "argon2_cffi-23.1.0.tar.gz", hash = "sha256:879c3e79a2729ce768ebb7d36d4609e3a78a4ca2ec3a9f12286ca057e3d0db08"}, -] - -[package.dependencies] -argon2-cffi-bindings = "*" - -[package.extras] -dev = ["argon2-cffi[tests,typing]", "tox (>4)"] -docs = ["furo", "myst-parser", "sphinx", "sphinx-copybutton", "sphinx-notfound-page"] -tests = ["hypothesis", "pytest"] -typing = ["mypy"] - -[[package]] -name = "argon2-cffi-bindings" -version = "21.2.0" -description = "Low-level CFFI bindings for Argon2" -optional = false -python-versions = ">=3.6" -groups = ["main"] -files = [ - {file = "argon2-cffi-bindings-21.2.0.tar.gz", hash = "sha256:bb89ceffa6c791807d1305ceb77dbfacc5aa499891d2c55661c6459651fc39e3"}, - {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ccb949252cb2ab3a08c02024acb77cfb179492d5701c7cbdbfd776124d4d2367"}, - {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9524464572e12979364b7d600abf96181d3541da11e23ddf565a32e70bd4dc0d"}, - {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b746dba803a79238e925d9046a63aa26bf86ab2a2fe74ce6b009a1c3f5c8f2ae"}, - {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:58ed19212051f49a523abb1dbe954337dc82d947fb6e5a0da60f7c8471a8476c"}, - {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:bd46088725ef7f58b5a1ef7ca06647ebaf0eb4baff7d1d0d177c6cc8744abd86"}, - {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_i686.whl", hash = "sha256:8cd69c07dd875537a824deec19f978e0f2078fdda07fd5c42ac29668dda5f40f"}, - {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:f1152ac548bd5b8bcecfb0b0371f082037e47128653df2e8ba6e914d384f3c3e"}, - {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-win32.whl", hash = "sha256:603ca0aba86b1349b147cab91ae970c63118a0f30444d4bc80355937c950c082"}, - {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-win_amd64.whl", hash = "sha256:b2ef1c30440dbbcba7a5dc3e319408b59676e2e039e2ae11a8775ecf482b192f"}, - {file = "argon2_cffi_bindings-21.2.0-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:e415e3f62c8d124ee16018e491a009937f8cf7ebf5eb430ffc5de21b900dad93"}, - {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3e385d1c39c520c08b53d63300c3ecc28622f076f4c2b0e6d7e796e9f6502194"}, - {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c3e3cc67fdb7d82c4718f19b4e7a87123caf8a93fde7e23cf66ac0337d3cb3f"}, - {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6a22ad9800121b71099d0fb0a65323810a15f2e292f2ba450810a7316e128ee5"}, - {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f9f8b450ed0547e3d473fdc8612083fd08dd2120d6ac8f73828df9b7d45bb351"}, - {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:93f9bf70084f97245ba10ee36575f0c3f1e7d7724d67d8e5b08e61787c320ed7"}, - {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3b9ef65804859d335dc6b31582cad2c5166f0c3e7975f324d9ffaa34ee7e6583"}, - {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4966ef5848d820776f5f562a7d45fdd70c2f330c961d0d745b784034bd9f48d"}, - {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20ef543a89dee4db46a1a6e206cd015360e5a75822f76df533845c3cbaf72670"}, - {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed2937d286e2ad0cc79a7087d3c272832865f779430e0cc2b4f3718d3159b0cb"}, - {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:5e00316dabdaea0b2dd82d141cc66889ced0cdcbfa599e8b471cf22c620c329a"}, -] - -[package.dependencies] -cffi = ">=1.0.1" - -[package.extras] -dev = ["cogapp", "pre-commit", "pytest", "wheel"] -tests = ["pytest"] - -[[package]] -name = "attrs" -version = "24.3.0" -description = "Classes Without Boilerplate" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "attrs-24.3.0-py3-none-any.whl", hash = "sha256:ac96cd038792094f438ad1f6ff80837353805ac950cd2aa0e0625ef19850c308"}, - {file = "attrs-24.3.0.tar.gz", hash = "sha256:8f5c07333d543103541ba7be0e2ce16eeee8130cb0b3f9238ab904ce1e85baff"}, -] - -[package.extras] -benchmark = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -cov = ["cloudpickle", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier (<24.7)"] -tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"] - -[[package]] -name = "certifi" -version = "2024.12.14" -description = "Python package for providing Mozilla's CA Bundle." -optional = false -python-versions = ">=3.6" -groups = ["main"] -files = [ - {file = "certifi-2024.12.14-py3-none-any.whl", hash = "sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56"}, - {file = "certifi-2024.12.14.tar.gz", hash = "sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db"}, -] - -[[package]] -name = "cffi" -version = "1.17.1" -description = "Foreign Function Interface for Python calling C code." -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"}, - {file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"}, - {file = "cffi-1.17.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382"}, - {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702"}, - {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3"}, - {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6"}, - {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17"}, - {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8"}, - {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e"}, - {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be"}, - {file = "cffi-1.17.1-cp310-cp310-win32.whl", hash = "sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c"}, - {file = "cffi-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15"}, - {file = "cffi-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401"}, - {file = "cffi-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf"}, - {file = "cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4"}, - {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41"}, - {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1"}, - {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6"}, - {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d"}, - {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6"}, - {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f"}, - {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b"}, - {file = "cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655"}, - {file = "cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0"}, - {file = "cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4"}, - {file = "cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c"}, - {file = "cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36"}, - {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5"}, - {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff"}, - {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99"}, - {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93"}, - {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3"}, - {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8"}, - {file = "cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65"}, - {file = "cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903"}, - {file = "cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e"}, - {file = "cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2"}, - {file = "cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3"}, - {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683"}, - {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5"}, - {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4"}, - {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd"}, - {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed"}, - {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9"}, - {file = "cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d"}, - {file = "cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a"}, - {file = "cffi-1.17.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:636062ea65bd0195bc012fea9321aca499c0504409f413dc88af450b57ffd03b"}, - {file = "cffi-1.17.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7eac2ef9b63c79431bc4b25f1cd649d7f061a28808cbc6c47b534bd789ef964"}, - {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e221cf152cff04059d011ee126477f0d9588303eb57e88923578ace7baad17f9"}, - {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:31000ec67d4221a71bd3f67df918b1f88f676f1c3b535a7eb473255fdc0b83fc"}, - {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f17be4345073b0a7b8ea599688f692ac3ef23ce28e5df79c04de519dbc4912c"}, - {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2b1fac190ae3ebfe37b979cc1ce69c81f4e4fe5746bb401dca63a9062cdaf1"}, - {file = "cffi-1.17.1-cp38-cp38-win32.whl", hash = "sha256:7596d6620d3fa590f677e9ee430df2958d2d6d6de2feeae5b20e82c00b76fbf8"}, - {file = "cffi-1.17.1-cp38-cp38-win_amd64.whl", hash = "sha256:78122be759c3f8a014ce010908ae03364d00a1f81ab5c7f4a7a5120607ea56e1"}, - {file = "cffi-1.17.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b2ab587605f4ba0bf81dc0cb08a41bd1c0a5906bd59243d56bad7668a6fc6c16"}, - {file = "cffi-1.17.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:28b16024becceed8c6dfbc75629e27788d8a3f9030691a1dbf9821a128b22c36"}, - {file = "cffi-1.17.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d599671f396c4723d016dbddb72fe8e0397082b0a77a4fab8028923bec050e8"}, - {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca74b8dbe6e8e8263c0ffd60277de77dcee6c837a3d0881d8c1ead7268c9e576"}, - {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87"}, - {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98e3969bcff97cae1b2def8ba499ea3d6f31ddfdb7635374834cf89a1a08ecf0"}, - {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdf5ce3acdfd1661132f2a9c19cac174758dc2352bfe37d98aa7512c6b7178b3"}, - {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9755e4345d1ec879e3849e62222a18c7174d65a6a92d5b346b1863912168b595"}, - {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f1e22e8c4419538cb197e4dd60acc919d7696e5ef98ee4da4e01d3f8cfa4cc5a"}, - {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c03e868a0b3bc35839ba98e74211ed2b05d2119be4e8a0f224fba9384f1fe02e"}, - {file = "cffi-1.17.1-cp39-cp39-win32.whl", hash = "sha256:e31ae45bc2e29f6b2abd0de1cc3b9d5205aa847cafaecb8af1476a609a2f6eb7"}, - {file = "cffi-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662"}, - {file = "cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824"}, -] - -[package.dependencies] -pycparser = "*" - -[[package]] -name = "click" -version = "8.1.8" -description = "Composable command line interface toolkit" -optional = false -python-versions = ">=3.7" -groups = ["main"] -files = [ - {file = "click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2"}, - {file = "click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "platform_system == \"Windows\""} - -[[package]] -name = "colorama" -version = "0.4.6" -description = "Cross-platform colored terminal text." -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -groups = ["main"] -markers = "platform_system == \"Windows\"" -files = [ - {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, - {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, -] - -[[package]] -name = "common" -version = "0.1.0" -description = "" -optional = false -python-versions = ">=3.12" -groups = ["main"] -files = [] -develop = true - -[package.dependencies] -dapr = ">=1.14.0,<2.0.0" -fastapi = ">=0.115.6,<0.116.0" -minio = ">=7.2.14,<8.0.0" -pydantic = ">=2.10.5,<3.0.0" -structlog = ">=25.1.0,<26.0.0" - -[package.source] -type = "directory" -url = "../../libs/common" - -[[package]] -name = "dapr" -version = "1.14.0" -description = "The official release of Dapr Python SDK." -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "dapr-1.14.0-py3-none-any.whl", hash = "sha256:31bfa9587b58d410a575dd46e568cd731e790e235d7b61b18cb17420977e9c84"}, - {file = "dapr-1.14.0.tar.gz", hash = "sha256:d901b787a5154f4b4e448e439825693f3352dda374889ef541281dd2727b8d61"}, -] - -[package.dependencies] -aiohttp = ">=3.9.0b0" -grpcio = ">=1.37.0" -grpcio-status = ">=1.37.0" -protobuf = ">=4.22" -python-dateutil = ">=2.8.1" -typing-extensions = ">=4.4.0" - -[[package]] -name = "fastapi" -version = "0.115.6" -description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "fastapi-0.115.6-py3-none-any.whl", hash = "sha256:e9240b29e36fa8f4bb7290316988e90c381e5092e0cbe84e7818cc3713bcf305"}, - {file = "fastapi-0.115.6.tar.gz", hash = "sha256:9ec46f7addc14ea472958a96aae5b5de65f39721a46aaf5705c480d9a8b76654"}, -] - -[package.dependencies] -pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0" -starlette = ">=0.40.0,<0.42.0" -typing-extensions = ">=4.8.0" - -[package.extras] -all = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.7)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"] -standard = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "jinja2 (>=2.11.2)", "python-multipart (>=0.0.7)", "uvicorn[standard] (>=0.12.0)"] - -[[package]] -name = "frozenlist" -version = "1.5.0" -description = "A list-like structure which implements collections.abc.MutableSequence" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "frozenlist-1.5.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5b6a66c18b5b9dd261ca98dffcb826a525334b2f29e7caa54e182255c5f6a65a"}, - {file = "frozenlist-1.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d1b3eb7b05ea246510b43a7e53ed1653e55c2121019a97e60cad7efb881a97bb"}, - {file = "frozenlist-1.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:15538c0cbf0e4fa11d1e3a71f823524b0c46299aed6e10ebb4c2089abd8c3bec"}, - {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e79225373c317ff1e35f210dd5f1344ff31066ba8067c307ab60254cd3a78ad5"}, - {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9272fa73ca71266702c4c3e2d4a28553ea03418e591e377a03b8e3659d94fa76"}, - {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:498524025a5b8ba81695761d78c8dd7382ac0b052f34e66939c42df860b8ff17"}, - {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:92b5278ed9d50fe610185ecd23c55d8b307d75ca18e94c0e7de328089ac5dcba"}, - {file = "frozenlist-1.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f3c8c1dacd037df16e85227bac13cca58c30da836c6f936ba1df0c05d046d8d"}, - {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f2ac49a9bedb996086057b75bf93538240538c6d9b38e57c82d51f75a73409d2"}, - {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e66cc454f97053b79c2ab09c17fbe3c825ea6b4de20baf1be28919460dd7877f"}, - {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:5a3ba5f9a0dfed20337d3e966dc359784c9f96503674c2faf015f7fe8e96798c"}, - {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6321899477db90bdeb9299ac3627a6a53c7399c8cd58d25da094007402b039ab"}, - {file = "frozenlist-1.5.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:76e4753701248476e6286f2ef492af900ea67d9706a0155335a40ea21bf3b2f5"}, - {file = "frozenlist-1.5.0-cp310-cp310-win32.whl", hash = "sha256:977701c081c0241d0955c9586ffdd9ce44f7a7795df39b9151cd9a6fd0ce4cfb"}, - {file = "frozenlist-1.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:189f03b53e64144f90990d29a27ec4f7997d91ed3d01b51fa39d2dbe77540fd4"}, - {file = "frozenlist-1.5.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:fd74520371c3c4175142d02a976aee0b4cb4a7cc912a60586ffd8d5929979b30"}, - {file = "frozenlist-1.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2f3f7a0fbc219fb4455264cae4d9f01ad41ae6ee8524500f381de64ffaa077d5"}, - {file = "frozenlist-1.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f47c9c9028f55a04ac254346e92977bf0f166c483c74b4232bee19a6697e4778"}, - {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0996c66760924da6e88922756d99b47512a71cfd45215f3570bf1e0b694c206a"}, - {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a2fe128eb4edeabe11896cb6af88fca5346059f6c8d807e3b910069f39157869"}, - {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1a8ea951bbb6cacd492e3948b8da8c502a3f814f5d20935aae74b5df2b19cf3d"}, - {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:de537c11e4aa01d37db0d403b57bd6f0546e71a82347a97c6a9f0dcc532b3a45"}, - {file = "frozenlist-1.5.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c2623347b933fcb9095841f1cc5d4ff0b278addd743e0e966cb3d460278840d"}, - {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cee6798eaf8b1416ef6909b06f7dc04b60755206bddc599f52232606e18179d3"}, - {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f5f9da7f5dbc00a604fe74aa02ae7c98bcede8a3b8b9666f9f86fc13993bc71a"}, - {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:90646abbc7a5d5c7c19461d2e3eeb76eb0b204919e6ece342feb6032c9325ae9"}, - {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:bdac3c7d9b705d253b2ce370fde941836a5f8b3c5c2b8fd70940a3ea3af7f4f2"}, - {file = "frozenlist-1.5.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:03d33c2ddbc1816237a67f66336616416e2bbb6beb306e5f890f2eb22b959cdf"}, - {file = "frozenlist-1.5.0-cp311-cp311-win32.whl", hash = "sha256:237f6b23ee0f44066219dae14c70ae38a63f0440ce6750f868ee08775073f942"}, - {file = "frozenlist-1.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:0cc974cc93d32c42e7b0f6cf242a6bd941c57c61b618e78b6c0a96cb72788c1d"}, - {file = "frozenlist-1.5.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:31115ba75889723431aa9a4e77d5f398f5cf976eea3bdf61749731f62d4a4a21"}, - {file = "frozenlist-1.5.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7437601c4d89d070eac8323f121fcf25f88674627505334654fd027b091db09d"}, - {file = "frozenlist-1.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7948140d9f8ece1745be806f2bfdf390127cf1a763b925c4a805c603df5e697e"}, - {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:feeb64bc9bcc6b45c6311c9e9b99406660a9c05ca8a5b30d14a78555088b0b3a"}, - {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:683173d371daad49cffb8309779e886e59c2f369430ad28fe715f66d08d4ab1a"}, - {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7d57d8f702221405a9d9b40f9da8ac2e4a1a8b5285aac6100f3393675f0a85ee"}, - {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30c72000fbcc35b129cb09956836c7d7abf78ab5416595e4857d1cae8d6251a6"}, - {file = "frozenlist-1.5.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:000a77d6034fbad9b6bb880f7ec073027908f1b40254b5d6f26210d2dab1240e"}, - {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5d7f5a50342475962eb18b740f3beecc685a15b52c91f7d975257e13e029eca9"}, - {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:87f724d055eb4785d9be84e9ebf0f24e392ddfad00b3fe036e43f489fafc9039"}, - {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:6e9080bb2fb195a046e5177f10d9d82b8a204c0736a97a153c2466127de87784"}, - {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9b93d7aaa36c966fa42efcaf716e6b3900438632a626fb09c049f6a2f09fc631"}, - {file = "frozenlist-1.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:52ef692a4bc60a6dd57f507429636c2af8b6046db8b31b18dac02cbc8f507f7f"}, - {file = "frozenlist-1.5.0-cp312-cp312-win32.whl", hash = "sha256:29d94c256679247b33a3dc96cce0f93cbc69c23bf75ff715919332fdbb6a32b8"}, - {file = "frozenlist-1.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:8969190d709e7c48ea386db202d708eb94bdb29207a1f269bab1196ce0dcca1f"}, - {file = "frozenlist-1.5.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7a1a048f9215c90973402e26c01d1cff8a209e1f1b53f72b95c13db61b00f953"}, - {file = "frozenlist-1.5.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:dd47a5181ce5fcb463b5d9e17ecfdb02b678cca31280639255ce9d0e5aa67af0"}, - {file = "frozenlist-1.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1431d60b36d15cda188ea222033eec8e0eab488f39a272461f2e6d9e1a8e63c2"}, - {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6482a5851f5d72767fbd0e507e80737f9c8646ae7fd303def99bfe813f76cf7f"}, - {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:44c49271a937625619e862baacbd037a7ef86dd1ee215afc298a417ff3270608"}, - {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:12f78f98c2f1c2429d42e6a485f433722b0061d5c0b0139efa64f396efb5886b"}, - {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ce3aa154c452d2467487765e3adc730a8c153af77ad84096bc19ce19a2400840"}, - {file = "frozenlist-1.5.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9b7dc0c4338e6b8b091e8faf0db3168a37101943e687f373dce00959583f7439"}, - {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:45e0896250900b5aa25180f9aec243e84e92ac84bd4a74d9ad4138ef3f5c97de"}, - {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:561eb1c9579d495fddb6da8959fd2a1fca2c6d060d4113f5844b433fc02f2641"}, - {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:df6e2f325bfee1f49f81aaac97d2aa757c7646534a06f8f577ce184afe2f0a9e"}, - {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:140228863501b44b809fb39ec56b5d4071f4d0aa6d216c19cbb08b8c5a7eadb9"}, - {file = "frozenlist-1.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:7707a25d6a77f5d27ea7dc7d1fc608aa0a478193823f88511ef5e6b8a48f9d03"}, - {file = "frozenlist-1.5.0-cp313-cp313-win32.whl", hash = "sha256:31a9ac2b38ab9b5a8933b693db4939764ad3f299fcaa931a3e605bc3460e693c"}, - {file = "frozenlist-1.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:11aabdd62b8b9c4b84081a3c246506d1cddd2dd93ff0ad53ede5defec7886b28"}, - {file = "frozenlist-1.5.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:dd94994fc91a6177bfaafd7d9fd951bc8689b0a98168aa26b5f543868548d3ca"}, - {file = "frozenlist-1.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2d0da8bbec082bf6bf18345b180958775363588678f64998c2b7609e34719b10"}, - {file = "frozenlist-1.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:73f2e31ea8dd7df61a359b731716018c2be196e5bb3b74ddba107f694fbd7604"}, - {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:828afae9f17e6de596825cf4228ff28fbdf6065974e5ac1410cecc22f699d2b3"}, - {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f1577515d35ed5649d52ab4319db757bb881ce3b2b796d7283e6634d99ace307"}, - {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2150cc6305a2c2ab33299453e2968611dacb970d2283a14955923062c8d00b10"}, - {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a72b7a6e3cd2725eff67cd64c8f13335ee18fc3c7befc05aed043d24c7b9ccb9"}, - {file = "frozenlist-1.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c16d2fa63e0800723139137d667e1056bee1a1cf7965153d2d104b62855e9b99"}, - {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:17dcc32fc7bda7ce5875435003220a457bcfa34ab7924a49a1c19f55b6ee185c"}, - {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:97160e245ea33d8609cd2b8fd997c850b56db147a304a262abc2b3be021a9171"}, - {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:f1e6540b7fa044eee0bb5111ada694cf3dc15f2b0347ca125ee9ca984d5e9e6e"}, - {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:91d6c171862df0a6c61479d9724f22efb6109111017c87567cfeb7b5d1449fdf"}, - {file = "frozenlist-1.5.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:c1fac3e2ace2eb1052e9f7c7db480818371134410e1f5c55d65e8f3ac6d1407e"}, - {file = "frozenlist-1.5.0-cp38-cp38-win32.whl", hash = "sha256:b97f7b575ab4a8af9b7bc1d2ef7f29d3afee2226bd03ca3875c16451ad5a7723"}, - {file = "frozenlist-1.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:374ca2dabdccad8e2a76d40b1d037f5bd16824933bf7bcea3e59c891fd4a0923"}, - {file = "frozenlist-1.5.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:9bbcdfaf4af7ce002694a4e10a0159d5a8d20056a12b05b45cea944a4953f972"}, - {file = "frozenlist-1.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1893f948bf6681733aaccf36c5232c231e3b5166d607c5fa77773611df6dc336"}, - {file = "frozenlist-1.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2b5e23253bb709ef57a8e95e6ae48daa9ac5f265637529e4ce6b003a37b2621f"}, - {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f253985bb515ecd89629db13cb58d702035ecd8cfbca7d7a7e29a0e6d39af5f"}, - {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:04a5c6babd5e8fb7d3c871dc8b321166b80e41b637c31a995ed844a6139942b6"}, - {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9fe0f1c29ba24ba6ff6abf688cb0b7cf1efab6b6aa6adc55441773c252f7411"}, - {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:226d72559fa19babe2ccd920273e767c96a49b9d3d38badd7c91a0fdeda8ea08"}, - {file = "frozenlist-1.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15b731db116ab3aedec558573c1a5eec78822b32292fe4f2f0345b7f697745c2"}, - {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:366d8f93e3edfe5a918c874702f78faac300209a4d5bf38352b2c1bdc07a766d"}, - {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:1b96af8c582b94d381a1c1f51ffaedeb77c821c690ea5f01da3d70a487dd0a9b"}, - {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:c03eff4a41bd4e38415cbed054bbaff4a075b093e2394b6915dca34a40d1e38b"}, - {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:50cf5e7ee9b98f22bdecbabf3800ae78ddcc26e4a435515fc72d97903e8488e0"}, - {file = "frozenlist-1.5.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1e76bfbc72353269c44e0bc2cfe171900fbf7f722ad74c9a7b638052afe6a00c"}, - {file = "frozenlist-1.5.0-cp39-cp39-win32.whl", hash = "sha256:666534d15ba8f0fda3f53969117383d5dc021266b3c1a42c9ec4855e4b58b9d3"}, - {file = "frozenlist-1.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:5c28f4b5dbef8a0d8aad0d4de24d1e9e981728628afaf4ea0792f5d0939372f0"}, - {file = "frozenlist-1.5.0-py3-none-any.whl", hash = "sha256:d994863bba198a4a518b467bb971c56e1db3f180a25c6cf7bb1949c267f748c3"}, - {file = "frozenlist-1.5.0.tar.gz", hash = "sha256:81d5af29e61b9c8348e876d442253723928dce6433e0e76cd925cd83f1b4b817"}, -] - -[[package]] -name = "googleapis-common-protos" -version = "1.70.0" -description = "Common protobufs used in Google APIs" -optional = false -python-versions = ">=3.7" -groups = ["main"] -files = [ - {file = "googleapis_common_protos-1.70.0-py3-none-any.whl", hash = "sha256:b8bfcca8c25a2bb253e0e0b0adaf8c00773e5e6af6fd92397576680b807e0fd8"}, - {file = "googleapis_common_protos-1.70.0.tar.gz", hash = "sha256:0e1b44e0ea153e6594f9f394fef15193a68aaaea2d843f83e2742717ca753257"}, -] - -[package.dependencies] -protobuf = ">=3.20.2,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<7.0.0" - -[package.extras] -grpc = ["grpcio (>=1.44.0,<2.0.0)"] - -[[package]] -name = "grpcio" -version = "1.69.0" -description = "HTTP/2-based RPC framework" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "grpcio-1.69.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:2060ca95a8db295ae828d0fc1c7f38fb26ccd5edf9aa51a0f44251f5da332e97"}, - {file = "grpcio-1.69.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:2e52e107261fd8fa8fa457fe44bfadb904ae869d87c1280bf60f93ecd3e79278"}, - {file = "grpcio-1.69.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:316463c0832d5fcdb5e35ff2826d9aa3f26758d29cdfb59a368c1d6c39615a11"}, - {file = "grpcio-1.69.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:26c9a9c4ac917efab4704b18eed9082ed3b6ad19595f047e8173b5182fec0d5e"}, - {file = "grpcio-1.69.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90b3646ced2eae3a0599658eeccc5ba7f303bf51b82514c50715bdd2b109e5ec"}, - {file = "grpcio-1.69.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:3b75aea7c6cb91b341c85e7c1d9db1e09e1dd630b0717f836be94971e015031e"}, - {file = "grpcio-1.69.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5cfd14175f9db33d4b74d63de87c64bb0ee29ce475ce3c00c01ad2a3dc2a9e51"}, - {file = "grpcio-1.69.0-cp310-cp310-win32.whl", hash = "sha256:9031069d36cb949205293cf0e243abd5e64d6c93e01b078c37921493a41b72dc"}, - {file = "grpcio-1.69.0-cp310-cp310-win_amd64.whl", hash = "sha256:cc89b6c29f3dccbe12d7a3b3f1b3999db4882ae076c1c1f6df231d55dbd767a5"}, - {file = "grpcio-1.69.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:8de1b192c29b8ce45ee26a700044717bcbbd21c697fa1124d440548964328561"}, - {file = "grpcio-1.69.0-cp311-cp311-macosx_10_14_universal2.whl", hash = "sha256:7e76accf38808f5c5c752b0ab3fd919eb14ff8fafb8db520ad1cc12afff74de6"}, - {file = "grpcio-1.69.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:d5658c3c2660417d82db51e168b277e0ff036d0b0f859fa7576c0ffd2aec1442"}, - {file = "grpcio-1.69.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5494d0e52bf77a2f7eb17c6da662886ca0a731e56c1c85b93505bece8dc6cf4c"}, - {file = "grpcio-1.69.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4ed866f9edb574fd9be71bf64c954ce1b88fc93b2a4cbf94af221e9426eb14d6"}, - {file = "grpcio-1.69.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c5ba38aeac7a2fe353615c6b4213d1fbb3a3c34f86b4aaa8be08baaaee8cc56d"}, - {file = "grpcio-1.69.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f79e05f5bbf551c4057c227d1b041ace0e78462ac8128e2ad39ec58a382536d2"}, - {file = "grpcio-1.69.0-cp311-cp311-win32.whl", hash = "sha256:bf1f8be0da3fcdb2c1e9f374f3c2d043d606d69f425cd685110dd6d0d2d61258"}, - {file = "grpcio-1.69.0-cp311-cp311-win_amd64.whl", hash = "sha256:fb9302afc3a0e4ba0b225cd651ef8e478bf0070cf11a529175caecd5ea2474e7"}, - {file = "grpcio-1.69.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:fc18a4de8c33491ad6f70022af5c460b39611e39578a4d84de0fe92f12d5d47b"}, - {file = "grpcio-1.69.0-cp312-cp312-macosx_10_14_universal2.whl", hash = "sha256:0f0270bd9ffbff6961fe1da487bdcd594407ad390cc7960e738725d4807b18c4"}, - {file = "grpcio-1.69.0-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:dc48f99cc05e0698e689b51a05933253c69a8c8559a47f605cff83801b03af0e"}, - {file = "grpcio-1.69.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e925954b18d41aeb5ae250262116d0970893b38232689c4240024e4333ac084"}, - {file = "grpcio-1.69.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87d222569273720366f68a99cb62e6194681eb763ee1d3b1005840678d4884f9"}, - {file = "grpcio-1.69.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:b62b0f41e6e01a3e5082000b612064c87c93a49b05f7602fe1b7aa9fd5171a1d"}, - {file = "grpcio-1.69.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:db6f9fd2578dbe37db4b2994c94a1d9c93552ed77dca80e1657bb8a05b898b55"}, - {file = "grpcio-1.69.0-cp312-cp312-win32.whl", hash = "sha256:b192b81076073ed46f4b4dd612b8897d9a1e39d4eabd822e5da7b38497ed77e1"}, - {file = "grpcio-1.69.0-cp312-cp312-win_amd64.whl", hash = "sha256:1227ff7836f7b3a4ab04e5754f1d001fa52a730685d3dc894ed8bc262cc96c01"}, - {file = "grpcio-1.69.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:a78a06911d4081a24a1761d16215a08e9b6d4d29cdbb7e427e6c7e17b06bcc5d"}, - {file = "grpcio-1.69.0-cp313-cp313-macosx_10_14_universal2.whl", hash = "sha256:dc5a351927d605b2721cbb46158e431dd49ce66ffbacb03e709dc07a491dde35"}, - {file = "grpcio-1.69.0-cp313-cp313-manylinux_2_17_aarch64.whl", hash = "sha256:3629d8a8185f5139869a6a17865d03113a260e311e78fbe313f1a71603617589"}, - {file = "grpcio-1.69.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c9a281878feeb9ae26db0622a19add03922a028d4db684658f16d546601a4870"}, - {file = "grpcio-1.69.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cc614e895177ab7e4b70f154d1a7c97e152577ea101d76026d132b7aaba003b"}, - {file = "grpcio-1.69.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:1ee76cd7e2e49cf9264f6812d8c9ac1b85dda0eaea063af07292400f9191750e"}, - {file = "grpcio-1.69.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:0470fa911c503af59ec8bc4c82b371ee4303ececbbdc055f55ce48e38b20fd67"}, - {file = "grpcio-1.69.0-cp313-cp313-win32.whl", hash = "sha256:b650f34aceac8b2d08a4c8d7dc3e8a593f4d9e26d86751ebf74ebf5107d927de"}, - {file = "grpcio-1.69.0-cp313-cp313-win_amd64.whl", hash = "sha256:028337786f11fecb5d7b7fa660475a06aabf7e5e52b5ac2df47414878c0ce7ea"}, - {file = "grpcio-1.69.0-cp38-cp38-linux_armv7l.whl", hash = "sha256:b7f693db593d6bf285e015d5538bf1c86cf9c60ed30b6f7da04a00ed052fe2f3"}, - {file = "grpcio-1.69.0-cp38-cp38-macosx_10_14_universal2.whl", hash = "sha256:8b94e83f66dbf6fd642415faca0608590bc5e8d30e2c012b31d7d1b91b1de2fd"}, - {file = "grpcio-1.69.0-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:b634851b92c090763dde61df0868c730376cdb73a91bcc821af56ae043b09596"}, - {file = "grpcio-1.69.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bf5f680d3ed08c15330d7830d06bc65f58ca40c9999309517fd62880d70cb06e"}, - {file = "grpcio-1.69.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:200e48a6e7b00f804cf00a1c26292a5baa96507c7749e70a3ec10ca1a288936e"}, - {file = "grpcio-1.69.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:45a4704339b6e5b24b0e136dea9ad3815a94f30eb4f1e1d44c4ac484ef11d8dd"}, - {file = "grpcio-1.69.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:85d347cb8237751b23539981dbd2d9d8f6e9ff90082b427b13022b948eb6347a"}, - {file = "grpcio-1.69.0-cp38-cp38-win32.whl", hash = "sha256:60e5de105dc02832dc8f120056306d0ef80932bcf1c0e2b4ca3b676de6dc6505"}, - {file = "grpcio-1.69.0-cp38-cp38-win_amd64.whl", hash = "sha256:282f47d0928e40f25d007f24eb8fa051cb22551e3c74b8248bc9f9bea9c35fe0"}, - {file = "grpcio-1.69.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:dd034d68a2905464c49479b0c209c773737a4245d616234c79c975c7c90eca03"}, - {file = "grpcio-1.69.0-cp39-cp39-macosx_10_14_universal2.whl", hash = "sha256:01f834732c22a130bdf3dc154d1053bdbc887eb3ccb7f3e6285cfbfc33d9d5cc"}, - {file = "grpcio-1.69.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:a7f4ed0dcf202a70fe661329f8874bc3775c14bb3911d020d07c82c766ce0eb1"}, - {file = "grpcio-1.69.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cd7ea241b10bc5f0bb0f82c0d7896822b7ed122b3ab35c9851b440c1ccf81588"}, - {file = "grpcio-1.69.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f03dc9b4da4c0dc8a1db7a5420f575251d7319b7a839004d8916257ddbe4816"}, - {file = "grpcio-1.69.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:ca71d73a270dff052fe4edf74fef142d6ddd1f84175d9ac4a14b7280572ac519"}, - {file = "grpcio-1.69.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5ccbed100dc43704e94ccff9e07680b540d64e4cc89213ab2832b51b4f68a520"}, - {file = "grpcio-1.69.0-cp39-cp39-win32.whl", hash = "sha256:1514341def9c6ec4b7f0b9628be95f620f9d4b99331b7ef0a1845fd33d9b579c"}, - {file = "grpcio-1.69.0-cp39-cp39-win_amd64.whl", hash = "sha256:c1fea55d26d647346acb0069b08dca70984101f2dc95066e003019207212e303"}, - {file = "grpcio-1.69.0.tar.gz", hash = "sha256:936fa44241b5379c5afc344e1260d467bee495747eaf478de825bab2791da6f5"}, -] - -[package.extras] -protobuf = ["grpcio-tools (>=1.69.0)"] - -[[package]] -name = "grpcio-status" -version = "1.62.3" -description = "Status proto mapping for gRPC" -optional = false -python-versions = ">=3.6" -groups = ["main"] -files = [ - {file = "grpcio-status-1.62.3.tar.gz", hash = "sha256:289bdd7b2459794a12cf95dc0cb727bd4a1742c37bd823f760236c937e53a485"}, - {file = "grpcio_status-1.62.3-py3-none-any.whl", hash = "sha256:f9049b762ba8de6b1086789d8315846e094edac2c50beaf462338b301a8fd4b8"}, -] - -[package.dependencies] -googleapis-common-protos = ">=1.5.5" -grpcio = ">=1.62.3" -protobuf = ">=4.21.6" - -[[package]] -name = "h11" -version = "0.16.0" -description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86"}, - {file = "h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1"}, -] - -[[package]] -name = "idna" -version = "3.10" -description = "Internationalized Domain Names in Applications (IDNA)" -optional = false -python-versions = ">=3.6" -groups = ["main"] -files = [ - {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, - {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, -] - -[package.extras] -all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] - -[[package]] -name = "minio" -version = "7.2.14" -description = "MinIO Python SDK for Amazon S3 Compatible Cloud Storage" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "minio-7.2.14-py3-none-any.whl", hash = "sha256:868dfe907e1702ce4bec86df1f3ced577a73ca85f344ef898d94fe2b5237f8c1"}, - {file = "minio-7.2.14.tar.gz", hash = "sha256:f5c24bf236fefd2edc567cd4455dc49a11ad8ff7ac984bb031b849d82f01222a"}, -] - -[package.dependencies] -argon2-cffi = "*" -certifi = "*" -pycryptodome = "*" -typing-extensions = "*" -urllib3 = "*" - -[[package]] -name = "multidict" -version = "6.1.0" -description = "multidict implementation" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "multidict-6.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3380252550e372e8511d49481bd836264c009adb826b23fefcc5dd3c69692f60"}, - {file = "multidict-6.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:99f826cbf970077383d7de805c0681799491cb939c25450b9b5b3ced03ca99f1"}, - {file = "multidict-6.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a114d03b938376557927ab23f1e950827c3b893ccb94b62fd95d430fd0e5cf53"}, - {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1c416351ee6271b2f49b56ad7f308072f6f44b37118d69c2cad94f3fa8a40d5"}, - {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6b5d83030255983181005e6cfbac1617ce9746b219bc2aad52201ad121226581"}, - {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3e97b5e938051226dc025ec80980c285b053ffb1e25a3db2a3aa3bc046bf7f56"}, - {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d618649d4e70ac6efcbba75be98b26ef5078faad23592f9b51ca492953012429"}, - {file = "multidict-6.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10524ebd769727ac77ef2278390fb0068d83f3acb7773792a5080f2b0abf7748"}, - {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ff3827aef427c89a25cc96ded1759271a93603aba9fb977a6d264648ebf989db"}, - {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:06809f4f0f7ab7ea2cabf9caca7d79c22c0758b58a71f9d32943ae13c7ace056"}, - {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f179dee3b863ab1c59580ff60f9d99f632f34ccb38bf67a33ec6b3ecadd0fd76"}, - {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:aaed8b0562be4a0876ee3b6946f6869b7bcdb571a5d1496683505944e268b160"}, - {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3c8b88a2ccf5493b6c8da9076fb151ba106960a2df90c2633f342f120751a9e7"}, - {file = "multidict-6.1.0-cp310-cp310-win32.whl", hash = "sha256:4a9cb68166a34117d6646c0023c7b759bf197bee5ad4272f420a0141d7eb03a0"}, - {file = "multidict-6.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:20b9b5fbe0b88d0bdef2012ef7dee867f874b72528cf1d08f1d59b0e3850129d"}, - {file = "multidict-6.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3efe2c2cb5763f2f1b275ad2bf7a287d3f7ebbef35648a9726e3b69284a4f3d6"}, - {file = "multidict-6.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c7053d3b0353a8b9de430a4f4b4268ac9a4fb3481af37dfe49825bf45ca24156"}, - {file = "multidict-6.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:27e5fc84ccef8dfaabb09d82b7d179c7cf1a3fbc8a966f8274fcb4ab2eb4cadb"}, - {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e2b90b43e696f25c62656389d32236e049568b39320e2735d51f08fd362761b"}, - {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d83a047959d38a7ff552ff94be767b7fd79b831ad1cd9920662db05fec24fe72"}, - {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d1a9dd711d0877a1ece3d2e4fea11a8e75741ca21954c919406b44e7cf971304"}, - {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec2abea24d98246b94913b76a125e855eb5c434f7c46546046372fe60f666351"}, - {file = "multidict-6.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4867cafcbc6585e4b678876c489b9273b13e9fff9f6d6d66add5e15d11d926cb"}, - {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5b48204e8d955c47c55b72779802b219a39acc3ee3d0116d5080c388970b76e3"}, - {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:d8fff389528cad1618fb4b26b95550327495462cd745d879a8c7c2115248e399"}, - {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a7a9541cd308eed5e30318430a9c74d2132e9a8cb46b901326272d780bf2d423"}, - {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:da1758c76f50c39a2efd5e9859ce7d776317eb1dd34317c8152ac9251fc574a3"}, - {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c943a53e9186688b45b323602298ab727d8865d8c9ee0b17f8d62d14b56f0753"}, - {file = "multidict-6.1.0-cp311-cp311-win32.whl", hash = "sha256:90f8717cb649eea3504091e640a1b8568faad18bd4b9fcd692853a04475a4b80"}, - {file = "multidict-6.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:82176036e65644a6cc5bd619f65f6f19781e8ec2e5330f51aa9ada7504cc1926"}, - {file = "multidict-6.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b04772ed465fa3cc947db808fa306d79b43e896beb677a56fb2347ca1a49c1fa"}, - {file = "multidict-6.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6180c0ae073bddeb5a97a38c03f30c233e0a4d39cd86166251617d1bbd0af436"}, - {file = "multidict-6.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:071120490b47aa997cca00666923a83f02c7fbb44f71cf7f136df753f7fa8761"}, - {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50b3a2710631848991d0bf7de077502e8994c804bb805aeb2925a981de58ec2e"}, - {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b58c621844d55e71c1b7f7c498ce5aa6985d743a1a59034c57a905b3f153c1ef"}, - {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55b6d90641869892caa9ca42ff913f7ff1c5ece06474fbd32fb2cf6834726c95"}, - {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b820514bfc0b98a30e3d85462084779900347e4d49267f747ff54060cc33925"}, - {file = "multidict-6.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10a9b09aba0c5b48c53761b7c720aaaf7cf236d5fe394cd399c7ba662d5f9966"}, - {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1e16bf3e5fc9f44632affb159d30a437bfe286ce9e02754759be5536b169b305"}, - {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:76f364861c3bfc98cbbcbd402d83454ed9e01a5224bb3a28bf70002a230f73e2"}, - {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:820c661588bd01a0aa62a1283f20d2be4281b086f80dad9e955e690c75fb54a2"}, - {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:0e5f362e895bc5b9e67fe6e4ded2492d8124bdf817827f33c5b46c2fe3ffaca6"}, - {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3ec660d19bbc671e3a6443325f07263be452c453ac9e512f5eb935e7d4ac28b3"}, - {file = "multidict-6.1.0-cp312-cp312-win32.whl", hash = "sha256:58130ecf8f7b8112cdb841486404f1282b9c86ccb30d3519faf301b2e5659133"}, - {file = "multidict-6.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:188215fc0aafb8e03341995e7c4797860181562380f81ed0a87ff455b70bf1f1"}, - {file = "multidict-6.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d569388c381b24671589335a3be6e1d45546c2988c2ebe30fdcada8457a31008"}, - {file = "multidict-6.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:052e10d2d37810b99cc170b785945421141bf7bb7d2f8799d431e7db229c385f"}, - {file = "multidict-6.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f90c822a402cb865e396a504f9fc8173ef34212a342d92e362ca498cad308e28"}, - {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b225d95519a5bf73860323e633a664b0d85ad3d5bede6d30d95b35d4dfe8805b"}, - {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:23bfd518810af7de1116313ebd9092cb9aa629beb12f6ed631ad53356ed6b86c"}, - {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c09fcfdccdd0b57867577b719c69e347a436b86cd83747f179dbf0cc0d4c1f3"}, - {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf6bea52ec97e95560af5ae576bdac3aa3aae0b6758c6efa115236d9e07dae44"}, - {file = "multidict-6.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57feec87371dbb3520da6192213c7d6fc892d5589a93db548331954de8248fd2"}, - {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0c3f390dc53279cbc8ba976e5f8035eab997829066756d811616b652b00a23a3"}, - {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:59bfeae4b25ec05b34f1956eaa1cb38032282cd4dfabc5056d0a1ec4d696d3aa"}, - {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:b2f59caeaf7632cc633b5cf6fc449372b83bbdf0da4ae04d5be36118e46cc0aa"}, - {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:37bb93b2178e02b7b618893990941900fd25b6b9ac0fa49931a40aecdf083fe4"}, - {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4e9f48f58c2c523d5a06faea47866cd35b32655c46b443f163d08c6d0ddb17d6"}, - {file = "multidict-6.1.0-cp313-cp313-win32.whl", hash = "sha256:3a37ffb35399029b45c6cc33640a92bef403c9fd388acce75cdc88f58bd19a81"}, - {file = "multidict-6.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:e9aa71e15d9d9beaad2c6b9319edcdc0a49a43ef5c0a4c8265ca9ee7d6c67774"}, - {file = "multidict-6.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:db7457bac39421addd0c8449933ac32d8042aae84a14911a757ae6ca3eef1392"}, - {file = "multidict-6.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d094ddec350a2fb899fec68d8353c78233debde9b7d8b4beeafa70825f1c281a"}, - {file = "multidict-6.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5845c1fd4866bb5dd3125d89b90e57ed3138241540897de748cdf19de8a2fca2"}, - {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9079dfc6a70abe341f521f78405b8949f96db48da98aeb43f9907f342f627cdc"}, - {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3914f5aaa0f36d5d60e8ece6a308ee1c9784cd75ec8151062614657a114c4478"}, - {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c08be4f460903e5a9d0f76818db3250f12e9c344e79314d1d570fc69d7f4eae4"}, - {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d093be959277cb7dee84b801eb1af388b6ad3ca6a6b6bf1ed7585895789d027d"}, - {file = "multidict-6.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3702ea6872c5a2a4eeefa6ffd36b042e9773f05b1f37ae3ef7264b1163c2dcf6"}, - {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:2090f6a85cafc5b2db085124d752757c9d251548cedabe9bd31afe6363e0aff2"}, - {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:f67f217af4b1ff66c68a87318012de788dd95fcfeb24cc889011f4e1c7454dfd"}, - {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:189f652a87e876098bbc67b4da1049afb5f5dfbaa310dd67c594b01c10388db6"}, - {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:6bb5992037f7a9eff7991ebe4273ea7f51f1c1c511e6a2ce511d0e7bdb754492"}, - {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:ac10f4c2b9e770c4e393876e35a7046879d195cd123b4f116d299d442b335bcd"}, - {file = "multidict-6.1.0-cp38-cp38-win32.whl", hash = "sha256:e27bbb6d14416713a8bd7aaa1313c0fc8d44ee48d74497a0ff4c3a1b6ccb5167"}, - {file = "multidict-6.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:22f3105d4fb15c8f57ff3959a58fcab6ce36814486500cd7485651230ad4d4ef"}, - {file = "multidict-6.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:4e18b656c5e844539d506a0a06432274d7bd52a7487e6828c63a63d69185626c"}, - {file = "multidict-6.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a185f876e69897a6f3325c3f19f26a297fa058c5e456bfcff8015e9a27e83ae1"}, - {file = "multidict-6.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ab7c4ceb38d91570a650dba194e1ca87c2b543488fe9309b4212694174fd539c"}, - {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e617fb6b0b6953fffd762669610c1c4ffd05632c138d61ac7e14ad187870669c"}, - {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:16e5f4bf4e603eb1fdd5d8180f1a25f30056f22e55ce51fb3d6ad4ab29f7d96f"}, - {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f4c035da3f544b1882bac24115f3e2e8760f10a0107614fc9839fd232200b875"}, - {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:957cf8e4b6e123a9eea554fa7ebc85674674b713551de587eb318a2df3e00255"}, - {file = "multidict-6.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:483a6aea59cb89904e1ceabd2b47368b5600fb7de78a6e4a2c2987b2d256cf30"}, - {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:87701f25a2352e5bf7454caa64757642734da9f6b11384c1f9d1a8e699758057"}, - {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:682b987361e5fd7a139ed565e30d81fd81e9629acc7d925a205366877d8c8657"}, - {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ce2186a7df133a9c895dea3331ddc5ddad42cdd0d1ea2f0a51e5d161e4762f28"}, - {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:9f636b730f7e8cb19feb87094949ba54ee5357440b9658b2a32a5ce4bce53972"}, - {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:73eae06aa53af2ea5270cc066dcaf02cc60d2994bbb2c4ef5764949257d10f43"}, - {file = "multidict-6.1.0-cp39-cp39-win32.whl", hash = "sha256:1ca0083e80e791cffc6efce7660ad24af66c8d4079d2a750b29001b53ff59ada"}, - {file = "multidict-6.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:aa466da5b15ccea564bdab9c89175c762bc12825f4659c11227f515cee76fa4a"}, - {file = "multidict-6.1.0-py3-none-any.whl", hash = "sha256:48e171e52d1c4d33888e529b999e5900356b9ae588c2f09a52dcefb158b27506"}, - {file = "multidict-6.1.0.tar.gz", hash = "sha256:22ae2ebf9b0c69d206c003e2f6a914ea33f0a932d4aa16f236afc049d9958f4a"}, -] - -[[package]] -name = "propcache" -version = "0.2.1" -description = "Accelerated property cache" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "propcache-0.2.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6b3f39a85d671436ee3d12c017f8fdea38509e4f25b28eb25877293c98c243f6"}, - {file = "propcache-0.2.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d51fbe4285d5db5d92a929e3e21536ea3dd43732c5b177c7ef03f918dff9f2"}, - {file = "propcache-0.2.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6445804cf4ec763dc70de65a3b0d9954e868609e83850a47ca4f0cb64bd79fea"}, - {file = "propcache-0.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f9479aa06a793c5aeba49ce5c5692ffb51fcd9a7016e017d555d5e2b0045d212"}, - {file = "propcache-0.2.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d9631c5e8b5b3a0fda99cb0d29c18133bca1e18aea9effe55adb3da1adef80d3"}, - {file = "propcache-0.2.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3156628250f46a0895f1f36e1d4fbe062a1af8718ec3ebeb746f1d23f0c5dc4d"}, - {file = "propcache-0.2.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b6fb63ae352e13748289f04f37868099e69dba4c2b3e271c46061e82c745634"}, - {file = "propcache-0.2.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:887d9b0a65404929641a9fabb6452b07fe4572b269d901d622d8a34a4e9043b2"}, - {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a96dc1fa45bd8c407a0af03b2d5218392729e1822b0c32e62c5bf7eeb5fb3958"}, - {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:a7e65eb5c003a303b94aa2c3852ef130230ec79e349632d030e9571b87c4698c"}, - {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:999779addc413181912e984b942fbcc951be1f5b3663cd80b2687758f434c583"}, - {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:19a0f89a7bb9d8048d9c4370c9c543c396e894c76be5525f5e1ad287f1750ddf"}, - {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:1ac2f5fe02fa75f56e1ad473f1175e11f475606ec9bd0be2e78e4734ad575034"}, - {file = "propcache-0.2.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:574faa3b79e8ebac7cb1d7930f51184ba1ccf69adfdec53a12f319a06030a68b"}, - {file = "propcache-0.2.1-cp310-cp310-win32.whl", hash = "sha256:03ff9d3f665769b2a85e6157ac8b439644f2d7fd17615a82fa55739bc97863f4"}, - {file = "propcache-0.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:2d3af2e79991102678f53e0dbf4c35de99b6b8b58f29a27ca0325816364caaba"}, - {file = "propcache-0.2.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1ffc3cca89bb438fb9c95c13fc874012f7b9466b89328c3c8b1aa93cdcfadd16"}, - {file = "propcache-0.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f174bbd484294ed9fdf09437f889f95807e5f229d5d93588d34e92106fbf6717"}, - {file = "propcache-0.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:70693319e0b8fd35dd863e3e29513875eb15c51945bf32519ef52927ca883bc3"}, - {file = "propcache-0.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b480c6a4e1138e1aa137c0079b9b6305ec6dcc1098a8ca5196283e8a49df95a9"}, - {file = "propcache-0.2.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d27b84d5880f6d8aa9ae3edb253c59d9f6642ffbb2c889b78b60361eed449787"}, - {file = "propcache-0.2.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:857112b22acd417c40fa4595db2fe28ab900c8c5fe4670c7989b1c0230955465"}, - {file = "propcache-0.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf6c4150f8c0e32d241436526f3c3f9cbd34429492abddbada2ffcff506c51af"}, - {file = "propcache-0.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:66d4cfda1d8ed687daa4bc0274fcfd5267873db9a5bc0418c2da19273040eeb7"}, - {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c2f992c07c0fca81655066705beae35fc95a2fa7366467366db627d9f2ee097f"}, - {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:4a571d97dbe66ef38e472703067021b1467025ec85707d57e78711c085984e54"}, - {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:bb6178c241278d5fe853b3de743087be7f5f4c6f7d6d22a3b524d323eecec505"}, - {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:ad1af54a62ffe39cf34db1aa6ed1a1873bd548f6401db39d8e7cd060b9211f82"}, - {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e7048abd75fe40712005bcfc06bb44b9dfcd8e101dda2ecf2f5aa46115ad07ca"}, - {file = "propcache-0.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:160291c60081f23ee43d44b08a7e5fb76681221a8e10b3139618c5a9a291b84e"}, - {file = "propcache-0.2.1-cp311-cp311-win32.whl", hash = "sha256:819ce3b883b7576ca28da3861c7e1a88afd08cc8c96908e08a3f4dd64a228034"}, - {file = "propcache-0.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:edc9fc7051e3350643ad929df55c451899bb9ae6d24998a949d2e4c87fb596d3"}, - {file = "propcache-0.2.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:081a430aa8d5e8876c6909b67bd2d937bfd531b0382d3fdedb82612c618bc41a"}, - {file = "propcache-0.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d2ccec9ac47cf4e04897619c0e0c1a48c54a71bdf045117d3a26f80d38ab1fb0"}, - {file = "propcache-0.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:14d86fe14b7e04fa306e0c43cdbeebe6b2c2156a0c9ce56b815faacc193e320d"}, - {file = "propcache-0.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:049324ee97bb67285b49632132db351b41e77833678432be52bdd0289c0e05e4"}, - {file = "propcache-0.2.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1cd9a1d071158de1cc1c71a26014dcdfa7dd3d5f4f88c298c7f90ad6f27bb46d"}, - {file = "propcache-0.2.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98110aa363f1bb4c073e8dcfaefd3a5cea0f0834c2aab23dda657e4dab2f53b5"}, - {file = "propcache-0.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:647894f5ae99c4cf6bb82a1bb3a796f6e06af3caa3d32e26d2350d0e3e3faf24"}, - {file = "propcache-0.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bfd3223c15bebe26518d58ccf9a39b93948d3dcb3e57a20480dfdd315356baff"}, - {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d71264a80f3fcf512eb4f18f59423fe82d6e346ee97b90625f283df56aee103f"}, - {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:e73091191e4280403bde6c9a52a6999d69cdfde498f1fdf629105247599b57ec"}, - {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3935bfa5fede35fb202c4b569bb9c042f337ca4ff7bd540a0aa5e37131659348"}, - {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f508b0491767bb1f2b87fdfacaba5f7eddc2f867740ec69ece6d1946d29029a6"}, - {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:1672137af7c46662a1c2be1e8dc78cb6d224319aaa40271c9257d886be4363a6"}, - {file = "propcache-0.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b74c261802d3d2b85c9df2dfb2fa81b6f90deeef63c2db9f0e029a3cac50b518"}, - {file = "propcache-0.2.1-cp312-cp312-win32.whl", hash = "sha256:d09c333d36c1409d56a9d29b3a1b800a42c76a57a5a8907eacdbce3f18768246"}, - {file = "propcache-0.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:c214999039d4f2a5b2073ac506bba279945233da8c786e490d411dfc30f855c1"}, - {file = "propcache-0.2.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aca405706e0b0a44cc6bfd41fbe89919a6a56999157f6de7e182a990c36e37bc"}, - {file = "propcache-0.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:12d1083f001ace206fe34b6bdc2cb94be66d57a850866f0b908972f90996b3e9"}, - {file = "propcache-0.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d93f3307ad32a27bda2e88ec81134b823c240aa3abb55821a8da553eed8d9439"}, - {file = "propcache-0.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba278acf14471d36316159c94a802933d10b6a1e117b8554fe0d0d9b75c9d536"}, - {file = "propcache-0.2.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4e6281aedfca15301c41f74d7005e6e3f4ca143584ba696ac69df4f02f40d629"}, - {file = "propcache-0.2.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5b750a8e5a1262434fb1517ddf64b5de58327f1adc3524a5e44c2ca43305eb0b"}, - {file = "propcache-0.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf72af5e0fb40e9babf594308911436c8efde3cb5e75b6f206c34ad18be5c052"}, - {file = "propcache-0.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b2d0a12018b04f4cb820781ec0dffb5f7c7c1d2a5cd22bff7fb055a2cb19ebce"}, - {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e800776a79a5aabdb17dcc2346a7d66d0777e942e4cd251defeb084762ecd17d"}, - {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:4160d9283bd382fa6c0c2b5e017acc95bc183570cd70968b9202ad6d8fc48dce"}, - {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:30b43e74f1359353341a7adb783c8f1b1c676367b011709f466f42fda2045e95"}, - {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:58791550b27d5488b1bb52bc96328456095d96206a250d28d874fafe11b3dfaf"}, - {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:0f022d381747f0dfe27e99d928e31bc51a18b65bb9e481ae0af1380a6725dd1f"}, - {file = "propcache-0.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:297878dc9d0a334358f9b608b56d02e72899f3b8499fc6044133f0d319e2ec30"}, - {file = "propcache-0.2.1-cp313-cp313-win32.whl", hash = "sha256:ddfab44e4489bd79bda09d84c430677fc7f0a4939a73d2bba3073036f487a0a6"}, - {file = "propcache-0.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:556fc6c10989f19a179e4321e5d678db8eb2924131e64652a51fe83e4c3db0e1"}, - {file = "propcache-0.2.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:6a9a8c34fb7bb609419a211e59da8887eeca40d300b5ea8e56af98f6fbbb1541"}, - {file = "propcache-0.2.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ae1aa1cd222c6d205853b3013c69cd04515f9d6ab6de4b0603e2e1c33221303e"}, - {file = "propcache-0.2.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:accb6150ce61c9c4b7738d45550806aa2b71c7668c6942f17b0ac182b6142fd4"}, - {file = "propcache-0.2.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5eee736daafa7af6d0a2dc15cc75e05c64f37fc37bafef2e00d77c14171c2097"}, - {file = "propcache-0.2.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7a31fc1e1bd362874863fdeed71aed92d348f5336fd84f2197ba40c59f061bd"}, - {file = "propcache-0.2.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba4cfa1052819d16699e1d55d18c92b6e094d4517c41dd231a8b9f87b6fa681"}, - {file = "propcache-0.2.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f089118d584e859c62b3da0892b88a83d611c2033ac410e929cb6754eec0ed16"}, - {file = "propcache-0.2.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:781e65134efaf88feb447e8c97a51772aa75e48b794352f94cb7ea717dedda0d"}, - {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:31f5af773530fd3c658b32b6bdc2d0838543de70eb9a2156c03e410f7b0d3aae"}, - {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:a7a078f5d37bee6690959c813977da5291b24286e7b962e62a94cec31aa5188b"}, - {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:cea7daf9fc7ae6687cf1e2c049752f19f146fdc37c2cc376e7d0032cf4f25347"}, - {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:8b3489ff1ed1e8315674d0775dc7d2195fb13ca17b3808721b54dbe9fd020faf"}, - {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:9403db39be1393618dd80c746cb22ccda168efce239c73af13c3763ef56ffc04"}, - {file = "propcache-0.2.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:5d97151bc92d2b2578ff7ce779cdb9174337390a535953cbb9452fb65164c587"}, - {file = "propcache-0.2.1-cp39-cp39-win32.whl", hash = "sha256:9caac6b54914bdf41bcc91e7eb9147d331d29235a7c967c150ef5df6464fd1bb"}, - {file = "propcache-0.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:92fc4500fcb33899b05ba73276dfb684a20d31caa567b7cb5252d48f896a91b1"}, - {file = "propcache-0.2.1-py3-none-any.whl", hash = "sha256:52277518d6aae65536e9cea52d4e7fd2f7a66f4aa2d30ed3f2fcea620ace3c54"}, - {file = "propcache-0.2.1.tar.gz", hash = "sha256:3f77ce728b19cb537714499928fe800c3dda29e8d9428778fc7c186da4c09a64"}, -] - -[[package]] -name = "protobuf" -version = "6.31.1" -description = "" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "protobuf-6.31.1-cp310-abi3-win32.whl", hash = "sha256:7fa17d5a29c2e04b7d90e5e32388b8bfd0e7107cd8e616feef7ed3fa6bdab5c9"}, - {file = "protobuf-6.31.1-cp310-abi3-win_amd64.whl", hash = "sha256:426f59d2964864a1a366254fa703b8632dcec0790d8862d30034d8245e1cd447"}, - {file = "protobuf-6.31.1-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:6f1227473dc43d44ed644425268eb7c2e488ae245d51c6866d19fe158e207402"}, - {file = "protobuf-6.31.1-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:a40fc12b84c154884d7d4c4ebd675d5b3b5283e155f324049ae396b95ddebc39"}, - {file = "protobuf-6.31.1-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:4ee898bf66f7a8b0bd21bce523814e6fbd8c6add948045ce958b73af7e8878c6"}, - {file = "protobuf-6.31.1-cp39-cp39-win32.whl", hash = "sha256:0414e3aa5a5f3ff423828e1e6a6e907d6c65c1d5b7e6e975793d5590bdeecc16"}, - {file = "protobuf-6.31.1-cp39-cp39-win_amd64.whl", hash = "sha256:8764cf4587791e7564051b35524b72844f845ad0bb011704c3736cce762d8fe9"}, - {file = "protobuf-6.31.1-py3-none-any.whl", hash = "sha256:720a6c7e6b77288b85063569baae8536671b39f15cc22037ec7045658d80489e"}, - {file = "protobuf-6.31.1.tar.gz", hash = "sha256:d8cac4c982f0b957a4dc73a80e2ea24fab08e679c0de9deb835f4a12d69aca9a"}, -] - -[[package]] -name = "pycparser" -version = "2.22" -description = "C parser in Python" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, - {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, -] - -[[package]] -name = "pycryptodome" -version = "3.21.0" -description = "Cryptographic library for Python" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" -groups = ["main"] -files = [ - {file = "pycryptodome-3.21.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:dad9bf36eda068e89059d1f07408e397856be9511d7113ea4b586642a429a4fd"}, - {file = "pycryptodome-3.21.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:a1752eca64c60852f38bb29e2c86fca30d7672c024128ef5d70cc15868fa10f4"}, - {file = "pycryptodome-3.21.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:3ba4cc304eac4d4d458f508d4955a88ba25026890e8abff9b60404f76a62c55e"}, - {file = "pycryptodome-3.21.0-cp27-cp27m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7cb087b8612c8a1a14cf37dd754685be9a8d9869bed2ffaaceb04850a8aeef7e"}, - {file = "pycryptodome-3.21.0-cp27-cp27m-musllinux_1_1_aarch64.whl", hash = "sha256:26412b21df30b2861424a6c6d5b1d8ca8107612a4cfa4d0183e71c5d200fb34a"}, - {file = "pycryptodome-3.21.0-cp27-cp27m-win32.whl", hash = "sha256:cc2269ab4bce40b027b49663d61d816903a4bd90ad88cb99ed561aadb3888dd3"}, - {file = "pycryptodome-3.21.0-cp27-cp27m-win_amd64.whl", hash = "sha256:0fa0a05a6a697ccbf2a12cec3d6d2650b50881899b845fac6e87416f8cb7e87d"}, - {file = "pycryptodome-3.21.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:6cce52e196a5f1d6797ff7946cdff2038d3b5f0aba4a43cb6bf46b575fd1b5bb"}, - {file = "pycryptodome-3.21.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:a915597ffccabe902e7090e199a7bf7a381c5506a747d5e9d27ba55197a2c568"}, - {file = "pycryptodome-3.21.0-cp27-cp27mu-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a4e74c522d630766b03a836c15bff77cb657c5fdf098abf8b1ada2aebc7d0819"}, - {file = "pycryptodome-3.21.0-cp27-cp27mu-musllinux_1_1_aarch64.whl", hash = "sha256:a3804675283f4764a02db05f5191eb8fec2bb6ca34d466167fc78a5f05bbe6b3"}, - {file = "pycryptodome-3.21.0-cp36-abi3-macosx_10_9_universal2.whl", hash = "sha256:2480ec2c72438430da9f601ebc12c518c093c13111a5c1644c82cdfc2e50b1e4"}, - {file = "pycryptodome-3.21.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:de18954104667f565e2fbb4783b56667f30fb49c4d79b346f52a29cb198d5b6b"}, - {file = "pycryptodome-3.21.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2de4b7263a33947ff440412339cb72b28a5a4c769b5c1ca19e33dd6cd1dcec6e"}, - {file = "pycryptodome-3.21.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0714206d467fc911042d01ea3a1847c847bc10884cf674c82e12915cfe1649f8"}, - {file = "pycryptodome-3.21.0-cp36-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7d85c1b613121ed3dbaa5a97369b3b757909531a959d229406a75b912dd51dd1"}, - {file = "pycryptodome-3.21.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:8898a66425a57bcf15e25fc19c12490b87bd939800f39a03ea2de2aea5e3611a"}, - {file = "pycryptodome-3.21.0-cp36-abi3-musllinux_1_2_i686.whl", hash = "sha256:932c905b71a56474bff8a9c014030bc3c882cee696b448af920399f730a650c2"}, - {file = "pycryptodome-3.21.0-cp36-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:18caa8cfbc676eaaf28613637a89980ad2fd96e00c564135bf90bc3f0b34dd93"}, - {file = "pycryptodome-3.21.0-cp36-abi3-win32.whl", hash = "sha256:280b67d20e33bb63171d55b1067f61fbd932e0b1ad976b3a184303a3dad22764"}, - {file = "pycryptodome-3.21.0-cp36-abi3-win_amd64.whl", hash = "sha256:b7aa25fc0baa5b1d95b7633af4f5f1838467f1815442b22487426f94e0d66c53"}, - {file = "pycryptodome-3.21.0-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:2cb635b67011bc147c257e61ce864879ffe6d03342dc74b6045059dfbdedafca"}, - {file = "pycryptodome-3.21.0-pp27-pypy_73-win32.whl", hash = "sha256:4c26a2f0dc15f81ea3afa3b0c87b87e501f235d332b7f27e2225ecb80c0b1cdd"}, - {file = "pycryptodome-3.21.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:d5ebe0763c982f069d3877832254f64974139f4f9655058452603ff559c482e8"}, - {file = "pycryptodome-3.21.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ee86cbde706be13f2dec5a42b52b1c1d1cbb90c8e405c68d0755134735c8dc6"}, - {file = "pycryptodome-3.21.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0fd54003ec3ce4e0f16c484a10bc5d8b9bd77fa662a12b85779a2d2d85d67ee0"}, - {file = "pycryptodome-3.21.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:5dfafca172933506773482b0e18f0cd766fd3920bd03ec85a283df90d8a17bc6"}, - {file = "pycryptodome-3.21.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:590ef0898a4b0a15485b05210b4a1c9de8806d3ad3d47f74ab1dc07c67a6827f"}, - {file = "pycryptodome-3.21.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f35e442630bc4bc2e1878482d6f59ea22e280d7121d7adeaedba58c23ab6386b"}, - {file = "pycryptodome-3.21.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff99f952db3db2fbe98a0b355175f93ec334ba3d01bbde25ad3a5a33abc02b58"}, - {file = "pycryptodome-3.21.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:8acd7d34af70ee63f9a849f957558e49a98f8f1634f86a59d2be62bb8e93f71c"}, - {file = "pycryptodome-3.21.0.tar.gz", hash = "sha256:f7787e0d469bdae763b876174cf2e6c0f7be79808af26b1da96f1a64bcf47297"}, -] - -[[package]] -name = "pydantic" -version = "2.10.5" -description = "Data validation using Python type hints" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "pydantic-2.10.5-py3-none-any.whl", hash = "sha256:4dd4e322dbe55472cb7ca7e73f4b63574eecccf2835ffa2af9021ce113c83c53"}, - {file = "pydantic-2.10.5.tar.gz", hash = "sha256:278b38dbbaec562011d659ee05f63346951b3a248a6f3642e1bc68894ea2b4ff"}, -] - -[package.dependencies] -annotated-types = ">=0.6.0" -pydantic-core = "2.27.2" -typing-extensions = ">=4.12.2" - -[package.extras] -email = ["email-validator (>=2.0.0)"] -timezone = ["tzdata"] - -[[package]] -name = "pydantic-core" -version = "2.27.2" -description = "Core functionality for Pydantic validation and serialization" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "pydantic_core-2.27.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2d367ca20b2f14095a8f4fa1210f5a7b78b8a20009ecced6b12818f455b1e9fa"}, - {file = "pydantic_core-2.27.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:491a2b73db93fab69731eaee494f320faa4e093dbed776be1a829c2eb222c34c"}, - {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7969e133a6f183be60e9f6f56bfae753585680f3b7307a8e555a948d443cc05a"}, - {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3de9961f2a346257caf0aa508a4da705467f53778e9ef6fe744c038119737ef5"}, - {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e2bb4d3e5873c37bb3dd58714d4cd0b0e6238cebc4177ac8fe878f8b3aa8e74c"}, - {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:280d219beebb0752699480fe8f1dc61ab6615c2046d76b7ab7ee38858de0a4e7"}, - {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47956ae78b6422cbd46f772f1746799cbb862de838fd8d1fbd34a82e05b0983a"}, - {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:14d4a5c49d2f009d62a2a7140d3064f686d17a5d1a268bc641954ba181880236"}, - {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:337b443af21d488716f8d0b6164de833e788aa6bd7e3a39c005febc1284f4962"}, - {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:03d0f86ea3184a12f41a2d23f7ccb79cdb5a18e06993f8a45baa8dfec746f0e9"}, - {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7041c36f5680c6e0f08d922aed302e98b3745d97fe1589db0a3eebf6624523af"}, - {file = "pydantic_core-2.27.2-cp310-cp310-win32.whl", hash = "sha256:50a68f3e3819077be2c98110c1f9dcb3817e93f267ba80a2c05bb4f8799e2ff4"}, - {file = "pydantic_core-2.27.2-cp310-cp310-win_amd64.whl", hash = "sha256:e0fd26b16394ead34a424eecf8a31a1f5137094cabe84a1bcb10fa6ba39d3d31"}, - {file = "pydantic_core-2.27.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:8e10c99ef58cfdf2a66fc15d66b16c4a04f62bca39db589ae8cba08bc55331bc"}, - {file = "pydantic_core-2.27.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:26f32e0adf166a84d0cb63be85c562ca8a6fa8de28e5f0d92250c6b7e9e2aff7"}, - {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c19d1ea0673cd13cc2f872f6c9ab42acc4e4f492a7ca9d3795ce2b112dd7e15"}, - {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5e68c4446fe0810e959cdff46ab0a41ce2f2c86d227d96dc3847af0ba7def306"}, - {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d9640b0059ff4f14d1f37321b94061c6db164fbe49b334b31643e0528d100d99"}, - {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:40d02e7d45c9f8af700f3452f329ead92da4c5f4317ca9b896de7ce7199ea459"}, - {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c1fd185014191700554795c99b347d64f2bb637966c4cfc16998a0ca700d048"}, - {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d81d2068e1c1228a565af076598f9e7451712700b673de8f502f0334f281387d"}, - {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1a4207639fb02ec2dbb76227d7c751a20b1a6b4bc52850568e52260cae64ca3b"}, - {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:3de3ce3c9ddc8bbd88f6e0e304dea0e66d843ec9de1b0042b0911c1663ffd474"}, - {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:30c5f68ded0c36466acede341551106821043e9afaad516adfb6e8fa80a4e6a6"}, - {file = "pydantic_core-2.27.2-cp311-cp311-win32.whl", hash = "sha256:c70c26d2c99f78b125a3459f8afe1aed4d9687c24fd677c6a4436bc042e50d6c"}, - {file = "pydantic_core-2.27.2-cp311-cp311-win_amd64.whl", hash = "sha256:08e125dbdc505fa69ca7d9c499639ab6407cfa909214d500897d02afb816e7cc"}, - {file = "pydantic_core-2.27.2-cp311-cp311-win_arm64.whl", hash = "sha256:26f0d68d4b235a2bae0c3fc585c585b4ecc51382db0e3ba402a22cbc440915e4"}, - {file = "pydantic_core-2.27.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9e0c8cfefa0ef83b4da9588448b6d8d2a2bf1a53c3f1ae5fca39eb3061e2f0b0"}, - {file = "pydantic_core-2.27.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:83097677b8e3bd7eaa6775720ec8e0405f1575015a463285a92bfdfe254529ef"}, - {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:172fce187655fece0c90d90a678424b013f8fbb0ca8b036ac266749c09438cb7"}, - {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:519f29f5213271eeeeb3093f662ba2fd512b91c5f188f3bb7b27bc5973816934"}, - {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05e3a55d124407fffba0dd6b0c0cd056d10e983ceb4e5dbd10dda135c31071d6"}, - {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c3ed807c7b91de05e63930188f19e921d1fe90de6b4f5cd43ee7fcc3525cb8c"}, - {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fb4aadc0b9a0c063206846d603b92030eb6f03069151a625667f982887153e2"}, - {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:28ccb213807e037460326424ceb8b5245acb88f32f3d2777427476e1b32c48c4"}, - {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:de3cd1899e2c279b140adde9357c4495ed9d47131b4a4eaff9052f23398076b3"}, - {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:220f892729375e2d736b97d0e51466252ad84c51857d4d15f5e9692f9ef12be4"}, - {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a0fcd29cd6b4e74fe8ddd2c90330fd8edf2e30cb52acda47f06dd615ae72da57"}, - {file = "pydantic_core-2.27.2-cp312-cp312-win32.whl", hash = "sha256:1e2cb691ed9834cd6a8be61228471d0a503731abfb42f82458ff27be7b2186fc"}, - {file = "pydantic_core-2.27.2-cp312-cp312-win_amd64.whl", hash = "sha256:cc3f1a99a4f4f9dd1de4fe0312c114e740b5ddead65bb4102884b384c15d8bc9"}, - {file = "pydantic_core-2.27.2-cp312-cp312-win_arm64.whl", hash = "sha256:3911ac9284cd8a1792d3cb26a2da18f3ca26c6908cc434a18f730dc0db7bfa3b"}, - {file = "pydantic_core-2.27.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7d14bd329640e63852364c306f4d23eb744e0f8193148d4044dd3dacdaacbd8b"}, - {file = "pydantic_core-2.27.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:82f91663004eb8ed30ff478d77c4d1179b3563df6cdb15c0817cd1cdaf34d154"}, - {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71b24c7d61131bb83df10cc7e687433609963a944ccf45190cfc21e0887b08c9"}, - {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fa8e459d4954f608fa26116118bb67f56b93b209c39b008277ace29937453dc9"}, - {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce8918cbebc8da707ba805b7fd0b382816858728ae7fe19a942080c24e5b7cd1"}, - {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eda3f5c2a021bbc5d976107bb302e0131351c2ba54343f8a496dc8783d3d3a6a"}, - {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd8086fa684c4775c27f03f062cbb9eaa6e17f064307e86b21b9e0abc9c0f02e"}, - {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8d9b3388db186ba0c099a6d20f0604a44eabdeef1777ddd94786cdae158729e4"}, - {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7a66efda2387de898c8f38c0cf7f14fca0b51a8ef0b24bfea5849f1b3c95af27"}, - {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:18a101c168e4e092ab40dbc2503bdc0f62010e95d292b27827871dc85450d7ee"}, - {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ba5dd002f88b78a4215ed2f8ddbdf85e8513382820ba15ad5ad8955ce0ca19a1"}, - {file = "pydantic_core-2.27.2-cp313-cp313-win32.whl", hash = "sha256:1ebaf1d0481914d004a573394f4be3a7616334be70261007e47c2a6fe7e50130"}, - {file = "pydantic_core-2.27.2-cp313-cp313-win_amd64.whl", hash = "sha256:953101387ecf2f5652883208769a79e48db18c6df442568a0b5ccd8c2723abee"}, - {file = "pydantic_core-2.27.2-cp313-cp313-win_arm64.whl", hash = "sha256:ac4dbfd1691affb8f48c2c13241a2e3b60ff23247cbcf981759c768b6633cf8b"}, - {file = "pydantic_core-2.27.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d3e8d504bdd3f10835468f29008d72fc8359d95c9c415ce6e767203db6127506"}, - {file = "pydantic_core-2.27.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:521eb9b7f036c9b6187f0b47318ab0d7ca14bd87f776240b90b21c1f4f149320"}, - {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85210c4d99a0114f5a9481b44560d7d1e35e32cc5634c656bc48e590b669b145"}, - {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d716e2e30c6f140d7560ef1538953a5cd1a87264c737643d481f2779fc247fe1"}, - {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f66d89ba397d92f840f8654756196d93804278457b5fbede59598a1f9f90b228"}, - {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:669e193c1c576a58f132e3158f9dfa9662969edb1a250c54d8fa52590045f046"}, - {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdbe7629b996647b99c01b37f11170a57ae675375b14b8c13b8518b8320ced5"}, - {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d262606bf386a5ba0b0af3b97f37c83d7011439e3dc1a9298f21efb292e42f1a"}, - {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:cabb9bcb7e0d97f74df8646f34fc76fbf793b7f6dc2438517d7a9e50eee4f14d"}, - {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_armv7l.whl", hash = "sha256:d2d63f1215638d28221f664596b1ccb3944f6e25dd18cd3b86b0a4c408d5ebb9"}, - {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bca101c00bff0adb45a833f8451b9105d9df18accb8743b08107d7ada14bd7da"}, - {file = "pydantic_core-2.27.2-cp38-cp38-win32.whl", hash = "sha256:f6f8e111843bbb0dee4cb6594cdc73e79b3329b526037ec242a3e49012495b3b"}, - {file = "pydantic_core-2.27.2-cp38-cp38-win_amd64.whl", hash = "sha256:fd1aea04935a508f62e0d0ef1f5ae968774a32afc306fb8545e06f5ff5cdf3ad"}, - {file = "pydantic_core-2.27.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:c10eb4f1659290b523af58fa7cffb452a61ad6ae5613404519aee4bfbf1df993"}, - {file = "pydantic_core-2.27.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ef592d4bad47296fb11f96cd7dc898b92e795032b4894dfb4076cfccd43a9308"}, - {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c61709a844acc6bf0b7dce7daae75195a10aac96a596ea1b776996414791ede4"}, - {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42c5f762659e47fdb7b16956c71598292f60a03aa92f8b6351504359dbdba6cf"}, - {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4c9775e339e42e79ec99c441d9730fccf07414af63eac2f0e48e08fd38a64d76"}, - {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:57762139821c31847cfb2df63c12f725788bd9f04bc2fb392790959b8f70f118"}, - {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d1e85068e818c73e048fe28cfc769040bb1f475524f4745a5dc621f75ac7630"}, - {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:097830ed52fd9e427942ff3b9bc17fab52913b2f50f2880dc4a5611446606a54"}, - {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:044a50963a614ecfae59bb1eaf7ea7efc4bc62f49ed594e18fa1e5d953c40e9f"}, - {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:4e0b4220ba5b40d727c7f879eac379b822eee5d8fff418e9d3381ee45b3b0362"}, - {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5e4f4bb20d75e9325cc9696c6802657b58bc1dbbe3022f32cc2b2b632c3fbb96"}, - {file = "pydantic_core-2.27.2-cp39-cp39-win32.whl", hash = "sha256:cca63613e90d001b9f2f9a9ceb276c308bfa2a43fafb75c8031c4f66039e8c6e"}, - {file = "pydantic_core-2.27.2-cp39-cp39-win_amd64.whl", hash = "sha256:77d1bca19b0f7021b3a982e6f903dcd5b2b06076def36a652e3907f596e29f67"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2bf14caea37e91198329b828eae1618c068dfb8ef17bb33287a7ad4b61ac314e"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:b0cb791f5b45307caae8810c2023a184c74605ec3bcbb67d13846c28ff731ff8"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:688d3fd9fcb71f41c4c015c023d12a79d1c4c0732ec9eb35d96e3388a120dcf3"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d591580c34f4d731592f0e9fe40f9cc1b430d297eecc70b962e93c5c668f15f"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:82f986faf4e644ffc189a7f1aafc86e46ef70372bb153e7001e8afccc6e54133"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:bec317a27290e2537f922639cafd54990551725fc844249e64c523301d0822fc"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:0296abcb83a797db256b773f45773da397da75a08f5fcaef41f2044adec05f50"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:0d75070718e369e452075a6017fbf187f788e17ed67a3abd47fa934d001863d9"}, - {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:7e17b560be3c98a8e3aa66ce828bdebb9e9ac6ad5466fba92eb74c4c95cb1151"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c33939a82924da9ed65dab5a65d427205a73181d8098e79b6b426bdf8ad4e656"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:00bad2484fa6bda1e216e7345a798bd37c68fb2d97558edd584942aa41b7d278"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c817e2b40aba42bac6f457498dacabc568c3b7a986fc9ba7c8d9d260b71485fb"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:251136cdad0cb722e93732cb45ca5299fb56e1344a833640bf93b2803f8d1bfd"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d2088237af596f0a524d3afc39ab3b036e8adb054ee57cbb1dcf8e09da5b29cc"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d4041c0b966a84b4ae7a09832eb691a35aec90910cd2dbe7a208de59be77965b"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:8083d4e875ebe0b864ffef72a4304827015cff328a1be6e22cc850753bfb122b"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f141ee28a0ad2123b6611b6ceff018039df17f32ada8b534e6aa039545a3efb2"}, - {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7d0c8399fcc1848491f00e0314bd59fb34a9c008761bcb422a057670c3f65e35"}, - {file = "pydantic_core-2.27.2.tar.gz", hash = "sha256:eb026e5a4c1fee05726072337ff51d1efb6f59090b7da90d30ea58625b1ffb39"}, -] - -[package.dependencies] -typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" - -[[package]] -name = "python-dateutil" -version = "2.9.0.post0" -description = "Extensions to the standard Python datetime module" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" -groups = ["main"] -files = [ - {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, - {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, -] - -[package.dependencies] -six = ">=1.5" - -[[package]] -name = "ruff" -version = "0.9.2" -description = "An extremely fast Python linter and code formatter, written in Rust." -optional = false -python-versions = ">=3.7" -groups = ["dev"] -files = [ - {file = "ruff-0.9.2-py3-none-linux_armv6l.whl", hash = "sha256:80605a039ba1454d002b32139e4970becf84b5fee3a3c3bf1c2af6f61a784347"}, - {file = "ruff-0.9.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b9aab82bb20afd5f596527045c01e6ae25a718ff1784cb92947bff1f83068b00"}, - {file = "ruff-0.9.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:fbd337bac1cfa96be615f6efcd4bc4d077edbc127ef30e2b8ba2a27e18c054d4"}, - {file = "ruff-0.9.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82b35259b0cbf8daa22a498018e300b9bb0174c2bbb7bcba593935158a78054d"}, - {file = "ruff-0.9.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8b6a9701d1e371bf41dca22015c3f89769da7576884d2add7317ec1ec8cb9c3c"}, - {file = "ruff-0.9.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9cc53e68b3c5ae41e8faf83a3b89f4a5d7b2cb666dff4b366bb86ed2a85b481f"}, - {file = "ruff-0.9.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:8efd9da7a1ee314b910da155ca7e8953094a7c10d0c0a39bfde3fcfd2a015684"}, - {file = "ruff-0.9.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3292c5a22ea9a5f9a185e2d131dc7f98f8534a32fb6d2ee7b9944569239c648d"}, - {file = "ruff-0.9.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1a605fdcf6e8b2d39f9436d343d1f0ff70c365a1e681546de0104bef81ce88df"}, - {file = "ruff-0.9.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c547f7f256aa366834829a08375c297fa63386cbe5f1459efaf174086b564247"}, - {file = "ruff-0.9.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:d18bba3d3353ed916e882521bc3e0af403949dbada344c20c16ea78f47af965e"}, - {file = "ruff-0.9.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:b338edc4610142355ccf6b87bd356729b62bf1bc152a2fad5b0c7dc04af77bfe"}, - {file = "ruff-0.9.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:492a5e44ad9b22a0ea98cf72e40305cbdaf27fac0d927f8bc9e1df316dcc96eb"}, - {file = "ruff-0.9.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:af1e9e9fe7b1f767264d26b1075ac4ad831c7db976911fa362d09b2d0356426a"}, - {file = "ruff-0.9.2-py3-none-win32.whl", hash = "sha256:71cbe22e178c5da20e1514e1e01029c73dc09288a8028a5d3446e6bba87a5145"}, - {file = "ruff-0.9.2-py3-none-win_amd64.whl", hash = "sha256:c5e1d6abc798419cf46eed03f54f2e0c3adb1ad4b801119dedf23fcaf69b55b5"}, - {file = "ruff-0.9.2-py3-none-win_arm64.whl", hash = "sha256:a1b63fa24149918f8b37cef2ee6fff81f24f0d74b6f0bdc37bc3e1f2143e41c6"}, - {file = "ruff-0.9.2.tar.gz", hash = "sha256:b5eceb334d55fae5f316f783437392642ae18e16dcf4f1858d55d3c2a0f8f5d0"}, -] - -[[package]] -name = "six" -version = "1.17.0" -description = "Python 2 and 3 compatibility utilities" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" -groups = ["main"] -files = [ - {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, - {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, -] - -[[package]] -name = "sniffio" -version = "1.3.1" -description = "Sniff out which async library your code is running under" -optional = false -python-versions = ">=3.7" -groups = ["main"] -files = [ - {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, - {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, -] - -[[package]] -name = "starlette" -version = "0.41.3" -description = "The little ASGI library that shines." -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "starlette-0.41.3-py3-none-any.whl", hash = "sha256:44cedb2b7c77a9de33a8b74b2b90e9f50d11fcf25d8270ea525ad71a25374ff7"}, - {file = "starlette-0.41.3.tar.gz", hash = "sha256:0e4ab3d16522a255be6b28260b938eae2482f98ce5cc934cb08dce8dc3ba5835"}, -] - -[package.dependencies] -anyio = ">=3.4.0,<5" - -[package.extras] -full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.7)", "pyyaml"] - -[[package]] -name = "structlog" -version = "25.1.0" -description = "Structured Logging for Python" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "structlog-25.1.0-py3-none-any.whl", hash = "sha256:843fe4f254540329f380812cbe612e1af5ec5b8172205ae634679cd35a6d6321"}, - {file = "structlog-25.1.0.tar.gz", hash = "sha256:2ef2a572e0e27f09664965d31a576afe64e46ac6084ef5cec3c2b8cd6e4e3ad3"}, -] - -[package.extras] -dev = ["freezegun (>=0.2.8)", "mypy (>=1.4)", "pretend", "pytest (>=6.0)", "pytest-asyncio (>=0.17)", "rich", "simplejson", "twisted"] -docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-mermaid", "sphinxext-opengraph", "twisted"] -tests = ["freezegun (>=0.2.8)", "pretend", "pytest (>=6.0)", "pytest-asyncio (>=0.17)", "simplejson"] -typing = ["mypy (>=1.4)", "rich", "twisted"] - -[[package]] -name = "typing-extensions" -version = "4.12.2" -description = "Backported and Experimental Type Hints for Python 3.8+" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, - {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, -] - -[[package]] -name = "urllib3" -version = "2.3.0" -description = "HTTP library with thread-safe connection pooling, file post, and more." -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df"}, - {file = "urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d"}, -] - -[package.extras] -brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] -h2 = ["h2 (>=4,<5)"] -socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] -zstd = ["zstandard (>=0.18.0)"] - -[[package]] -name = "uvicorn" -version = "0.34.0" -description = "The lightning-fast ASGI server." -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "uvicorn-0.34.0-py3-none-any.whl", hash = "sha256:023dc038422502fa28a09c7a30bf2b6991512da7dcdb8fd35fe57cfc154126f4"}, - {file = "uvicorn-0.34.0.tar.gz", hash = "sha256:404051050cd7e905de2c9a7e61790943440b3416f49cb409f965d9dcd0fa73e9"}, -] - -[package.dependencies] -click = ">=7.0" -h11 = ">=0.8" - -[package.extras] -standard = ["colorama (>=0.4)", "httptools (>=0.6.3)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"] - -[[package]] -name = "yarl" -version = "1.18.3" -description = "Yet another URL library" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "yarl-1.18.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7df647e8edd71f000a5208fe6ff8c382a1de8edfbccdbbfe649d263de07d8c34"}, - {file = "yarl-1.18.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c69697d3adff5aa4f874b19c0e4ed65180ceed6318ec856ebc423aa5850d84f7"}, - {file = "yarl-1.18.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:602d98f2c2d929f8e697ed274fbadc09902c4025c5a9963bf4e9edfc3ab6f7ed"}, - {file = "yarl-1.18.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c654d5207c78e0bd6d749f6dae1dcbbfde3403ad3a4b11f3c5544d9906969dde"}, - {file = "yarl-1.18.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5094d9206c64181d0f6e76ebd8fb2f8fe274950a63890ee9e0ebfd58bf9d787b"}, - {file = "yarl-1.18.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:35098b24e0327fc4ebdc8ffe336cee0a87a700c24ffed13161af80124b7dc8e5"}, - {file = "yarl-1.18.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3236da9272872443f81fedc389bace88408f64f89f75d1bdb2256069a8730ccc"}, - {file = "yarl-1.18.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e2c08cc9b16f4f4bc522771d96734c7901e7ebef70c6c5c35dd0f10845270bcd"}, - {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:80316a8bd5109320d38eef8833ccf5f89608c9107d02d2a7f985f98ed6876990"}, - {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:c1e1cc06da1491e6734f0ea1e6294ce00792193c463350626571c287c9a704db"}, - {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:fea09ca13323376a2fdfb353a5fa2e59f90cd18d7ca4eaa1fd31f0a8b4f91e62"}, - {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:e3b9fd71836999aad54084906f8663dffcd2a7fb5cdafd6c37713b2e72be1760"}, - {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:757e81cae69244257d125ff31663249b3013b5dc0a8520d73694aed497fb195b"}, - {file = "yarl-1.18.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b1771de9944d875f1b98a745bc547e684b863abf8f8287da8466cf470ef52690"}, - {file = "yarl-1.18.3-cp310-cp310-win32.whl", hash = "sha256:8874027a53e3aea659a6d62751800cf6e63314c160fd607489ba5c2edd753cf6"}, - {file = "yarl-1.18.3-cp310-cp310-win_amd64.whl", hash = "sha256:93b2e109287f93db79210f86deb6b9bbb81ac32fc97236b16f7433db7fc437d8"}, - {file = "yarl-1.18.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8503ad47387b8ebd39cbbbdf0bf113e17330ffd339ba1144074da24c545f0069"}, - {file = "yarl-1.18.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:02ddb6756f8f4517a2d5e99d8b2f272488e18dd0bfbc802f31c16c6c20f22193"}, - {file = "yarl-1.18.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:67a283dd2882ac98cc6318384f565bffc751ab564605959df4752d42483ad889"}, - {file = "yarl-1.18.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d980e0325b6eddc81331d3f4551e2a333999fb176fd153e075c6d1c2530aa8a8"}, - {file = "yarl-1.18.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b643562c12680b01e17239be267bc306bbc6aac1f34f6444d1bded0c5ce438ca"}, - {file = "yarl-1.18.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c017a3b6df3a1bd45b9fa49a0f54005e53fbcad16633870104b66fa1a30a29d8"}, - {file = "yarl-1.18.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75674776d96d7b851b6498f17824ba17849d790a44d282929c42dbb77d4f17ae"}, - {file = "yarl-1.18.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ccaa3a4b521b780a7e771cc336a2dba389a0861592bbce09a476190bb0c8b4b3"}, - {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2d06d3005e668744e11ed80812e61efd77d70bb7f03e33c1598c301eea20efbb"}, - {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:9d41beda9dc97ca9ab0b9888cb71f7539124bc05df02c0cff6e5acc5a19dcc6e"}, - {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ba23302c0c61a9999784e73809427c9dbedd79f66a13d84ad1b1943802eaaf59"}, - {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:6748dbf9bfa5ba1afcc7556b71cda0d7ce5f24768043a02a58846e4a443d808d"}, - {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0b0cad37311123211dc91eadcb322ef4d4a66008d3e1bdc404808992260e1a0e"}, - {file = "yarl-1.18.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0fb2171a4486bb075316ee754c6d8382ea6eb8b399d4ec62fde2b591f879778a"}, - {file = "yarl-1.18.3-cp311-cp311-win32.whl", hash = "sha256:61b1a825a13bef4a5f10b1885245377d3cd0bf87cba068e1d9a88c2ae36880e1"}, - {file = "yarl-1.18.3-cp311-cp311-win_amd64.whl", hash = "sha256:b9d60031cf568c627d028239693fd718025719c02c9f55df0a53e587aab951b5"}, - {file = "yarl-1.18.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1dd4bdd05407ced96fed3d7f25dbbf88d2ffb045a0db60dbc247f5b3c5c25d50"}, - {file = "yarl-1.18.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7c33dd1931a95e5d9a772d0ac5e44cac8957eaf58e3c8da8c1414de7dd27c576"}, - {file = "yarl-1.18.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:25b411eddcfd56a2f0cd6a384e9f4f7aa3efee14b188de13048c25b5e91f1640"}, - {file = "yarl-1.18.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:436c4fc0a4d66b2badc6c5fc5ef4e47bb10e4fd9bf0c79524ac719a01f3607c2"}, - {file = "yarl-1.18.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e35ef8683211db69ffe129a25d5634319a677570ab6b2eba4afa860f54eeaf75"}, - {file = "yarl-1.18.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:84b2deecba4a3f1a398df819151eb72d29bfeb3b69abb145a00ddc8d30094512"}, - {file = "yarl-1.18.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00e5a1fea0fd4f5bfa7440a47eff01d9822a65b4488f7cff83155a0f31a2ecba"}, - {file = "yarl-1.18.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d0e883008013c0e4aef84dcfe2a0b172c4d23c2669412cf5b3371003941f72bb"}, - {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5a3f356548e34a70b0172d8890006c37be92995f62d95a07b4a42e90fba54272"}, - {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ccd17349166b1bee6e529b4add61727d3f55edb7babbe4069b5764c9587a8cc6"}, - {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b958ddd075ddba5b09bb0be8a6d9906d2ce933aee81100db289badbeb966f54e"}, - {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c7d79f7d9aabd6011004e33b22bc13056a3e3fb54794d138af57f5ee9d9032cb"}, - {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:4891ed92157e5430874dad17b15eb1fda57627710756c27422200c52d8a4e393"}, - {file = "yarl-1.18.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ce1af883b94304f493698b00d0f006d56aea98aeb49d75ec7d98cd4a777e9285"}, - {file = "yarl-1.18.3-cp312-cp312-win32.whl", hash = "sha256:f91c4803173928a25e1a55b943c81f55b8872f0018be83e3ad4938adffb77dd2"}, - {file = "yarl-1.18.3-cp312-cp312-win_amd64.whl", hash = "sha256:7e2ee16578af3b52ac2f334c3b1f92262f47e02cc6193c598502bd46f5cd1477"}, - {file = "yarl-1.18.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:90adb47ad432332d4f0bc28f83a5963f426ce9a1a8809f5e584e704b82685dcb"}, - {file = "yarl-1.18.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:913829534200eb0f789d45349e55203a091f45c37a2674678744ae52fae23efa"}, - {file = "yarl-1.18.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ef9f7768395923c3039055c14334ba4d926f3baf7b776c923c93d80195624782"}, - {file = "yarl-1.18.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88a19f62ff30117e706ebc9090b8ecc79aeb77d0b1f5ec10d2d27a12bc9f66d0"}, - {file = "yarl-1.18.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e17c9361d46a4d5addf777c6dd5eab0715a7684c2f11b88c67ac37edfba6c482"}, - {file = "yarl-1.18.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1a74a13a4c857a84a845505fd2d68e54826a2cd01935a96efb1e9d86c728e186"}, - {file = "yarl-1.18.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:41f7ce59d6ee7741af71d82020346af364949314ed3d87553763a2df1829cc58"}, - {file = "yarl-1.18.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f52a265001d830bc425f82ca9eabda94a64a4d753b07d623a9f2863fde532b53"}, - {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:82123d0c954dc58db301f5021a01854a85bf1f3bb7d12ae0c01afc414a882ca2"}, - {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:2ec9bbba33b2d00999af4631a3397d1fd78290c48e2a3e52d8dd72db3a067ac8"}, - {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:fbd6748e8ab9b41171bb95c6142faf068f5ef1511935a0aa07025438dd9a9bc1"}, - {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:877d209b6aebeb5b16c42cbb377f5f94d9e556626b1bfff66d7b0d115be88d0a"}, - {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b464c4ab4bfcb41e3bfd3f1c26600d038376c2de3297760dfe064d2cb7ea8e10"}, - {file = "yarl-1.18.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8d39d351e7faf01483cc7ff7c0213c412e38e5a340238826be7e0e4da450fdc8"}, - {file = "yarl-1.18.3-cp313-cp313-win32.whl", hash = "sha256:61ee62ead9b68b9123ec24bc866cbef297dd266175d53296e2db5e7f797f902d"}, - {file = "yarl-1.18.3-cp313-cp313-win_amd64.whl", hash = "sha256:578e281c393af575879990861823ef19d66e2b1d0098414855dd367e234f5b3c"}, - {file = "yarl-1.18.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:61e5e68cb65ac8f547f6b5ef933f510134a6bf31bb178be428994b0cb46c2a04"}, - {file = "yarl-1.18.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fe57328fbc1bfd0bd0514470ac692630f3901c0ee39052ae47acd1d90a436719"}, - {file = "yarl-1.18.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a440a2a624683108a1b454705ecd7afc1c3438a08e890a1513d468671d90a04e"}, - {file = "yarl-1.18.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09c7907c8548bcd6ab860e5f513e727c53b4a714f459b084f6580b49fa1b9cee"}, - {file = "yarl-1.18.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b4f6450109834af88cb4cc5ecddfc5380ebb9c228695afc11915a0bf82116789"}, - {file = "yarl-1.18.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9ca04806f3be0ac6d558fffc2fdf8fcef767e0489d2684a21912cc4ed0cd1b8"}, - {file = "yarl-1.18.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77a6e85b90a7641d2e07184df5557132a337f136250caafc9ccaa4a2a998ca2c"}, - {file = "yarl-1.18.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6333c5a377c8e2f5fae35e7b8f145c617b02c939d04110c76f29ee3676b5f9a5"}, - {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0b3c92fa08759dbf12b3a59579a4096ba9af8dd344d9a813fc7f5070d86bbab1"}, - {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:4ac515b860c36becb81bb84b667466885096b5fc85596948548b667da3bf9f24"}, - {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:045b8482ce9483ada4f3f23b3774f4e1bf4f23a2d5c912ed5170f68efb053318"}, - {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:a4bb030cf46a434ec0225bddbebd4b89e6471814ca851abb8696170adb163985"}, - {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:54d6921f07555713b9300bee9c50fb46e57e2e639027089b1d795ecd9f7fa910"}, - {file = "yarl-1.18.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1d407181cfa6e70077df3377938c08012d18893f9f20e92f7d2f314a437c30b1"}, - {file = "yarl-1.18.3-cp39-cp39-win32.whl", hash = "sha256:ac36703a585e0929b032fbaab0707b75dc12703766d0b53486eabd5139ebadd5"}, - {file = "yarl-1.18.3-cp39-cp39-win_amd64.whl", hash = "sha256:ba87babd629f8af77f557b61e49e7c7cac36f22f871156b91e10a6e9d4f829e9"}, - {file = "yarl-1.18.3-py3-none-any.whl", hash = "sha256:b57f4f58099328dfb26c6a771d09fb20dbbae81d20cfb66141251ea063bd101b"}, - {file = "yarl-1.18.3.tar.gz", hash = "sha256:ac1801c45cbf77b6c99242eeff4fffb5e4e73a800b5c4ad4fc0be5def634d2e1"}, -] - -[package.dependencies] -idna = ">=2.0" -multidict = ">=4.0" -propcache = ">=0.2.0" - -[metadata] -lock-version = "2.1" -python-versions = "^3.12" -content-hash = "1756fb94573e8776d4e8b711c9e95b6a1ba43e5d7b31954267cdb43eea3110e6" diff --git a/projects/dotnet_service/.dockerignore b/projects/dotnet_service/.dockerignore new file mode 100644 index 0000000..a6fc51c --- /dev/null +++ b/projects/dotnet_service/.dockerignore @@ -0,0 +1,24 @@ +**/.dockerignore +**/.env +**/.git +**/.gitignore +**/.project +**/.settings +**/.toolstarget +**/.vs +**/.vscode +**/*.*proj.user +**/*.dbmdl +**/*.jfm +**/azds.yaml +**/bin +**/charts +**/docker-compose* +**/Dockerfile* +**/node_modules +**/npm-debug.log +**/obj +**/secrets.dev.yaml +**/values.dev.yaml +LICENSE +README.md \ No newline at end of file diff --git a/projects/dotnet_service/Controllers/DecompilerController.cs b/projects/dotnet_service/Controllers/DecompilerController.cs new file mode 100644 index 0000000..464f5a3 --- /dev/null +++ b/projects/dotnet_service/Controllers/DecompilerController.cs @@ -0,0 +1,128 @@ +using Dapr; +using Dapr.Client; +using ILSpyDecompilerService.Models; +using ILSpyDecompilerService.Services; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; +using System; +using System.IO; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; + +namespace ILSpyDecompilerService.Controllers +{ + [ApiController] + [Route("[controller]")] + public class DecompilerController : ControllerBase + { + private readonly ILogger _logger; + private readonly DaprClient _daprClient; + private readonly MinioService _minioService; + private readonly DecompilerEngine _decompilerEngine; + private readonly AssemblyAnalysisService _assemblyAnalysisService; + private readonly SemaphoreSlim _processingSemaphore; + private const string PubSubName = "pubsub"; + private const string OutputTopicName = "dotnet-output"; + + public DecompilerController( + ILogger logger, + DaprClient daprClient, + MinioService minioService, + DecompilerEngine decompilerEngine, + AssemblyAnalysisService assemblyAnalysisService, + IConfiguration configuration) + { + _logger = logger; + _daprClient = daprClient; + _minioService = minioService; + _decompilerEngine = decompilerEngine; + _assemblyAnalysisService = assemblyAnalysisService; + + // Get max concurrent processing from environment variable, default to 5 + var maxConcurrentProcessing = configuration.GetValue("MAX_CONCURRENT_PROCESSING", 5); + _processingSemaphore = new SemaphoreSlim(maxConcurrentProcessing, maxConcurrentProcessing); + _logger.LogInformation("Maximum concurrent processing set to: {MaxConcurrentProcessing}", maxConcurrentProcessing); + } + + [Topic(PubSubName, "dotnet-input")] + [HttpPost("process")] + public async Task ProcessDecompilationRequest([FromBody] InputMessage inputMessage) + { + var rawObjectJson = JsonConvert.SerializeObject(inputMessage, Formatting.Indented); + // _logger.LogDebug("Raw input object: {RawObject}", rawObjectJson); + + // Wait for semaphore to limit concurrent processing + await _processingSemaphore.WaitAsync(); + + string downloadedFilePath = null; + string outputDirectory = null; + string zipFilePath = null; + + try + { + _logger.LogInformation("Processing decompilation request for object ID: {ObjectId}", inputMessage.ObjectId); + + if (string.IsNullOrEmpty(inputMessage.ObjectId)) + { + _logger.LogError("Invalid input message - ObjectId is null or empty"); + return BadRequest("ObjectId is required"); + } + + var objectId = inputMessage.ObjectId; + + // Download file from Minio + downloadedFilePath = await _minioService.DownloadFileAsync(objectId); + _logger.LogDebug("File downloaded to: {downloadedFilePath}", downloadedFilePath); + + // Perform assembly analysis + _logger.LogInformation("Starting assembly analysis for object ID: {ObjectId}", objectId); + var analysisResult = _assemblyAnalysisService.AnalyzeAssembly(downloadedFilePath); + _logger.LogDebug("Assembly analysis completed for: {ObjectId}", objectId); + + // Decompile assembly + outputDirectory = Path.Combine(Path.GetTempPath(), $"{objectId}_source"); + await _decompilerEngine.DecompileAssemblyAsync(downloadedFilePath, outputDirectory); + _logger.LogDebug("File decompiled to: {outputDirectory}", outputDirectory); + + // Create ZIP file + var newObjectId = Guid.NewGuid().ToString(); + zipFilePath = Path.Combine(Path.GetTempPath(), newObjectId); + await _decompilerEngine.CreateZipFromDirectoryAsync(outputDirectory, zipFilePath); + + // Upload ZIP to Minio + await _minioService.UploadFileAsync(zipFilePath, newObjectId); + _logger.LogDebug("Zip uploaded to: {newObjectId}", newObjectId); + + // Publish result with both decompilation and analysis data + var outputMessage = new OutputMessage + { + ObjectId = objectId, + Decompilation = newObjectId, + Analysis = analysisResult + }; + + await _daprClient.PublishEventAsync(PubSubName, OutputTopicName, outputMessage); + + _logger.LogInformation("Successfully processed decompilation request and published result: {NewObjectId}", newObjectId); + + return Ok(new { success = true, outputId = newObjectId, analysisResult = analysisResult }); + } + catch (Exception ex) + { + _logger.LogError(ex, "Failed to process decompilation request for object ID: {ObjectId}", inputMessage?.ObjectId); + return StatusCode(500, new { success = false, error = ex.Message }); + } + finally + { + // Cleanup temporary files + _decompilerEngine.CleanupTemporaryFiles(downloadedFilePath, outputDirectory, zipFilePath); + + // Release semaphore + _processingSemaphore.Release(); + } + } + } +} \ No newline at end of file diff --git a/projects/dotnet_service/Dockerfile b/projects/dotnet_service/Dockerfile new file mode 100644 index 0000000..eea7204 --- /dev/null +++ b/projects/dotnet_service/Dockerfile @@ -0,0 +1,45 @@ + +# Build stage +FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build + +WORKDIR /src + +# Copy project file and restore dependencies (fix the path) +COPY ./projects/dotnet_service/ILSpyDecompilerService.csproj ./projects/dotnet_service/ +RUN dotnet restore ./projects/dotnet_service/ILSpyDecompilerService.csproj + +# Copy source code and build +COPY ./projects/dotnet_service/ ./projects/dotnet_service/ +WORKDIR /src/projects/dotnet_service +RUN dotnet build ILSpyDecompilerService.csproj -c Release -o /app/build + +# Publish stage +FROM build AS publish +RUN dotnet publish ILSpyDecompilerService.csproj -c Release -o /app/publish + +# Development stage (for hot reload and debugging) +FROM mcr.microsoft.com/dotnet/sdk:9.0 AS dev +WORKDIR /app +COPY ./projects/dotnet_service/ . +RUN dotnet restore +EXPOSE 5000 +ENTRYPOINT ["dotnet", "run", "--urls", "http://0.0.0.0:5000"] + +# Production runtime stage +FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS prod +WORKDIR /app + +RUN apt-get update && \ + apt-get install -y --no-install-recommends curl && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* + +COPY --from=publish /app/publish . + +# Create a non-root user +RUN groupadd -r appuser && useradd -r -g appuser appuser +RUN chown -R appuser:appuser /app +USER appuser + +EXPOSE 5000 +ENTRYPOINT ["dotnet", "ILSpyDecompilerService.dll", "--urls", "http://0.0.0.0:5000"] \ No newline at end of file diff --git a/projects/dotnet_service/ILSpyDecompilerService.csproj b/projects/dotnet_service/ILSpyDecompilerService.csproj new file mode 100644 index 0000000..6a307ef --- /dev/null +++ b/projects/dotnet_service/ILSpyDecompilerService.csproj @@ -0,0 +1,30 @@ + + + + Exe + net9.0 + true + true + true + en-US + false + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/projects/dotnet_service/Models/DecompilerModels.cs b/projects/dotnet_service/Models/DecompilerModels.cs new file mode 100644 index 0000000..3ac3ac1 --- /dev/null +++ b/projects/dotnet_service/Models/DecompilerModels.cs @@ -0,0 +1,55 @@ +using Newtonsoft.Json; +using System.Collections.Generic; + +namespace ILSpyDecompilerService.Models +{ + public class InputMessage + { + [JsonProperty("object_id")] + public string ObjectId { get; set; } + } + + public class OutputMessage + { + [JsonProperty("object_id")] + public string ObjectId { get; set; } + + [JsonProperty("decompilation")] + public string Decompilation { get; set; } + + [JsonProperty("analysis")] + public string Analysis { get; set; } + } + + public class AssemblyAnalysis + { + public string AssemblyName { get; set; } + public string Error { get; set; } + public string[] RemotingChannels { get; set; } + public bool IsWCFServer { get; set; } + public bool IsWCFClient { get; set; } + public Dictionary SerializationGadgetCalls { get; set; } + public Dictionary WcfServerCalls { get; set; } + public Dictionary ClientCalls { get; set; } + public Dictionary RemotingCalls { get; set; } + public Dictionary ExecutionCalls { get; set; } + } + + public class MethodInfo + { + public string MethodName { get; set; } + public string FilterLevel { get; set; } + } + + public class GadgetItem + { + public bool IsDotNetRemoting { get; set; } + public string RemotingChannel { get; set; } + public bool IsWCFServer { get; set; } + public bool IsWCFClient { get; set; } + public bool IsExecution { get; set; } + public string GadgetName { get; set; } + public string FilterLevel { get; set; } + public string MethodAppearance { get; set; } + } +} \ No newline at end of file diff --git a/projects/dotnet_service/Program.cs b/projects/dotnet_service/Program.cs new file mode 100644 index 0000000..04fd64e --- /dev/null +++ b/projects/dotnet_service/Program.cs @@ -0,0 +1,33 @@ +using Dapr.Client; +using ILSpyDecompilerService; +using ILSpyDecompilerService.Services; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.AspNetCore.Mvc; +using System; + +var builder = WebApplication.CreateBuilder(args); + +// Add services to the container +builder.Services.AddControllers().AddDapr(); + +builder.Services.AddControllers().AddNewtonsoftJson(); + +// Add our custom services +builder.Services.AddSingleton(); +builder.Services.AddSingleton(); +builder.Services.AddSingleton(); + +var app = builder.Build(); + +// Configure the HTTP request pipeline +app.UseRouting(); +app.UseCloudEvents(); +app.MapControllers(); +app.MapSubscribeHandler(); + +// Add health check endpoint +app.MapGet("/health", () => Results.Ok(new { status = "healthy", timestamp = DateTime.UtcNow })); + +app.Run(); \ No newline at end of file diff --git a/projects/dotnet_service/README.md b/projects/dotnet_service/README.md new file mode 100644 index 0000000..dfebb33 --- /dev/null +++ b/projects/dotnet_service/README.md @@ -0,0 +1,29 @@ +# DotNet Service + +Provides decompilation capabilities using the ILSpy decompiler engine as well as deserialization analysis with functionality pulled from InspectAssembly. + +Integrates directly with Dapr's pub/sub and Minio to cut down on process creates via Python. + +Original [ILSpy code](https://github.com/icsharpcode/ILSpy/tree/master/ICSharpCode.ILSpyCmd) was adapted (MIT license). + +Original [InspectAssembly code](https://github.com/matterpreter/OffensiveCSharp/tree/master/InspectAssembly) is by [@matterpreter](https://github.com/matterpreter/OffensiveCSharp/tree/master/InspectAssembly) under a BSD 3-Clause license. + + +## Features + +- Listens for decompilation requests via Dapr pub/sub +- Downloads .NET assemblies from Minio object storage +- Decompiles assemblies using the ILSpy decompiler engine natively +- Compresses output to ZIP files +- Uploads results back to Minio +- Analyzes the original assembly using InspectAssembly +- Publishes decompilation + analysis results via Dapr pub/sub + +## Environment Variables + +The following environment variables are required: + +- `MINIO_ENDPOINT` - Minio server endpoint (e.g., `http://minio:9000`) +- `MINIO_ACCESS_KEY` - Minio access key +- `MINIO_SECRET_KEY` - Minio secret key +- `MINIO_BUCKET` - Minio bucket name (e.g., `files`) diff --git a/projects/dotnet_service/Services/AssemblyAnalysisService.cs b/projects/dotnet_service/Services/AssemblyAnalysisService.cs new file mode 100644 index 0000000..39c6e53 --- /dev/null +++ b/projects/dotnet_service/Services/AssemblyAnalysisService.cs @@ -0,0 +1,319 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using ILSpyDecompilerService.Models; +using Microsoft.Extensions.Logging; +using Mono.Cecil; +using Newtonsoft.Json; + +namespace ILSpyDecompilerService.Services +{ + public class AssemblyAnalysisService + { + private readonly ILogger _logger; + + private const string BF_DESERIALIZE = "System.Runtime.Serialization.Formatters.Binary.BinaryFormatter::Deserialize"; + private const string DC_JSON_READ_OBJ = "System.Runtime.Serialization.Json.DataContractJsonSerializer::ReadObject"; + private const string DC_XML_READ_OBJ = "System.Runtime.Serialization.Xml.DataContractSerializer::ReadObject"; + private const string JS_SERIALIZER_DESERIALIZE = "System.Web.Script.Serialization.JavaScriptSerializer::Deserialize"; + private const string LOS_FORMATTER_DESERIALIZE = "System.Web.UI.LosFormatter::Deserialize"; + private const string NET_DATA_CONTRACT_READ_OBJ = "System.Runtime.Serialization.NetDataContractSerializer::ReadObject"; + private const string NET_DATA_CONTRACT_DESERIALIZE = "System.Runtime.Serialization.NetDataContractSerializer::Deserialize"; + private const string OBJ_STATE_FORMATTER_DESERIALIZE = "System.Web.UI.ObjectStateFormatter::Deserialize"; + private const string SOAP_FORMATTER_DESERIALIZE = "System.Runtime.Serialization.Formatters.Soap.SoapFormatter::Deserialize"; + private const string XML_SERIALIZER_DESERIALIZE = "System.Xml.Serialization.XmlSerializer::Deserialize"; + private const string REGISTER_CHANNEL = "System.Runtime.Remoting.Channels.ChannelServices::RegisterChannel"; + private const string WCF_SERVER_STRING = "System.ServiceModel.ServiceHost::AddServiceEndpoint"; + private const string WCF_CLIENT_STRING = "System.ServiceModel.ChannelFactory::CreateChannel"; + private const string JSCRIPT_EVALUATION = "Microsoft.JScript.Eval::JScriptEvaluate"; + private const string POWERSHELL_EVALUATION = "System.Management.Automation.Runspaces.Pipeline::Invoke"; + private const string PROCESS_START = "System.Diagnostics.Process::Start"; + + public AssemblyAnalysisService(ILogger logger) + { + _logger = logger; + } + + public string AnalyzeAssembly(string assemblyPath) + { + try + { + _logger.LogInformation($"Starting analysis of assembly: {assemblyPath}"); + + // Validate that the file is actually an assembly + try + { + AssemblyName assemblyName = AssemblyName.GetAssemblyName(assemblyPath); + } + catch + { + var errorResult = new AssemblyAnalysis + { + AssemblyName = Path.GetFileName(assemblyPath), + Error = $"file path {assemblyPath} is not an assembly" + }; + return JsonConvert.SerializeObject(errorResult); + } + + var result = AnalyzeAssemblyInternal(assemblyPath); + var json = JsonConvert.SerializeObject(result); + + _logger.LogInformation($"Analysis completed for assembly: {assemblyPath}"); + return json; + } + catch (Exception ex) + { + _logger.LogError(ex, $"Failed to analyze assembly: {assemblyPath}"); + var errorResult = new AssemblyAnalysis + { + AssemblyName = Path.GetFileName(assemblyPath), + Error = ex.Message + }; + return JsonConvert.SerializeObject(errorResult); + } + } + + private AssemblyAnalysis AnalyzeAssemblyInternal(string assemblyPath) + { + string[] dnrChannel = { }; + string typeFilterLevel = "ldc.i4.2"; + string filterLevel = "Low"; + List listGadgets = new List(); + + // Parse the target assembly and get its types + AssemblyDefinition assembly = AssemblyDefinition.ReadAssembly(assemblyPath); + IEnumerable allTypes = assembly.MainModule.GetTypes(); + + // Pull out all the types with methods that we want to look at + var validTypes = allTypes.SelectMany(t => t.Methods.Select(m => new { t, m })) + .Where(x => x.m.HasBody); + + foreach (var method in validTypes) + { + // Disassemble the assembly and check for potentially vulnerable functions + foreach (var instruction in method.m.Body.Instructions) + { + string gadgetName = ""; + bool isRemoting = false; + string remotingChannel = ""; + bool isWCFServer = false; + bool isWCFClient = false; + bool isExecution = false; + + // Deserialization checks + if (instruction.OpCode.ToString() == "callvirt") + { + switch (instruction.Operand.ToString()) + { + case string x when x.Contains(BF_DESERIALIZE): + gadgetName = BF_DESERIALIZE; + break; + case string x when x.Contains(DC_JSON_READ_OBJ): + gadgetName = DC_JSON_READ_OBJ; + break; + case string x when x.Contains(DC_XML_READ_OBJ): + gadgetName = DC_XML_READ_OBJ; + break; + case string x when x.Contains(JS_SERIALIZER_DESERIALIZE): + gadgetName = JS_SERIALIZER_DESERIALIZE; + break; + case string x when x.Contains(LOS_FORMATTER_DESERIALIZE): + gadgetName = LOS_FORMATTER_DESERIALIZE; + break; + case string x when x.Contains(NET_DATA_CONTRACT_READ_OBJ): + gadgetName = NET_DATA_CONTRACT_READ_OBJ; + break; + case string x when x.Contains(NET_DATA_CONTRACT_DESERIALIZE): + gadgetName = NET_DATA_CONTRACT_DESERIALIZE; + break; + case string x when x.Contains(OBJ_STATE_FORMATTER_DESERIALIZE): + gadgetName = OBJ_STATE_FORMATTER_DESERIALIZE; + break; + case string x when x.Contains(SOAP_FORMATTER_DESERIALIZE): + gadgetName = SOAP_FORMATTER_DESERIALIZE; + break; + case string x when x.Contains(XML_SERIALIZER_DESERIALIZE): + gadgetName = XML_SERIALIZER_DESERIALIZE; + break; + case string x when x.Contains(POWERSHELL_EVALUATION): + gadgetName = POWERSHELL_EVALUATION; + isExecution = true; + break; + case string x when x.Contains(WCF_SERVER_STRING): + gadgetName = WCF_SERVER_STRING; + isWCFServer = true; + break; + case string x when x.Contains("System.ServiceModel.ChannelFactory") && x.Contains("CreateChannel"): + gadgetName = WCF_CLIENT_STRING; + isWCFClient = true; + break; + case string x when x.Contains("set_FilterLevel(System.Runtime.Serialization.Formatters.TypeFilterLevel)"): + if (typeFilterLevel.EndsWith("3")) + { + filterLevel = "Full"; + } + break; + } + } + else if (instruction.OpCode.ToString().StartsWith("ldc.i4")) + { + typeFilterLevel = instruction.OpCode.ToString(); + } + else if (instruction.OpCode.ToString() == "newobj" && instruction.Operand.ToString().Contains("System.Runtime.Remoting.Channels.")) + { + // .NET Remoting Checks + dnrChannel = instruction.Operand.ToString().Split('.'); + } + else if (instruction.OpCode.ToString() == "call") + { + switch (instruction.Operand.ToString()) + { + case string x when x.Contains(JSCRIPT_EVALUATION): + gadgetName = JSCRIPT_EVALUATION; + isExecution = true; + break; + case string x when x.Contains(PROCESS_START): + gadgetName = PROCESS_START; + isExecution = true; + break; + case string x when x.Contains(REGISTER_CHANNEL): + isRemoting = true; + gadgetName = REGISTER_CHANNEL; + remotingChannel = dnrChannel.Length > 5 ? dnrChannel[5] : ""; + break; + } + } + + if (!string.IsNullOrEmpty(gadgetName) || isWCFClient || isWCFServer || isRemoting) + { + listGadgets.Add(new GadgetItem + { + GadgetName = gadgetName, + IsDotNetRemoting = isRemoting, + RemotingChannel = remotingChannel, + IsWCFClient = isWCFClient, + IsWCFServer = isWCFServer, + IsExecution = isExecution, + MethodAppearance = $"{method.t.Name}.{method.m.Name}", + FilterLevel = gadgetName.Contains(BF_DESERIALIZE) ? filterLevel : null + }); + } + } + } + + return CreateAssemblyAnalysis(Path.GetFileName(assemblyPath), listGadgets.ToArray()); + } + + private AssemblyAnalysis CreateAssemblyAnalysis(string assemblyName, GadgetItem[] items) + { + var analysis = new AssemblyAnalysis + { + AssemblyName = assemblyName, + IsWCFClient = false, + IsWCFServer = false, + SerializationGadgetCalls = new Dictionary(), + ClientCalls = new Dictionary(), + WcfServerCalls = new Dictionary(), + RemotingCalls = new Dictionary(), + ExecutionCalls = new Dictionary() + }; + + Dictionary> temp = new Dictionary>(); + Dictionary> tempClient = new Dictionary>(); + Dictionary> tempServer = new Dictionary>(); + Dictionary> tempRemoting = new Dictionary>(); + Dictionary> tempExecution = new Dictionary>(); + List dnRemotingChannels = new List(); + + foreach (var gadget in items) + { + if (gadget.IsWCFClient && !tempClient.ContainsKey(gadget.GadgetName)) + tempClient[gadget.GadgetName] = new List(); + else if (gadget.IsWCFServer && !tempServer.ContainsKey(gadget.GadgetName)) + tempServer[gadget.GadgetName] = new List(); + if (gadget.IsDotNetRemoting && !tempRemoting.ContainsKey(gadget.GadgetName)) + tempRemoting[gadget.GadgetName] = new List(); + if (gadget.IsExecution && !tempExecution.ContainsKey(gadget.GadgetName)) + tempExecution[gadget.GadgetName] = new List(); + else if (!temp.ContainsKey(gadget.GadgetName)) + temp[gadget.GadgetName] = new List(); + + if (gadget.IsWCFClient) + { + tempClient[gadget.GadgetName].Add(new Models.MethodInfo + { + MethodName = gadget.MethodAppearance, + FilterLevel = gadget.FilterLevel + }); + } + else if (gadget.IsWCFServer) + { + tempServer[gadget.GadgetName].Add(new Models.MethodInfo + { + MethodName = gadget.MethodAppearance, + FilterLevel = gadget.FilterLevel + }); + } + else if (gadget.IsDotNetRemoting) + { + tempRemoting[gadget.GadgetName].Add(new Models.MethodInfo + { + MethodName = gadget.MethodAppearance, + FilterLevel = gadget.FilterLevel + }); + } + else if (gadget.IsExecution) + { + tempExecution[gadget.GadgetName].Add(new Models.MethodInfo + { + MethodName = gadget.MethodAppearance, + FilterLevel = gadget.FilterLevel + }); + } + else + { + temp[gadget.GadgetName].Add(new Models.MethodInfo + { + MethodName = gadget.MethodAppearance, + FilterLevel = gadget.FilterLevel + }); + } + + if (gadget.IsDotNetRemoting) + dnRemotingChannels.Add(gadget.RemotingChannel); + } + + analysis.RemotingChannels = dnRemotingChannels.ToArray(); + + foreach (var key in temp.Keys) + { + if (!string.IsNullOrEmpty(key)) + analysis.SerializationGadgetCalls[key] = temp[key].ToArray(); + } + foreach (var key in tempClient.Keys) + { + if (!string.IsNullOrEmpty(key)) + analysis.ClientCalls[key] = tempClient[key].ToArray(); + } + foreach (var key in tempServer.Keys) + { + if (!string.IsNullOrEmpty(key)) + analysis.WcfServerCalls[key] = tempServer[key].ToArray(); + } + foreach (var key in tempRemoting.Keys) + { + if (!string.IsNullOrEmpty(key)) + analysis.RemotingCalls[key] = tempRemoting[key].ToArray(); + } + foreach (var key in tempExecution.Keys) + { + if (!string.IsNullOrEmpty(key)) + analysis.ExecutionCalls[key] = tempExecution[key].ToArray(); + } + + return analysis; + } + } +} \ No newline at end of file diff --git a/projects/dotnet_service/Services/DecompilerEngine.cs b/projects/dotnet_service/Services/DecompilerEngine.cs new file mode 100644 index 0000000..0c20403 --- /dev/null +++ b/projects/dotnet_service/Services/DecompilerEngine.cs @@ -0,0 +1,114 @@ +using ICSharpCode.Decompiler; +using ICSharpCode.Decompiler.CSharp; +using ICSharpCode.Decompiler.CSharp.ProjectDecompiler; +using ICSharpCode.Decompiler.Metadata; +using Microsoft.Extensions.Logging; +using System; +using System.IO; +using System.IO.Compression; +using System.Reflection.Metadata; +using System.Reflection.PortableExecutable; +using System.Threading.Tasks; + +namespace ILSpyDecompilerService +{ + public class DecompilerEngine + { + private readonly ILogger _logger; + + public DecompilerEngine(ILogger logger) + { + _logger = logger; + } + + public async Task DecompileAssemblyAsync(string assemblyPath, string outputDirectory) + { + try + { + _logger.LogInformation($"Starting decompilation of {assemblyPath}"); + + var module = new PEFile(assemblyPath); + var resolver = new UniversalAssemblyResolver(assemblyPath, false, module.Metadata.DetectTargetFrameworkId()); + + var decompilerSettings = new DecompilerSettings(LanguageVersion.Latest) + { + ThrowOnAssemblyResolveErrors = false, + RemoveDeadCode = false, + RemoveDeadStores = false, + UseSdkStyleProjectFormat = WholeProjectDecompiler.CanUseSdkStyleProjectFormat(module), + UseNestedDirectoriesForNamespaces = false, + }; + + var decompiler = new WholeProjectDecompiler(decompilerSettings, resolver, resolver, null); + + Directory.CreateDirectory(outputDirectory); + + string projectFileName = Path.Combine(outputDirectory, Path.GetFileNameWithoutExtension(assemblyPath) + ".csproj"); + + await Task.Run(() => + { + using (var projectFileWriter = new StreamWriter(File.OpenWrite(projectFileName))) + { + decompiler.DecompileProject(module, outputDirectory, projectFileWriter); + } + }); + + _logger.LogInformation($"Decompilation completed for {assemblyPath}"); + return outputDirectory; + } + catch (Exception ex) + { + _logger.LogError(ex, $"Failed to decompile assembly {assemblyPath}"); + throw; + } + } + + public async Task CreateZipFromDirectoryAsync(string sourceDirectory, string zipFilePath) + { + try + { + _logger.LogInformation($"Creating ZIP file from {sourceDirectory}"); + + await Task.Run(() => + { + if (File.Exists(zipFilePath)) + File.Delete(zipFilePath); + + ZipFile.CreateFromDirectory(sourceDirectory, zipFilePath); + }); + + _logger.LogInformation($"ZIP file created at {zipFilePath}"); + return zipFilePath; + } + catch (Exception ex) + { + _logger.LogError(ex, $"Failed to create ZIP file from {sourceDirectory}"); + throw; + } + } + + public void CleanupTemporaryFiles(params string[] paths) + { + foreach (var path in paths) + { + try + { + if (File.Exists(path)) + { + File.Delete(path); + _logger.LogDebug($"Deleted temporary file: {path}"); + } + else if (Directory.Exists(path)) + { + Directory.Delete(path, true); + _logger.LogDebug($"Deleted temporary directory: {path}"); + } + } + catch (Exception ex) + { + _logger.LogWarning(ex, $"Failed to cleanup temporary path: {path}"); + } + } + } + } +} \ No newline at end of file diff --git a/projects/dotnet_service/Services/MinioService.cs b/projects/dotnet_service/Services/MinioService.cs new file mode 100644 index 0000000..5c869dd --- /dev/null +++ b/projects/dotnet_service/Services/MinioService.cs @@ -0,0 +1,79 @@ +using Microsoft.Extensions.Logging; +using Minio; +using Minio.DataModel.Args; +using System; +using System.IO; +using System.Threading.Tasks; + +namespace ILSpyDecompilerService +{ + public class MinioService + { + private readonly IMinioClient _minioClient; + private readonly ILogger _logger; + private readonly string _bucketName; + + public MinioService(ILogger logger) + { + _logger = logger; + + var endpoint = Environment.GetEnvironmentVariable("MINIO_ENDPOINT") ?? throw new InvalidOperationException("MINIO_ENDPOINT environment variable is required"); + var accessKey = Environment.GetEnvironmentVariable("MINIO_ACCESS_KEY") ?? throw new InvalidOperationException("MINIO_ACCESS_KEY environment variable is required"); + var secretKey = Environment.GetEnvironmentVariable("MINIO_SECRET_KEY") ?? throw new InvalidOperationException("MINIO_SECRET_KEY environment variable is required"); + _bucketName = Environment.GetEnvironmentVariable("MINIO_BUCKET") ?? throw new InvalidOperationException("MINIO_BUCKET environment variable is required"); + + // Strip protocol from endpoint if present (MinIO client expects hostname:port format) + var cleanEndpoint = endpoint.Replace("http://", "").Replace("https://", ""); + + _minioClient = new MinioClient() + .WithEndpoint(cleanEndpoint) + .WithCredentials(accessKey, secretKey) + .Build(); + } + + public async Task DownloadFileAsync(string objectId) + { + try + { + var tempFilePath = Path.GetTempFileName(); + + var getObjectArgs = new GetObjectArgs() + .WithBucket(_bucketName) + .WithObject(objectId) + .WithFile(tempFilePath); + + await _minioClient.GetObjectAsync(getObjectArgs); + + _logger.LogInformation($"Downloaded file {objectId} to {tempFilePath}"); + return tempFilePath; + } + catch (Exception ex) + { + _logger.LogError(ex, $"Failed to download file {objectId}"); + throw; + } + } + + public async Task UploadFileAsync(string filePath, string newObjectId) + { + try + { + var putObjectArgs = new PutObjectArgs() + .WithBucket(_bucketName) + .WithObject(newObjectId) + .WithFileName(filePath) + .WithContentType("application/zip"); + + await _minioClient.PutObjectAsync(putObjectArgs); + + _logger.LogInformation($"Uploaded file {filePath} as {newObjectId}"); + return newObjectId; + } + catch (Exception ex) + { + _logger.LogError(ex, $"Failed to upload file {filePath} as {newObjectId}"); + throw; + } + } + } +} \ No newline at end of file diff --git a/projects/dotnet_service/global.json b/projects/dotnet_service/global.json new file mode 100644 index 0000000..2a896c8 --- /dev/null +++ b/projects/dotnet_service/global.json @@ -0,0 +1,7 @@ +{ + "sdk": { + "version": "9.0.302", + "rollForward": "major", + "allowPrerelease": true + } +} \ No newline at end of file diff --git a/projects/file_enrichment/.vscode/settings.json b/projects/file_enrichment/.vscode/settings.json index 34bd581..80e49a5 100644 --- a/projects/file_enrichment/.vscode/settings.json +++ b/projects/file_enrichment/.vscode/settings.json @@ -8,13 +8,12 @@ "[python]": { "editor.formatOnSave": true, "editor.codeActionsOnSave": { - "source.fixAll": "explicit", - "source.organizeImports": "explicit" + "source.fixAll.ruff": "explicit", + "source.organizeImports.ruff": "explicit" }, "editor.defaultFormatter": "charliermarsh.ruff" }, "autoDocstring.docstringFormat": "google", - "editor.formatOnSave": true, "files.exclude": { "**/.DS_Store": true, "**/.git": true, @@ -39,27 +38,18 @@ "**/*.egg-info/**": true, "**/build/**": true, "**/dist/**": true, - "**/node_modules/*/**": true, + "**/node_modules/*/**": true }, "python.analysis.diagnosticSeverityOverrides": { "reportMissingImports": "none", - "reportMissingModuleSource": "none", + "reportMissingModuleSource": "none" }, "python.analysis.useLibraryCodeForTypes": true, // Pyright - // "python.formatting.provider": "none", "python.languageServer": "Pylance", - "python.linting.enabled": true, - "python.linting.flake8Args": [ - "--max-line-length=240", - "--ignore=E203,E722,W503", - ], - "python.linting.flake8Enabled": true, - "python.linting.lintOnSave": true, - "python.linting.pylintEnabled": false, "python.testing.pytestArgs": [ "tests" ], "python.testing.unittestEnabled": false, "python.testing.pytestEnabled": true, - "python.analysis.typeCheckingMode": "basic", + "python.analysis.typeCheckingMode": "basic" } \ No newline at end of file diff --git a/projects/file_enrichment/Dockerfile b/projects/file_enrichment/Dockerfile index 6f59360..857e9cb 100644 --- a/projects/file_enrichment/Dockerfile +++ b/projects/file_enrichment/Dockerfile @@ -8,8 +8,8 @@ FROM ${PYTHON_BASE_DEV_IMAGE} AS base # that this project uses RUN apt-get update && \ - apt-get install -y libmagic1 libpq5 \ - gcc libc6-dev curl && \ + apt-get install -y libmagic1 libpq5 binutils \ + gcc g++ libc6-dev curl && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* @@ -64,6 +64,9 @@ ENV PYTHONUNBUFFERED=1 # No .pyc/pycache files ENV PYTHONDONTWRITEBYTECODE=1 +ENV PYTHONDEVMODE=1 +ENV PYTHONASYNCIODEBUG=1 + ENV LOG_LEVEL=DEBUG ENV UVICORN_HOST="0.0.0.0" @@ -79,7 +82,7 @@ ENTRYPOINT ["/bin/sh", "-c", " \ --workers ${UVICORN_WORKERS} \ --reload \ --reload-dir ${UVICORN_RELOAD_DIR} \ -"] + "] ######################## # Production ######################## @@ -94,9 +97,9 @@ FROM ${PYTHON_BASE_PROD_IMAGE} AS prod RUN apt-get update && \ apt-get install -y \ - libmagic1 libpq5 \ - binutils \ - && \ + libmagic1 libpq5 \ + binutils \ + && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* @@ -109,10 +112,11 @@ RUN mkdir -p /yara_rules/ # Uvicorn production settings ENV UVICORN_HOST=0.0.0.0 \ UVICORN_PORT=8001 \ - UVICORN_WORKERS=2 \ + UVICORN_WORKERS=1 \ UVICORN_PROXY_HEADERS=1 \ UVICORN_ACCESS_LOG=false + # TODO: Re-enable when we're ready for release # USER nemesis @@ -123,4 +127,4 @@ ENTRYPOINT ["/bin/sh", "-c", "\ --workers ${UVICORN_WORKERS} \ --proxy-headers \ --no-access-log \ -"] + "] diff --git a/projects/file_enrichment/README.md b/projects/file_enrichment/README.md index 3d4df43..572e8c2 100644 --- a/projects/file_enrichment/README.md +++ b/projects/file_enrichment/README.md @@ -55,9 +55,7 @@ The service includes specialized modules for: Environment variables for tuning performance: -- `MAX_PARALLEL_WORKFLOWS`: Maximum concurrent file processing workflows (default: 3) - `MAX_WORKFLOW_EXECUTION_TIME`: Workflow timeout in seconds (default: 300) -- `MAX_PARALLEL_ENRICHMENT_MODULES`: Concurrent modules per workflow (default: 5) - `WORKFLOW_RUNTIME_LOG_LEVEL`: Workflow engine logging level (default: WARNING) ## Workflow Process diff --git a/projects/file_enrichment/file_enrichment/activities/__init__.py b/projects/file_enrichment/file_enrichment/activities/__init__.py new file mode 100644 index 0000000..4dc5ca8 --- /dev/null +++ b/projects/file_enrichment/file_enrichment/activities/__init__.py @@ -0,0 +1,17 @@ +"""Workflow activities for file enrichment.""" + +from .basic_analysis import get_basic_analysis +from .enrichment_modules import run_enrichment_modules +from .file_linkings import check_file_linkings +from .plaintext_handler import handle_file_if_plaintext +from .publish_enriched import publish_enriched_file +from .publish_findings import publish_findings_alerts + +__all__ = [ + "get_basic_analysis", + "check_file_linkings", + "publish_findings_alerts", + "handle_file_if_plaintext", + "publish_enriched_file", + "run_enrichment_modules", +] diff --git a/projects/file_enrichment/file_enrichment/activities/basic_analysis.py b/projects/file_enrichment/file_enrichment/activities/basic_analysis.py new file mode 100644 index 0000000..5acfa72 --- /dev/null +++ b/projects/file_enrichment/file_enrichment/activities/basic_analysis.py @@ -0,0 +1,160 @@ +"""Basic file analysis activity.""" + +import json +import pathlib +import posixpath +from datetime import datetime + +import common.helpers as helpers +import magic +from common.helpers import get_file_extension, is_container +from common.logger import get_logger +from common.workflows.setup import workflow_activity +from dapr.ext.workflow.workflow_activity_context import WorkflowActivityContext + +from .. import global_vars + +logger = get_logger(__name__) + + +@workflow_activity +async def get_basic_analysis(ctx: WorkflowActivityContext, activity_input): + """ + Perform 'basic' analysis on a file and save to database. Run for every file. + + This activity downloads the file, processes it to extract metadata, + and saves the results to the database. + """ + object_id = activity_input["object_id"] + + with global_vars.storage.download(object_id) as file: + file_enriched = process_basic_analysis(file.name, activity_input) + await save_file_enriched_to_db(file_enriched) + + return file_enriched + + +def parse_timestamp(ts): + """Parse a timestamp string or return the datetime object as-is.""" + if isinstance(ts, str): + return datetime.fromisoformat(ts.replace("Z", "+00:00")) + return ts + + +def process_basic_analysis(temp_file_path: str, activity_input: dict) -> dict: + """ + Process a file and extract basic metadata including hashes, mime type, etc. + + Args: + temp_file_path: Path to the temporary file to analyze + activity_input: Dictionary containing file metadata (object_id, path, etc.) + + Returns: + Dictionary with all file enrichment data (activity_input merged with basic_analysis) + """ + path = activity_input.get("path", "") + + mime_type = magic.from_file(temp_file_path, mime=True) + if mime_type == "text/plain" or helpers.is_text_file(temp_file_path): + is_plaintext = True + else: + is_plaintext = False + + basic_analysis = { + "file_name": posixpath.basename(path), + "extension": get_file_extension(path), + "size": pathlib.Path(temp_file_path).stat().st_size, + "hashes": { + "md5": helpers.calculate_file_hash(temp_file_path, "md5"), + "sha1": helpers.calculate_file_hash(temp_file_path, "sha1"), + "sha256": helpers.calculate_file_hash(temp_file_path, "sha256"), + }, + "magic_type": magic.from_file(temp_file_path), + "mime_type": mime_type, + "is_plaintext": is_plaintext, + "is_container": is_container(mime_type), + } + + file_enriched = { + **activity_input, + **basic_analysis, + } + + return file_enriched + + +async def save_file_enriched_to_db(file_enriched: dict) -> None: + """ + Save file enrichment data to the PostgreSQL database. + + Args: + file_enriched: Dictionary containing all file enrichment data + """ + try: + async with global_vars.asyncpg_pool.acquire() as conn: + # Convert field names to match database schema + insert_query = """ + INSERT INTO files_enriched ( + object_id, agent_id, source, project, timestamp, expiration, path, + file_name, extension, size, magic_type, mime_type, + is_plaintext, is_container, originating_object_id, originating_container_id, + nesting_level, file_creation_time, file_access_time, + file_modification_time, security_info, hashes + ) VALUES ( + $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, + $17, $18, $19, $20, $21, $22 + ) + ON CONFLICT (object_id) DO UPDATE SET + agent_id = EXCLUDED.agent_id, + source = EXCLUDED.source, + project = EXCLUDED.project, + timestamp = EXCLUDED.timestamp, + expiration = EXCLUDED.expiration, + path = EXCLUDED.path, + file_name = EXCLUDED.file_name, + extension = EXCLUDED.extension, + size = EXCLUDED.size, + magic_type = EXCLUDED.magic_type, + mime_type = EXCLUDED.mime_type, + is_plaintext = EXCLUDED.is_plaintext, + is_container = EXCLUDED.is_container, + originating_object_id = EXCLUDED.originating_object_id, + originating_container_id = EXCLUDED.originating_container_id, + nesting_level = EXCLUDED.nesting_level, + file_creation_time = EXCLUDED.file_creation_time, + file_access_time = EXCLUDED.file_access_time, + file_modification_time = EXCLUDED.file_modification_time, + security_info = EXCLUDED.security_info, + hashes = EXCLUDED.hashes, + updated_at = CURRENT_TIMESTAMP + """ + + await conn.execute( + insert_query, + file_enriched["object_id"], + file_enriched.get("agent_id"), + file_enriched.get("source"), + file_enriched.get("project"), + parse_timestamp(file_enriched.get("timestamp")), + parse_timestamp(file_enriched.get("expiration")), + file_enriched.get("path"), + file_enriched.get("file_name"), + file_enriched.get("extension"), + file_enriched.get("size"), + file_enriched.get("magic_type"), + file_enriched.get("mime_type"), + file_enriched.get("is_plaintext"), + file_enriched.get("is_container"), + file_enriched.get("originating_object_id"), + file_enriched.get("originating_container_id"), + file_enriched.get("nesting_level"), + parse_timestamp(file_enriched.get("file_creation_time")), + parse_timestamp(file_enriched.get("file_access_time")), + parse_timestamp(file_enriched.get("file_modification_time")), + json.dumps(file_enriched.get("security_info")) if file_enriched.get("security_info") else None, + json.dumps(file_enriched.get("hashes")) if file_enriched.get("hashes") else None, + ) + logger.debug("Stored file_enriched in PostgreSQL", object_id=file_enriched["object_id"]) + except Exception as e: + logger.exception(e, message="Error storing file_enriched in PostgreSQL", file_enriched=file_enriched) + raise diff --git a/projects/file_enrichment/file_enrichment/activities/enrichment_modules.py b/projects/file_enrichment/file_enrichment/activities/enrichment_modules.py new file mode 100644 index 0000000..7e45cfa --- /dev/null +++ b/projects/file_enrichment/file_enrichment/activities/enrichment_modules.py @@ -0,0 +1,209 @@ +"""Enrichment modules activity.""" + +import json +import os + +import common.helpers as helpers +import file_enrichment.global_vars as global_vars +from common.logger import get_logger +from common.models import EnrichmentResult +from common.workflows.setup import workflow_activity +from dapr.ext.workflow.workflow_activity_context import WorkflowActivityContext + +from .. import global_vars +from ..tracing import get_tracer + +logger = get_logger(__name__) + +# Global module map - will be set during initialization + + +@workflow_activity +async def run_enrichment_modules(ctx: WorkflowActivityContext, activity_input: dict): + """Activity that runs all enrichment modules for a file with single file download.""" + + object_id = activity_input["object_id"] + execution_order = activity_input["execution_order"] + + tracer = get_tracer() + + with tracer.start_as_current_span("run_enrichment_modules") as span: + span.set_attribute("object_id", object_id) + span.set_attribute("module_count", len(execution_order)) + + logger.info("Starting enrichment modules processing", object_id=object_id, execution_order=execution_order) + + results = [] + + try: + with global_vars.storage.download(object_id) as temp_file: + logger.debug( + "Downloaded file for processing", + object_id=object_id, + temp_file=temp_file.name, + size=os.path.getsize(temp_file.name), + ) + + modules_to_process = determine_modules_to_process(object_id, temp_file.name, execution_order) + span.set_attribute("modules_to_process_count", len(modules_to_process)) + + for module_name in modules_to_process: + # Create a span for each module execution + with tracer.start_as_current_span(f"enrichment.{module_name}") as module_span: + module_span.set_attribute("module.name", module_name) + module_span.set_attribute("object_id", object_id) + + try: + result = await execute_enrichment_module(object_id, temp_file.name, module_name) + + if result: + # Store enrichment result directly in database + await store_enrichment_results(object_id, module_name, result) + + results.append((module_name, {"status": "success", "module": module_name})) + logger.debug("Module completed successfully", module_name=module_name) + module_span.set_attribute("module.status", "success") + else: + results.append((module_name, None)) + logger.debug("Module returned no result", module_name=module_name) + module_span.set_attribute("module.status", "no_result") + + except Exception as e: + logger.exception( + "Error in enrichment module", module_name=module_name, object_id=object_id, error=str(e) + ) + + # Update workflow in database with failed module + await record_module_failure(object_id, module_name, e) + + results.append((module_name, None)) + module_span.set_attribute("module.status", "error") + module_span.set_attribute("module.error", str(e)[:200]) + # Continue with other modules instead of raising + + except Exception as e: + logger.exception("Error in run_enrichment_modules", object_id=object_id, error=str(e)) + span.set_attribute("error", True) + span.set_attribute("error.message", str(e)[:200]) + raise + + logger.debug("Enrichment modules processing completed", object_id=object_id, total_modules=len(results)) + span.set_attribute("total_results", len(results)) + return results + + +def determine_modules_to_process(object_id: str, temp_file_path: str, execution_order: list[str]) -> list[str]: + """First pass: determine which modules should process this file.""" + modules_to_process = [] + + for module_name in execution_order: + if module_name not in global_vars.global_module_map: + logger.warning("Module not found", module_name=module_name) + continue + + module = global_vars.global_module_map[module_name] + try: + should_process = module.should_process(object_id, temp_file_path) + + if should_process: + modules_to_process.append(module_name) + except Exception as e: + logger.exception("Error in should_process", module_name=module_name, error=str(e)) + + logger.info("Modules selected for processing", object_id=object_id, modules_to_process=modules_to_process) + return modules_to_process + + +async def execute_enrichment_module(object_id: str, temp_file_path: str, module_name: str) -> EnrichmentResult | None: + """Second pass: process a single module and return its result.""" + module = global_vars.global_module_map[module_name] + logger.debug("Starting module processing", module_name=module_name) + + # Check if the module's process method returns a coroutine (async) + result_or_coro = module.process(object_id, temp_file_path) + if hasattr(result_or_coro, "__await__"): + # It's a coroutine, await it + result: EnrichmentResult = await result_or_coro + else: + # It's a synchronous result + result: EnrichmentResult = result_or_coro + + return result + + +async def store_enrichment_results(object_id: str, module_name: str, result: EnrichmentResult): + """Store enrichment results, transforms, and findings in the database.""" + async with global_vars.asyncpg_pool.acquire() as conn: + # Store enrichment + results_escaped = json.dumps(helpers.sanitize_for_jsonb(result.model_dump(mode="json"))) + await conn.execute( + """ + INSERT INTO enrichments (object_id, module_name, result_data) + VALUES ($1, $2, $3) + """, + object_id, + module_name, + results_escaped, + ) + + # Store any transforms + if result.transforms: + for transform in result.transforms: + await conn.execute( + """ + INSERT INTO transforms (object_id, type, transform_object_id, metadata) + VALUES ($1, $2, $3, $4) + """, + object_id, + transform.type, + transform.object_id, + json.dumps(transform.metadata) if transform.metadata else None, + ) + + # Store any findings + if result.findings: + for finding in result.findings: + await conn.execute( + """ + INSERT INTO findings ( + finding_name, category, severity, object_id, + origin_type, origin_name, raw_data, data + ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8) + """, + finding.finding_name, + finding.category, + finding.severity, + object_id, + finding.origin_type, + finding.origin_name, + json.dumps(finding.raw_data), + json.dumps([obj.model_dump_json() for obj in finding.data]), + ) + + # Update workflow in database with successful module + await conn.execute( + """ + UPDATE workflows + SET enrichments_success = array_append(enrichments_success, $1) + WHERE object_id = $2 + """, + module_name, + object_id, + ) + + +async def record_module_failure(object_id: str, module_name: str, error: Exception): + """Record a module failure in the database.""" + try: + async with global_vars.asyncpg_pool.acquire() as conn: + await conn.execute( + """ + UPDATE workflows + SET enrichments_failure = array_append(enrichments_failure, $1) + WHERE object_id = $2 + """, + f"{module_name}:{str(error)[:100]}", + object_id, + ) + except Exception as db_error: + logger.error(f"Failed to update workflow failure in database: {str(db_error)}") diff --git a/projects/file_enrichment/file_enrichment/activities/file_linkings.py b/projects/file_enrichment/file_enrichment/activities/file_linkings.py new file mode 100644 index 0000000..91426b5 --- /dev/null +++ b/projects/file_enrichment/file_enrichment/activities/file_linkings.py @@ -0,0 +1,32 @@ +"""File linking activity.""" + +from common.logger import get_logger +from common.state_helpers import get_file_enriched_async +from common.workflows.setup import workflow_activity +from dapr.ext.workflow.workflow_activity_context import WorkflowActivityContext + +from .. import global_vars + +logger = get_logger(__name__) + + +@workflow_activity +async def check_file_linkings(ctx: WorkflowActivityContext, activity_input): + """ + Check for file linkings using the rules engine and update database tables. + """ + + object_id = activity_input["object_id"] + file_enriched = await get_file_enriched_async(object_id, global_vars.asyncpg_pool) + + try: + linkings_created = await global_vars.file_linking_engine.apply_linking_rules(file_enriched) + + logger.debug("File linking check complete", object_id=object_id, linkings_created=linkings_created) + + return {"linkings_created": linkings_created} + + except Exception as e: + logger.exception("Error in file linking check", object_id=object_id, error=str(e)) + # Don't raise to ensure workflow can complete + return {"linkings_created": 0, "error": str(e)} diff --git a/projects/file_enrichment/file_enrichment/activities/plaintext_handler.py b/projects/file_enrichment/file_enrichment/activities/plaintext_handler.py new file mode 100644 index 0000000..963163f --- /dev/null +++ b/projects/file_enrichment/file_enrichment/activities/plaintext_handler.py @@ -0,0 +1,83 @@ +"""Plaintext file handling activity.""" + +import io +import json + +from common.helpers import create_text_reader +from common.logger import get_logger +from common.models import NoseyParkerInput +from common.state_helpers import get_file_enriched_async +from common.workflows.setup import workflow_activity +from dapr.clients import DaprClient +from dapr.ext.workflow.workflow_activity_context import WorkflowActivityContext + +from .. import global_vars +from ..tracing import get_trace_injector + +logger = get_logger(__name__) + + +@workflow_activity +async def handle_file_if_plaintext(ctx: WorkflowActivityContext, activity_input): + """ + Activity to index a file's contents if it's plaintext and + send a pub/sub message to NoseyParker + """ + object_id = activity_input["object_id"] + file_enriched = await get_file_enriched_async(object_id, global_vars.asyncpg_pool) + + # if the file is plaintext, make sure we index it + if file_enriched.is_plaintext: + with global_vars.storage.download(object_id) as tmp_file: + with open(tmp_file.name, "rb") as binary_file: + with create_text_reader(binary_file) as text_file: + await index_plaintext_content(f"{object_id}", text_file) + + nosey_parker_input = NoseyParkerInput(object_id=object_id) + with DaprClient(headers_callback=get_trace_injector()) as client: + client.publish_event( + pubsub_name="pubsub", + topic_name="noseyparker-input", + data=json.dumps(nosey_parker_input.model_dump()), + data_content_type="application/json", + ) + logger.debug(f"Published noseyparker_input: {object_id}") + + +async def index_plaintext_content(object_id: str, file_obj: io.TextIOWrapper, max_chunk_bytes: int = 800000): + """Used to index plaintext content with byte-based chunking to avoid tsvector limits""" + logger.debug(f"indexing plaintext for {object_id}") + + async with global_vars.asyncpg_pool.acquire() as conn: + await conn.execute("DELETE FROM plaintext_content WHERE object_id = $1", object_id) + + chunk_number = 0 + insert_query = """ + INSERT INTO plaintext_content (object_id, chunk_number, content) + VALUES ($1, $2, $3); + """ + + # Read file content + file_content = file_obj.read() + + # Process in chunks, ensuring we don't exceed byte limits + i = 0 + while i < len(file_content): + # Take a chunk that's guaranteed to be under the byte limit + chunk_end = min(i + max_chunk_bytes // 4, len(file_content)) # Div by 4 for worst-case UTF-8 + chunk_content = file_content[i:chunk_end] + + # If chunk is still too big in bytes, trim it down + while len(chunk_content.encode("utf-8")) > max_chunk_bytes and chunk_content: + chunk_content = chunk_content[:-100] # Remove 100 chars at a time + + if chunk_content: # Only insert non-empty chunks + actual_bytes = len(chunk_content.encode("utf-8")) + logger.debug(f"Inserting chunk {chunk_number} with {actual_bytes} bytes") + await conn.execute(insert_query, object_id, chunk_number, chunk_content) + chunk_number += 1 + + # Move to next chunk + i = chunk_end + + logger.debug("Indexed chunked content", object_id=object_id, num_chunks=chunk_number) diff --git a/projects/file_enrichment/file_enrichment/activities/publish_enriched.py b/projects/file_enrichment/file_enrichment/activities/publish_enriched.py new file mode 100644 index 0000000..c7d6b42 --- /dev/null +++ b/projects/file_enrichment/file_enrichment/activities/publish_enriched.py @@ -0,0 +1,44 @@ +"""Publish enriched file activity.""" + +import json + +from common.logger import get_logger +from common.state_helpers import get_file_enriched_async +from common.workflows.setup import workflow_activity +from dapr.clients import DaprClient +from dapr.ext.workflow.workflow_activity_context import WorkflowActivityContext + +from ..tracing import get_trace_injector + +logger = get_logger(__name__) + + +@workflow_activity +async def publish_enriched_file(ctx: WorkflowActivityContext, activity_input): + """ + Activity to publish enriched file data to pubsub after retrieving from state store. + """ + object_id = activity_input["object_id"] + file_enriched = await get_file_enriched_async(object_id) + + try: + with DaprClient(headers_callback=get_trace_injector()) as client: + data = file_enriched.model_dump( + exclude_unset=True, + mode="json", + ) + + # Publish to pubsub + client.publish_event( + pubsub_name="pubsub", + topic_name="file_enriched", + data=json.dumps(data), + data_content_type="application/json", + ) + + return True + + except Exception as e: + logger.exception(e, message="Error publishing enriched file data", object_id=object_id) + # Don't raise to ensure workflow can complete + return False diff --git a/projects/file_enrichment/file_enrichment/activities/publish_findings.py b/projects/file_enrichment/file_enrichment/activities/publish_findings.py new file mode 100644 index 0000000..667a25a --- /dev/null +++ b/projects/file_enrichment/file_enrichment/activities/publish_findings.py @@ -0,0 +1,78 @@ +"""Publish findings and alerts activity.""" + +import json + +import common.helpers as helpers +from common.logger import get_logger +from common.models import Alert +from common.state_helpers import get_file_enriched_async +from common.workflows.setup import workflow_activity +from dapr.clients import DaprClient +from dapr.ext.workflow.workflow_activity_context import WorkflowActivityContext + +from .. import global_vars +from ..tracing import get_trace_injector + +logger = get_logger(__name__) + + +@workflow_activity +async def publish_findings_alerts(ctx: WorkflowActivityContext, activity_input): + """ + Activity to publish enriched file data to pubsub after retrieving from state store. + """ + object_id = activity_input["object_id"] + file_enriched = await get_file_enriched_async(object_id) + + # Fetch findings from the database for this object_id + async with global_vars.asyncpg_pool.acquire() as conn: + findings = await conn.fetch( + """ + SELECT finding_name, category, severity, origin_name, raw_data + FROM findings + WHERE object_id = $1 + """, + object_id, + ) + + if findings: + with DaprClient(headers_callback=get_trace_injector()) as client: + if file_enriched.path: + file_path = helpers.sanitize_file_path(file_enriched.path) + else: + file_path = "UNKNOWN" + + for finding in findings: + finding_name = finding["finding_name"] + category = finding["category"] + severity = finding["severity"] + origin_name = finding["origin_name"] + raw_data = finding["raw_data"] + + finding_message = f"- *Category:* {category} / *Severity:* {severity}\n" + file_message = f"- *File Path:* {file_path}\n" + nemesis_finding_url = f"{global_vars.nemesis_url}findings?object_id={file_enriched.object_id}" + nemesis_file_url = f"{global_vars.nemesis_url}files?object_id={file_enriched.object_id}" + nemesis_footer_finding = f"*<{nemesis_finding_url}|View Finding in Nemesis>* / " + nemesis_footer_file = f"*<{nemesis_file_url}|View File in Nemesis>*\n" + separator = "⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯" + + rule_message = "" + try: + if finding_name == "noseyparker_match" and raw_data: + if "match" in raw_data and "rule_name" in raw_data["match"]: + rule_name = raw_data["match"]["rule_name"] + rule_message = f"- *Rule name:* {rule_name}\n" + except (json.JSONDecodeError, KeyError) as e: + logger.warning("Error processing raw_data for noseyparker_match", error=str(e)) + + body = f"{finding_message}{rule_message}{file_message}{nemesis_footer_finding}{nemesis_footer_file}{separator}" + + alert = Alert(title=finding_name, body=body, service=origin_name) + client.publish_event( + pubsub_name="pubsub", + topic_name="alert", + data=json.dumps(alert.model_dump(exclude_unset=True)), + data_content_type="application/json", + ) + logger.debug("Published alert", alert=alert) diff --git a/projects/file_enrichment/file_enrichment/controller.py b/projects/file_enrichment/file_enrichment/controller.py index f686aae..4b4ab8d 100644 --- a/projects/file_enrichment/file_enrichment/controller.py +++ b/projects/file_enrichment/file_enrichment/controller.py @@ -1,1059 +1,161 @@ -# src/workflow/controller.py import asyncio -import json import os -import time -import uuid from contextlib import asynccontextmanager -from datetime import datetime -from typing import Optional -import common.helpers as helpers -import structlog -from common.models import CloudEvent, File, NoseyParkerOutput +import asyncpg +from common.db import get_postgres_connection_str +from common.logger import get_logger +from common.models import BulkEnrichmentEvent, CloudEvent, DotNetOutput, File, NoseyParkerOutput +from common.workflows.setup import set_fastapi_loop from dapr.clients import DaprClient from dapr.ext.fastapi import DaprApp -from dapr.ext.workflow.workflow_state import WorkflowStatus -from fastapi import Body, FastAPI, HTTPException, Path -from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor -from psycopg_pool import ConnectionPool -from pydantic import BaseModel +from fastapi import FastAPI +from file_enrichment.postgres_notifications import postgres_notify_listener +from file_enrichment.workflow_recovery import recover_interrupted_workflows +from file_linking import FileLinkingEngine +from nemesis_dpapi import DpapiManager as NemesisDpapiManager +from nemesis_dpapi.eventing import DaprDpapiEventPublisher -from file_enrichment.noseyparker import store_noseyparker_results +from . import global_vars +from .debug_utils import setup_debug_signals +from .routes.dpapi import dpapi_background_monitor, dpapi_router +from .routes.enrichments import router as enrichments_router +from .subscriptions.bulk_enrichment import process_bulk_enrichment_event +from .subscriptions.dotnet import process_dotnet_event +from .subscriptions.file import process_file_event +from .subscriptions.noseyparker import process_noseyparker_event +from .workflow import initialize_workflow_runtime, wf_runtime +from .workflow_manager import WorkflowManager -from .logger import configure_logging, get_tracer -from .workflow import ( - enrichment_workflow, - get_workflow_client, - initialize_workflow_runtime, - reload_yara_rules, - shutdown_workflow_runtime, - workflow_runtime, -) +logger = get_logger(__name__) -configure_logging() -logger = structlog.get_logger(module=__name__) -tracer = get_tracer(__name__, os.getenv("NEMESIS_MONITORING", "").lower() == "enabled") - -max_parallel_workflows = int(os.getenv("MAX_PARALLEL_WORKFLOWS", 3)) # maximum workflows that can run at a time max_workflow_execution_time = int( os.getenv("MAX_WORKFLOW_EXECUTION_TIME", 300) ) # maximum time (in seconds) until a workflow is killed -logger.info(f"max_parallel_workflows: {max_parallel_workflows}") -logger.info(f"max_workflow_execution_time: {max_workflow_execution_time}") - -with DaprClient() as client: - secret = client.get_secret(store_name="nemesis-secret-store", key="POSTGRES_CONNECTION_STRING") - postgres_connection_string = secret.secret["POSTGRES_CONNECTION_STRING"] - -pool = ConnectionPool( - postgres_connection_string, min_size=max_parallel_workflows, max_size=(3 * max_parallel_workflows) -) - - -class EnrichmentRequest(BaseModel): - object_id: str - - -class WorkflowManager: - """Manages workflow execution with a simple queue system""" - - def __init__(self, max_concurrent=3, max_execution_time=300): - """Initialize the workflow manager""" - self.active_workflows = set() # Set of active workflow IDs - self.workflow_queue = asyncio.Queue() # Queue for pending workflows - self.max_concurrent = max_concurrent # Maximum concurrent workflows - self.lock = asyncio.Lock() # For synchronizing access to shared state - self.max_execution_time = max_execution_time # max time (in seconds) until a workflow is killed - - logger.info("WorkflowManager initialized", max_concurrent=max_concurrent, max_execution_time=max_execution_time) - - def _get_status_string(self, state_obj): - """Convert workflow state to string""" - if state_obj.runtime_status == WorkflowStatus.FAILED: - logger.warning( - "Workflow failed", - instance_id=state_obj.instance_id, - error=state_obj.failure_details.message if state_obj.failure_details else "Unknown", - ) - - return state_obj.runtime_status.name - - async def update_workflow_status(self, instance_id, status, runtime_seconds=None, error_message=None): - """ - Generalized function to update workflow status in database. - - Args: - instance_id: The workflow instance ID - status: The status to set (COMPLETED, FAILED, ERROR, TIMEOUT, etc.) - runtime_seconds: Runtime in seconds (optional) - error_message: Error message to append to enrichments_failure (optional) - """ - - def _update_workflow_in_db(): - with pool.connection() as conn: - with conn.cursor() as cur: - if error_message: - # Update with error message appended to enrichments_failure - cur.execute( - """ - UPDATE workflows - SET status = %s, - runtime_seconds = COALESCE(%s, runtime_seconds), - enrichments_failure = array_append(enrichments_failure, %s) - WHERE wf_id = %s - """, - (status, runtime_seconds, error_message[:100], instance_id), - ) - else: - # Update without modifying enrichments_failure - cur.execute( - """ - UPDATE workflows - SET status = %s, - runtime_seconds = COALESCE(%s, runtime_seconds) - WHERE wf_id = %s - """, - (status, runtime_seconds, instance_id), - ) - conn.commit() - - try: - await asyncio.to_thread(_update_workflow_in_db) - logger.debug( - "Updated workflow status", - instance_id=instance_id, - status=status, - runtime_seconds=runtime_seconds, - has_error=bool(error_message), - ) - except Exception as e: - logger.error( - "Failed to update workflow status in database", instance_id=instance_id, status=status, error=str(e) - ) - - async def reset(self): - """Reset the workflow manager's state.""" - async with self.lock: - # Clear active workflows - self.active_workflows.clear() - - # Clear the workflow queue - while not self.workflow_queue.empty(): - try: - self.workflow_queue.get_nowait() - self.workflow_queue.task_done() - except asyncio.QueueEmpty: - break - - # Reset workflows in database - try: - - def reset_db_workflows(): - with pool.connection() as conn: - with conn.cursor() as cur: - # Clear existing workflows - cur.execute("DELETE FROM workflows") - conn.commit() - - await asyncio.to_thread(reset_db_workflows) - except Exception as e: - logger.exception(e, message="Error resetting workflows in database") - - logger.warning( - "WorkflowManager reset", active_count=len(self.active_workflows), queue_size=self.workflow_queue.qsize() - ) - - return { - "status": "success", - "message": "Workflow manager reset successfully", - "timestamp": datetime.now().isoformat(), - } - - async def start_workflow(self, workflow_input): - """Start a workflow or queue it if at capacity""" - async with self.lock: - # Check if we're at capacity - if len(self.active_workflows) >= self.max_concurrent: - # Queue the workflow and return - await self.workflow_queue.put(workflow_input) - logger.info( - "Queued workflow - at capacity", - queue_size=self.workflow_queue.qsize(), - object_id=workflow_input["file"].get("object_id"), - ) - return f"queued-{uuid.uuid4()}" - - # If not at capacity, then start the workflow immediately - try: - start_time = time.time() - - client = get_workflow_client() - if client is None: - raise ValueError("Workflow client is None") - - # Start the workflow - instance_id = client.schedule_new_workflow(workflow=enrichment_workflow, input=workflow_input) - - with tracer.start_as_current_span("start_workflow") as current_span: - # Add workflow ID to trace for Jaeger queries - current_span.set_attribute("workflow.instance_id", instance_id) - current_span.set_attribute("workflow.start", True) - current_span.set_attribute("workflow.type", "enrichment_workflow") - if "file" in workflow_input and "object_id" in workflow_input["file"]: - current_span.set_attribute("workflow.object_id", workflow_input["file"]["object_id"]) - - # Extract and store metadata for tracking - base_filename = None - object_id = None - if "file" in workflow_input: - if "path" in workflow_input["file"]: - filepath = workflow_input["file"]["path"] - base_filename = os.path.basename(filepath) - if "object_id" in workflow_input["file"]: - object_id = workflow_input["file"].get("object_id") - - # Store workflow in database - def store_workflow(): - with pool.connection() as conn: - with conn.cursor() as cur: - cur.execute( - """ - INSERT INTO workflows (wf_id, object_id, filename, status, start_time) - VALUES (%s, %s, %s, %s, %s) - """, - ( - instance_id, - object_id, - base_filename, - "RUNNING", - datetime.fromtimestamp(start_time), - ), - ) - conn.commit() - - await asyncio.to_thread(store_workflow) - - # Add to active set - self.active_workflows.add(instance_id) - - logger.info( - "Started workflow", - instance_id=instance_id, - object_id=object_id, - active_count=len(self.active_workflows), - ) - - # Start a task to monitor this workflow - asyncio.create_task(self._monitor_workflow(instance_id, start_time)) - - # Check queue for more work - self._check_queue() - - return instance_id - - except Exception as e: - logger.exception(e, message="Error starting workflow") - raise - - def _check_queue(self): - """Check if we can process more workflows from the queue""" - # Schedule as a task so it doesn't block - asyncio.create_task(self._process_queue()) - - async def _process_queue(self) -> None: - """Process pending workflows from the queue""" - - def store_workflow( - instance_id: str, - object_id: Optional[str], - base_filename: Optional[str], - start_time: float, - ) -> None: - with pool.connection() as conn: - with conn.cursor() as cur: - cur.execute( - """ - INSERT INTO workflows (wf_id, object_id, filename, status, start_time) - VALUES (%s, %s, %s, %s, %s) - """, - ( - instance_id, - object_id, - base_filename, - "RUNNING", - datetime.fromtimestamp(start_time), - ), - ) - conn.commit() - - async with self.lock: - # Process as many as we can from the queue - while not self.workflow_queue.empty() and len(self.active_workflows) < self.max_concurrent: - try: - # Get the next workflow input - workflow_input: dict = self.workflow_queue.get_nowait() - - # Start the workflow - start_time: float = time.time() - client = get_workflow_client() - - instance_id: str = client.schedule_new_workflow(workflow=enrichment_workflow, input=workflow_input) - - # Add tracing for queued workflows - with tracer.start_as_current_span("store_workflow") as current_span: - current_span.set_attribute("workflow.instance_id", instance_id) - current_span.set_attribute("workflow.start", True) - current_span.set_attribute("workflow.type", "enrichment_workflow") - current_span.set_attribute("workflow.queued", True) - if "file" in workflow_input and "object_id" in workflow_input["file"]: - current_span.set_attribute("workflow.object_id", workflow_input["file"]["object_id"]) - - # Extract and store metadata for tracking - base_filename: Optional[str] = None - object_id: Optional[str] = None - if "file" in workflow_input: - if "path" in workflow_input["file"]: - filepath: str = workflow_input["file"]["path"] - base_filename = os.path.basename(filepath) - if "object_id" in workflow_input["file"]: - object_id = workflow_input["file"].get("object_id") - - # Store workflow in database - await asyncio.to_thread(store_workflow, instance_id, object_id, base_filename, start_time) - - # Add to active set - self.active_workflows.add(instance_id) - - logger.info( - "Started queued workflow", - instance_id=instance_id, - object_id=object_id, - queue_remaining=self.workflow_queue.qsize(), - ) - - # Create monitoring task - asyncio.create_task(self._monitor_workflow(instance_id, start_time)) - - # Mark as done - self.workflow_queue.task_done() - - except asyncio.QueueEmpty: - break - except Exception as e: - logger.exception(e, message="Error starting queued workflow") - self.workflow_queue.task_done() # Mark as done despite error - - async def _monitor_workflow(self, instance_id, start_time): - """Monitor a workflow until completion or timeout""" - - with tracer.start_as_current_span("monitor_workflow") as current_span: - current_span.set_attribute("workflow.instance_id", instance_id) - current_span.set_attribute("workflow.monitor", True) - - try: - # Wait for completion or timeout - try: - # Use wait_for to implement a timeout - final_status = await asyncio.wait_for( - self._wait_for_completion(instance_id), timeout=self.max_execution_time - ) - - processing_time = time.time() - start_time - - await self.update_workflow_status(instance_id, final_status, processing_time) - - # Remove from active set - async with self.lock: - if instance_id in self.active_workflows: - self.active_workflows.remove(instance_id) - - # Check if we can process more from queue - self._check_queue() - - logger.info( - "Workflow completed", - instance_id=instance_id, - processing_time=f"{processing_time:.2f}s", - final_status=final_status, - ) - - except TimeoutError: - processing_time = time.time() - start_time - - logger.warning( - "Workflow timed out after exceeding maximum execution time", - instance_id=instance_id, - max_execution_time=f"{self.max_execution_time}s", - actual_time=f"{processing_time:.2f}s", - ) - - # Try to terminate the workflow - try: - client = get_workflow_client() - if client: - client.terminate_workflow(instance_id) - logger.info("Workflow terminated due to timeout", instance_id=instance_id) - except Exception as e: - logger.error("Failed to terminate timed-out workflow", instance_id=instance_id, error=str(e)) - - # Update workflow status for timeout - await self.update_workflow_status(instance_id, "TIMEOUT", processing_time, "timeout") - - # Remove from active set - async with self.lock: - if instance_id in self.active_workflows: - self.active_workflows.remove(instance_id) - - # Check if we can process more from queue - self._check_queue() - - except Exception as e: - # Handle other failures - processing_time = time.time() - start_time - - logger.exception( - "Workflow monitoring failed", - instance_id=instance_id, - processing_time=f"{processing_time:.2f}s", - error=str(e), - ) - - # Update workflow status for error - await self.update_workflow_status(instance_id, "ERROR", processing_time, str(e)) - - # Remove from active set and check queue - async with self.lock: - if instance_id in self.active_workflows: - self.active_workflows.remove(instance_id) - - # Check if we can process more from queue - self._check_queue() - - async def _wait_for_completion(self, instance_id): - """Wait for workflow to complete and return the final status""" - start_time = datetime.now() - error_count = 0 - - client = get_workflow_client() - - # Add trace attributes for workflow status monitoring - with tracer.start_as_current_span("wait_for_completion") as current_span: - current_span.set_attribute("workflow.instance_id", instance_id) - current_span.set_attribute("workflow.wait_for_completion", True) - - while True: - try: - state = await asyncio.to_thread(client.get_workflow_state, instance_id) - status = self._get_status_string(state) - error_count = 0 # Reset on successful check - - logger.debug( - "Workflow status check", - instance_id=instance_id, - status=status, - runtime=str(datetime.now() - start_time), - ) - - if status in ["COMPLETED", "FAILED", "TERMINATED", "ERROR"]: - runtime_seconds = (datetime.now() - start_time).total_seconds() - logger.info( - "Workflow finished", - instance_id=instance_id, - final_status=status, - runtime=str(datetime.now() - start_time), - ) - - # For failed workflows, capture the error message and update status - if status in ["FAILED", "TERMINATED", "ERROR"]: - error_msg = "" - if status == "FAILED" and state.failure_details: - error_msg = state.failure_details.message - - await self.update_workflow_status( - instance_id, status, runtime_seconds, error_msg[:100] if error_msg else status.lower() - ) - else: - await self.update_workflow_status(instance_id, status, runtime_seconds, "") - - # Return the actual status so _monitor_workflow knows what happened - return status - - await asyncio.sleep(0.1) - - except Exception as e: - error_count += 1 - logger.warning( - f"Error checking workflow status: {str(e)}", instance_id=instance_id, error_count=error_count - ) - - if error_count >= 3: # Break after 3 consecutive errors - logger.error( - "Too many consecutive errors checking workflow status", - instance_id=instance_id, - error_count=error_count, - ) - # Return ERROR status so the monitoring can handle it appropriately - return "ERROR" - - await asyncio.sleep(0.5) - - async def get_status(self): - """Get current status information with enhanced metrics from database""" - try: - async with self.lock: - active_ids = list(self.active_workflows) - current_queue_size = self.workflow_queue.qsize() - - logger.debug("Getting status", active_count=len(active_ids), queue_size=current_queue_size) - - # Get metrics and workflow information from database - def get_db_metrics(): - with pool.connection() as conn: - with conn.cursor() as cur: - # Get metrics: counts and processing times - cur.execute(""" - SELECT - COUNT(*) FILTER (WHERE status = 'COMPLETED') as completed_count, - COUNT(*) FILTER (WHERE status IN ('FAILED', 'TERMINATED', 'ERROR', 'TIMEOUT')) as failed_count, - AVG(runtime_seconds) as avg_time, - MIN(runtime_seconds) as min_time, - MAX(runtime_seconds) as max_time, - COUNT(runtime_seconds) as samples_count - FROM workflows - WHERE runtime_seconds IS NOT NULL - """) - metrics_row = cur.fetchone() - completed_count, failed_count, avg_time, min_time, max_time, samples_count = metrics_row - - # Get active workflow details from database - cur.execute(""" - SELECT - wf_id, - object_id, - status, - EXTRACT(EPOCH FROM (CURRENT_TIMESTAMP - start_time)) as runtime_seconds, - enrichments_success, - enrichments_failure, - filename - FROM workflows - WHERE status = 'RUNNING' - """) - active_workflows_db = [] - for row in cur.fetchall(): - wf_id, object_id, status, runtime_seconds, success_modules, failure_modules, filename = row - - active_workflows_db.append( - { - "id": wf_id, - "status": status, - "runtime_seconds": runtime_seconds, - "filename": filename, - "object_id": object_id, - "success_modules": success_modules, - "failure_modules": failure_modules, - } - ) - - # Get status counts - cur.execute(""" - SELECT status, COUNT(*) FROM workflows GROUP BY status - """) - status_counts = {row[0]: row[1] for row in cur.fetchall()} - - # Calculate percentiles - percentiles = {} - if samples_count >= 5: - cur.execute(""" - SELECT - percentile_cont(0.5) WITHIN GROUP (ORDER BY runtime_seconds) as p50, - percentile_cont(0.9) WITHIN GROUP (ORDER BY runtime_seconds) as p90, - percentile_cont(0.95) WITHIN GROUP (ORDER BY runtime_seconds) as p95, - percentile_cont(0.99) WITHIN GROUP (ORDER BY runtime_seconds) as p99 - FROM workflows - WHERE runtime_seconds IS NOT NULL - """) - - result = cur.fetchone() - if result: - p50, p90, p95, p99 = result - percentiles = { - "p50_seconds": round(float(p50), 2) if p50 is not None else None, - "p90_seconds": round(float(p90), 2) if p90 is not None else None, - "p95_seconds": round(float(p95), 2) if p95 is not None else None, - "p99_seconds": round(float(p99), 2) if p99 is not None else None, - } - - return { - "metrics": { - "completed_count": completed_count or 0, - "failed_count": failed_count or 0, - "total_processed": (completed_count or 0) + (failed_count or 0), - "success_rate": round( - (completed_count or 0) / ((completed_count or 0) + (failed_count or 0)) * 100, 2 - ) - if ((completed_count or 0) + (failed_count or 0)) > 0 - else None, - "processing_times": { - "avg_seconds": round(avg_time, 2) if avg_time else None, - "min_seconds": round(min_time, 2) if min_time else None, - "max_seconds": round(max_time, 2) if max_time else None, - "samples_count": samples_count or 0, - **percentiles, - }, - }, - "status_counts": status_counts, - "active_workflows_db": active_workflows_db, - } - - # Get database metrics and status information - db_info = await asyncio.to_thread(get_db_metrics) - metrics = db_info["metrics"] - status_counts = db_info["status_counts"] - db_active_workflows = db_info["active_workflows_db"] - - # Get status for each active workflow from Dapr client as well (for comparison) - active_statuses = [] - if active_ids: - try: - client = get_workflow_client() - if client is None: - raise ValueError("Could not get workflow client") - - for wf_id in active_ids: - try: - state = client.get_workflow_state(wf_id) - status = self._get_status_string(state) - # Get runtime for active workflows - if hasattr(state, "created_at") and state.created_at: - runtime = datetime.now() - state.created_at - runtime_seconds = runtime.total_seconds() - else: - runtime_seconds = None - - # Find this workflow in our DB results - matching_db_workflow = next((w for w in db_active_workflows if w["id"] == wf_id), None) - - # Combine information from both sources - workflow_info = {"id": wf_id, "status": status, "runtime_seconds": runtime_seconds} - - if matching_db_workflow: - workflow_info["object_id"] = matching_db_workflow["object_id"] - workflow_info["filename"] = matching_db_workflow["filename"] - workflow_info["success_modules"] = matching_db_workflow["success_modules"] - workflow_info["failure_modules"] = matching_db_workflow["failure_modules"] - - active_statuses.append(workflow_info) - except Exception as e: - logger.error("Error getting workflow state", workflow_id=wf_id, error=str(e)) - active_statuses.append({"id": wf_id, "status": "ERROR", "error": str(e)}) - except Exception as e: - logger.error("Error getting workflow client", error=str(e)) - # Use only database-based active workflows - active_statuses = db_active_workflows - - # If we have no active statuses from the client but do have from DB, use DB ones - if not active_statuses and db_active_workflows: - active_statuses = db_active_workflows - - status = { - "queued_files": current_queue_size, - "active_workflows": len(active_ids), - "status_counts": status_counts, - "active_details": active_statuses, - "metrics": metrics, - "timestamp": datetime.now().isoformat(), - } - - logger.debug("Status response", status=status) - return status - - except Exception as e: - logger.exception(e, message="Error getting workflow status") - # Try to get basic queue info even if other parts fail - try: - return { - "error": str(e), - "queued_files": self.workflow_queue.qsize(), - "active_workflows": len(self.active_workflows), - "active_details": [], - "metrics": {}, - "timestamp": datetime.now().isoformat(), - } - except Exception as ee: - return { - "error": f"Complete status failure: {ee}", - "queued_files": 0, - "active_workflows": 0, - "active_details": [], - "metrics": {}, - "timestamp": datetime.now().isoformat(), - } +logger.info(f"max_workflow_execution_time: {max_workflow_execution_time}", pid=os.getpid()) module_execution_order = [] workflow_manager: WorkflowManager = None +# Global tracking for bulk enrichment processes +bulk_enrichment_tasks = {} # {enrichment_name: task_info} +bulk_enrichment_lock = asyncio.Lock() + +postgres_notify_listener_task = None +background_dpapi_task = None + @asynccontextmanager async def lifespan(app: FastAPI): """FastAPI lifespan manager for workflow runtime setup/teardown""" - global module_execution_order, workflow_manager + global module_execution_order, workflow_manager, postgres_notify_listener_task, background_dpapi_task - logger.info("Initializing workflow runtime...") + logger.info("Initializing workflow runtime...", pid=os.getpid()) + + setup_debug_signals() + + app.state.event_loop = asyncio.get_running_loop() + set_fastapi_loop(asyncio.get_event_loop()) + + dapr_client = DaprClient() + postgres_connection_string = get_postgres_connection_str(dapr_client) + + global_vars.asyncpg_pool = await asyncpg.create_pool( + postgres_connection_string, + min_size=5, + max_size=15, + ) + + global_vars.file_linking_engine = FileLinkingEngine(global_vars.asyncpg_pool) + + dpapi_manager = NemesisDpapiManager( + storage_backend=global_vars.asyncpg_pool, + auto_decrypt=True, + publisher=DaprDpapiEventPublisher(dapr_client, loop=app.state.event_loop), + ) + await dpapi_manager.__aenter__() + app.state.dpapi_manager = dpapi_manager + + # Initialize workflow runtime and modules + module_execution_order = await initialize_workflow_runtime(dpapi_manager) + + # Wait a bit for runtime to initialize + await asyncio.sleep(5) try: - # Initialize workflow runtime and modules - module_execution_order = await initialize_workflow_runtime() - # Wait for runtime to initialize - await asyncio.sleep(5) + # Use async context manager for WorkflowManager + async with WorkflowManager( + pool=global_vars.asyncpg_pool, max_execution_time=max_workflow_execution_time + ) as wf_manager: + workflow_manager = wf_manager - # Test workflow client - client = get_workflow_client() - if client is None: - raise ValueError("Workflow client not available after initialization") + try: + # Start PostgreSQL NOTIFY listener in background + postgres_notify_listener_task = asyncio.create_task( + postgres_notify_listener(global_vars.asyncpg_pool, workflow_manager) + ) + logger.info("Started PostgreSQL NOTIFY listener task") - # Initialize workflow manager with our global ENV variables - workflow_manager = WorkflowManager( - max_concurrent=max_parallel_workflows, max_execution_time=max_workflow_execution_time - ) + # Start masterkey watcher in background + background_dpapi_task = asyncio.create_task(dpapi_background_monitor(app.state.dpapi_manager)) + logger.info("Started masterkey watcher task") - logger.info( - "Workflow runtime initialized successfully", - module_execution_order=module_execution_order, - client_available=client is not None, - ) + # Recover any interrupted workflows before starting normal processing + await recover_interrupted_workflows(global_vars.asyncpg_pool) - except Exception as e: - logger.error("Failed to initialize workflow runtime", error=str(e)) - raise + logger.info( + "Workflow runtime initialized successfully", + module_execution_order=module_execution_order, + pid=os.getpid(), + ) - yield + yield - # Cleanup - try: - logger.info("Shutting down workflow runtime...") - shutdown_workflow_runtime() - except Exception as e: - logger.error("Error during workflow runtime shutdown", error=str(e)) + finally: + logger.info("Shutting down workflow runtime...", pid=os.getpid()) + + # Cleanup DpapiManager + if hasattr(app.state, "dpapi_manager") and app.state.dpapi_manager: + logger.info("Closing DpapiManager...", pid=os.getpid()) + await app.state.dpapi_manager.__aexit__(None, None, None) + + # Cancel masterkey watcher task + if background_dpapi_task and not background_dpapi_task.done(): + logger.info("Cancelling masterkey watcher task...", pid=os.getpid()) + background_dpapi_task.cancel() + try: + await background_dpapi_task + except asyncio.CancelledError: + logger.info("Masterkey watcher task cancelled", pid=os.getpid()) + + # Cancel PostgreSQL NOTIFY listener + if postgres_notify_listener_task and not postgres_notify_listener_task.done(): + logger.info("Cancelling PostgreSQL NOTIFY listener...") + postgres_notify_listener_task.cancel() + try: + await postgres_notify_listener_task + except asyncio.CancelledError: + logger.info("PostgreSQL NOTIFY listener cancelled") + + if wf_runtime: + wf_runtime.shutdown() + + dapr_client.close() + + finally: + if global_vars.asyncpg_pool: + await global_vars.asyncpg_pool.close() + logger.info("AsyncPG pool closed", pid=os.getpid()) # Initialize FastAPI app with lifespan manager app = FastAPI(lifespan=lifespan) dapr_app = DaprApp(app) - -@dapr_app.subscribe(pubsub="pubsub", topic="file") -async def process_file(event: CloudEvent[File]): - """Handler for incoming file events""" - - try: - file = event.data - workflow_input = { - "file": file.model_dump(exclude_unset=True, mode="json"), - "execution_order": module_execution_order, - } - - await workflow_manager.start_workflow(workflow_input) - - except Exception as e: - logger.exception(e, message="Error processing file event", cloud_event=event) - - -@dapr_app.subscribe(pubsub="pubsub", topic="yara") -async def process_yara(event: CloudEvent): - """Handler Yara events""" - - try: - action = event.data["action"] - if action == "reload": - reload_yara_rules() - else: - logger.warning(f"Unsupported yara action: {action}") - except Exception as e: - logger.exception(e, message="Error processing Yara event", cloud_event=event) - - -@dapr_app.subscribe(pubsub="pubsub", topic="noseyparker-output") -async def process_nosey_parker_results(event: CloudEvent): - """Handler for incoming Nosey Parker scan results""" - try: - # Extract the raw data - raw_data = event.data - logger.warning("Received NoseyParker output event") - - # Try to parse the event data into our NoseyParkerOutput model - try: - # If it's already a dict, use the from_dict factory method - if isinstance(raw_data, dict): - nosey_output = NoseyParkerOutput.from_dict(raw_data) - # If it's a string, try to parse it as JSON - elif isinstance(raw_data, str): - import json - - parsed_data = json.loads(raw_data) - nosey_output = NoseyParkerOutput.from_dict(parsed_data) - else: - logger.warning(f"Unexpected data type: {type(raw_data)}") - return - - # Now process the properly parsed output - object_id = nosey_output.object_id - matches = nosey_output.scan_result.matches - stats = nosey_output.scan_result.stats - - logger.debug(f"Found {len(matches)} matches for object {object_id}") - - # Store the findings in the database using our helper function - await store_noseyparker_results( - object_id=object_id, - matches=matches, - scan_stats=stats, - postgres_connection_string=postgres_connection_string, - ) - - except Exception as parsing_error: - # If parsing fails, fall back to direct dictionary access - logger.warning(f"Error parsing NoseyParker output as model: {parsing_error}") - - if hasattr(raw_data, "get"): - object_id = raw_data.get("object_id") - scan_result = raw_data.get("scan_result", {}) - matches = scan_result.get("matches", []) - stats = scan_result.get("stats", {}) - - logger.debug(f"Using dict access: Found {len(matches)} matches for {object_id}") - - # Store the findings using direct dict access - await store_noseyparker_results( - object_id=f"{object_id}", - matches=matches, - scan_stats=stats, - postgres_connection_string=postgres_connection_string, - ) - - except Exception as e: - logger.exception(e, message="Error processing Nosey Parker output event") - - -@app.get("/status") -async def get_workflow_status(): - """Get current workflow system status.""" - try: - status = await workflow_manager.get_status() - return status - except Exception as e: - logger.exception(e, message="Error getting workflow status") - raise HTTPException(status_code=500, detail=f"Internal server error: {str(e)}") from e - - -@app.get("/llm_enrichments") -async def list_enabled_llm_enrichments(): - """List the enabled LLM enrichments based on environment variables.""" - try: - if not workflow_runtime or not workflow_runtime.modules: - raise HTTPException(status_code=503, detail="Workflow runtime or modules not initialized") - - llm_enrichments = [] - if os.getenv("RIGGING_GENERATOR_CREDENTIALS"): - llm_enrichments.append("llm_credential_analysis") - if os.getenv("RIGGING_GENERATOR_SUMMARY"): - llm_enrichments.append("text_summarizer") - if os.getenv("RIGGING_GENERATOR_TRIAGE"): - llm_enrichments.append("finding_triage") - - return {"modules": llm_enrichments} - - except Exception as e: - logger.exception(e, message="Error listing enabled LLM enrichment modules") - raise HTTPException(status_code=500, detail=f"Internal server error: {str(e)}") from e - - -@app.get("/enrichments") -async def list_enrichments(): - """List all available enrichment modules.""" - try: - if not workflow_runtime or not workflow_runtime.modules: - raise HTTPException(status_code=503, detail="Workflow runtime or modules not initialized") - - modules = list(workflow_runtime.modules.keys()) - return {"modules": modules} - - except Exception as e: - logger.exception(e, message="Error listing enrichment modules") - raise HTTPException(status_code=500, detail=f"Internal server error: {str(e)}") from e - - -@app.post("/enrichments/{enrichment_name}") -async def run_enrichment( - enrichment_name: str = Path(..., description="Name of the enrichment module to run"), - request: EnrichmentRequest = Body(..., description="The enrichment request containing the object ID"), -): - """Run a specific enrichment module directly.""" - - try: - # Check if module exists - if not workflow_runtime or not workflow_runtime.modules: - raise HTTPException(status_code=503, detail="Workflow runtime or modules not initialized") - - if enrichment_name not in workflow_runtime.modules: - raise HTTPException(status_code=404, detail=f"Enrichment module '{enrichment_name}' not found") - - # Get the module - module = workflow_runtime.modules[enrichment_name] - - # Check if we should process this file - run in thread since it might use sync operations - should_process = await asyncio.to_thread(module.should_process, request.object_id) - if not should_process: - return { - "status": "skipped", - "message": f"Module {enrichment_name} decided to skip processing", - "object_id": request.object_id, - "instance_id": "", - } - - # Process the file in a separate thread to avoid event loop conflicts - result = await asyncio.to_thread(module.process, request.object_id) - - if result: - # Store enrichment result in database - def store_results(): - with pool.connection() as conn: - with conn.cursor() as cur: - # Store main enrichment result - results_escaped = json.dumps(helpers.sanitize_for_jsonb(result.model_dump(mode="json"))) - cur.execute( - """ - INSERT INTO enrichments (object_id, module_name, result_data) - VALUES (%s, %s, %s) - """, - (request.object_id, enrichment_name, results_escaped), - ) - - # Store any transforms - if result.transforms: - for transform in result.transforms: - cur.execute( - """ - INSERT INTO transforms (object_id, type, transform_object_id, metadata) - VALUES (%s, %s, %s, %s) - """, - ( - request.object_id, - transform.type, - transform.object_id, - json.dumps(transform.metadata) if transform.metadata else None, - ), - ) - - # Store any findings - if result.findings: - for finding in result.findings: - cur.execute( - """ - INSERT INTO findings ( - finding_name, category, severity, object_id, - origin_type, origin_name, raw_data, data - ) VALUES (%s, %s, %s, %s, %s, %s, %s, %s) - """, - ( - finding.finding_name, - finding.category, - finding.severity, - request.object_id, - finding.origin_type, - finding.origin_name, - json.dumps(finding.raw_data), - json.dumps([obj.model_dump() for obj in finding.data]), - ), - ) - - conn.commit() - - # Run database operations in thread - await asyncio.to_thread(store_results) - - return { - "status": "success", - "message": f"Completed enrichment with module '{enrichment_name}'", - "object_id": request.object_id, - "instance_id": str(uuid.uuid4()), # Generate a unique instance ID - } - - except HTTPException: - raise - except Exception as e: - logger.exception( - e, message="Error running enrichment module", enrichment_name=enrichment_name, object_id=request.object_id - ) - raise HTTPException(status_code=500, detail=f"Internal server error: {str(e)}") from e - - -@app.get("/failed") -async def get_failed_workflows(): - """Get information about failed, error, and timed-out workflows from database.""" - try: - # Query failed workflows from database - def get_failed_workflows_from_db(): - with pool.connection() as conn: - with conn.cursor() as cur: - cur.execute(""" - SELECT - wf_id as id, - object_id, - status, - runtime_seconds, - start_time, - enrichments_failure, - filename - FROM workflows - WHERE status IN ('FAILED', 'ERROR', 'TIMEOUT', 'TERMINATED') - ORDER BY start_time DESC - LIMIT 100 - """) - columns = [desc[0] for desc in cur.description] - failed_workflows = [] - - for row in cur.fetchall(): - workflow_dict = dict(zip(columns, row)) - - # Convert datetime to string - if "start_time" in workflow_dict: - workflow_dict["timestamp"] = workflow_dict["start_time"].isoformat() - del workflow_dict["start_time"] - - # Add error from failure list if available - if workflow_dict.get("enrichments_failure") and len(workflow_dict["enrichments_failure"]) > 0: - workflow_dict["error"] = workflow_dict["enrichments_failure"][-1] # Most recent failure - - failed_workflows.append(workflow_dict) - - return failed_workflows - - failed_workflows = await asyncio.to_thread(get_failed_workflows_from_db) - - return { - "failed_count": len(failed_workflows), - "workflows": failed_workflows, - "timestamp": datetime.now().isoformat(), - } - except Exception as e: - logger.exception(e, message="Error getting failed workflow information") - raise HTTPException(status_code=500, detail=f"Internal server error: {str(e)}") from e - - -@app.post("/reset") -async def reset_workflow_manager(): - """Reset the workflow manager's state.""" - try: - if workflow_manager is None: - raise HTTPException(status_code=503, detail="Workflow manager not initialized") - - result = await workflow_manager.reset() - return result - except Exception as e: - logger.exception(e, message="Error resetting workflow manager") - raise HTTPException(status_code=500, detail=f"Internal server error: {str(e)}") from e +# region API Routers/Endpoints +app.include_router(dpapi_router) +app.include_router(enrichments_router) @app.api_route("/healthz", methods=["GET", "HEAD"]) @@ -1062,4 +164,33 @@ async def healthcheck(): return {"status": "healthy"} -FastAPIInstrumentor.instrument_app(app, excluded_urls="healthz") +# endregion + + +# region Dapr Subscriptions +@dapr_app.subscribe(pubsub="pubsub", topic="file") +async def process_file(event: CloudEvent[File]): + """Handler for incoming file events""" + global workflow_manager + await process_file_event(event.data, workflow_manager, module_execution_order) + + +@dapr_app.subscribe(pubsub="pubsub", topic="dotnet-output") +async def process_dotnet_results(event: CloudEvent[DotNetOutput]): + """Handler for incoming .NET processing results from the dotnet_service.""" + await process_dotnet_event(event.data) + + +@dapr_app.subscribe(pubsub="pubsub", topic="noseyparker-output") +async def process_nosey_parker_results(event: CloudEvent[NoseyParkerOutput]): + """Handler for incoming Nosey Parker scan results""" + await process_noseyparker_event(event.data) + + +@dapr_app.subscribe(pubsub="pubsub", topic="bulk-enrichment-task") +async def process_bulk_enrichment_task(event: CloudEvent[BulkEnrichmentEvent]): + """Handler for individual bulk enrichment tasks""" + global workflow_manager + import file_enrichment.global_vars as global_vars + + await process_bulk_enrichment_event(event.data, workflow_manager, global_vars.global_module_map) diff --git a/projects/file_enrichment/file_enrichment/debug_utils.py b/projects/file_enrichment/file_enrichment/debug_utils.py new file mode 100644 index 0000000..bfdbc48 --- /dev/null +++ b/projects/file_enrichment/file_enrichment/debug_utils.py @@ -0,0 +1,107 @@ +"""Debug utilities for identifying asyncio blocking issues.""" + +import asyncio +import faulthandler +import os +import signal +import sys +import threading +import traceback +from datetime import datetime + +from common.logger import get_logger + +logger = get_logger(__name__) + + +def dump_all_stacks(sig=None, frame=None): + """Dump stack traces of all threads when SIGUSR1 is received.""" + logger.warning("=" * 80) + logger.warning(f"STACK TRACE DUMP - {datetime.now().isoformat()}") + logger.warning(f"PID: {os.getpid()}") + logger.warning("=" * 80) + + # Dump all thread stacks + for thread_id, frame in sys._current_frames().items(): + thread_name = None + for thread in threading.enumerate(): + if thread.ident == thread_id: + thread_name = thread.name + break + + logger.warning(f"\nThread {thread_id} ({thread_name}):") + for line in traceback.format_stack(frame): + logger.warning(line.strip()) + + # Dump asyncio tasks if available + try: + loop = asyncio.get_running_loop() + all_tasks = asyncio.all_tasks(loop) + + logger.warning("\n" + "=" * 80) + logger.warning(f"ASYNCIO TASKS ({len(all_tasks)} total)") + logger.warning("=" * 80) + + for i, task in enumerate(all_tasks): + logger.warning(f"\nTask {i}: {task.get_name()}") + logger.warning(f" Done: {task.done()}") + logger.warning(f" Cancelled: {task.cancelled()}") + + try: + coro = task.get_coro() + if coro.cr_frame: + logger.warning(f" Coroutine: {coro.__name__}") + logger.warning(f" File: {coro.cr_frame.f_code.co_filename}:{coro.cr_frame.f_lineno}") + logger.warning(f" Function: {coro.cr_frame.f_code.co_name}") + + # Print the stack of the coroutine + stack = traceback.format_stack(coro.cr_frame) + logger.warning(" Stack:") + for line in stack: + logger.warning(f" {line.strip()}") + except Exception as e: + logger.warning(f" Error getting coroutine info: {e}") + + except RuntimeError: + logger.warning("No asyncio event loop running") + + logger.warning("\n" + "=" * 80) + logger.warning("END STACK TRACE DUMP") + logger.warning("=" * 80) + + +def dump_blocking_threads(): + """Identify threads that might be blocking.""" + logger.warning("\nBLOCKING THREAD ANALYSIS:") + + for thread in threading.enumerate(): + logger.warning(f"\nThread: {thread.name} (daemon={thread.daemon})") + logger.warning(f" Alive: {thread.is_alive()}") + + # Check if thread is in a blocking state + if thread.ident: + frame = sys._current_frames().get(thread.ident) + if frame: + # Look for common blocking patterns + code = frame.f_code + if "wait" in code.co_name or "lock" in code.co_name or "result" in code.co_name: + logger.warning(f" ⚠️ POTENTIALLY BLOCKING: {code.co_filename}:{frame.f_lineno} in {code.co_name}") + + +def setup_debug_signals(): + """Setup signal handlers for debugging.""" + + # Enable faulthandler to dump on SIGSEGV + faulthandler.enable() + + # Dump all stacks on SIGUSR1 + signal.signal(signal.SIGUSR1, dump_all_stacks) + + # Dump blocking threads on SIGUSR2 + signal.signal(signal.SIGUSR2, lambda sig, frame: dump_blocking_threads()) + + logger.info( + "Debug signal handlers installed", + pid=os.getpid(), + usage="Kill -USR1 to dump all stacks, kill -USR2 to analyze blocking threads", + ) diff --git a/projects/file_enrichment/file_enrichment/file_feature_extractor.py b/projects/file_enrichment/file_enrichment/file_feature_extractor.py index 99a66a6..5b93e86 100644 --- a/projects/file_enrichment/file_enrichment/file_feature_extractor.py +++ b/projects/file_enrichment/file_enrichment/file_feature_extractor.py @@ -7,12 +7,10 @@ import re import statistics from collections import Counter from datetime import datetime -from typing import Optional -import structlog - -logger = structlog.get_logger(module=__name__) +from common.logger import get_logger +logger = get_logger(__name__) # unix epoch for a default DEFAULT_TIMESTAMP = datetime(1970, 1, 1, 0, 0, 0, tzinfo=datetime.now().astimezone().tzinfo) @@ -593,9 +591,9 @@ class FileFeatureExtractor: filepath: str, size: int, sibling_data: dict, - created_time: Optional[str] = None, - modified_time: Optional[str] = None, - accessed_time: Optional[str] = None, + created_time: str | None = None, + modified_time: str | None = None, + accessed_time: str | None = None, ) -> dict[str, float]: features = {} @@ -692,16 +690,16 @@ class FileFeatureExtractor: filepath: str, size: int, population_stats: dict, - created_time: Optional[str] = None, - modified_time: Optional[str] = None, - accessed_time: Optional[str] = None, + created_time: str | None = None, + modified_time: str | None = None, + accessed_time: str | None = None, ) -> dict[str, float]: """ Extract population-based features including time patterns """ features = {} - file_name = ntpath.basename(filepath) + # file_name = ntpath.basename(filepath) dir_path = ntpath.dirname(filepath) dir_path_lower = dir_path.lower() @@ -854,9 +852,9 @@ class FileFeatureExtractor: self, filepath: str, size: int, - created_time: Optional[str] = None, - modified_time: Optional[str] = None, - accessed_time: Optional[str] = None, + created_time: str | None = None, + modified_time: str | None = None, + accessed_time: str | None = None, ) -> dict[str, float]: """ Extract features for an individual file from file metadata. @@ -923,7 +921,7 @@ class FileFeatureExtractor: "numbers_in_file_name": len(re.findall(r"\d", file_name)), "numbers_in_dir_path": len(re.findall(r"\d", dir_path)), "file_name_naming_convention": self._get_naming_convention(file_name), - "file_name_naming_convention": self._get_naming_convention(dir_path), + "dir_name_naming_convention": self._get_naming_convention(dir_path), "file_name_has_date_pattern": int(self._has_date_pattern(file_name)), "dir_path_has_date_pattern": int(self._has_date_pattern(dir_path)), } @@ -1225,7 +1223,7 @@ class FileFeatureExtractor: @staticmethod def compute_sibling_data( - target_file: dict, sibling_files: list[dict], known_sensitive: Optional[set[str]] = None + target_file: dict, sibling_files: list[dict], known_sensitive: set[str] | None = None ) -> dict: """ Compute statistics about sibling files in the same directory. diff --git a/projects/file_enrichment/file_enrichment/global_vars.py b/projects/file_enrichment/file_enrichment/global_vars.py new file mode 100644 index 0000000..fa91732 --- /dev/null +++ b/projects/file_enrichment/file_enrichment/global_vars.py @@ -0,0 +1,33 @@ +# src/workflow/workflow.py +import asyncio +import os + +import asyncpg +import dapr.ext.workflow as wf +from common.logger import WORKFLOW_CLIENT_LOG_LEVEL +from common.storage import StorageMinio +from dapr.ext.workflow.logger.options import LoggerOptions +from file_enrichment_modules.module_loader import EnrichmentModule +from file_linking import FileLinkingEngine + +workflow_client = wf.DaprWorkflowClient( + logger_options=LoggerOptions( + log_level=WORKFLOW_CLIENT_LOG_LEVEL, + ) +) +activity_functions = {} +storage = StorageMinio() +global_module_map: dict[str, EnrichmentModule] = {} # Enrichment modules loaded at initialization + +_dapr_port = os.getenv("DAPR_HTTP_PORT", 3500) +gotenberg_url = f"http://localhost:{_dapr_port}/v1.0/invoke/gotenberg/method/forms/libreoffice/convert" + +nemesis_url = os.getenv("NEMESIS_URL", "http://localhost/") +nemesis_url = f"{nemesis_url}/" if not nemesis_url.endswith("/") else nemesis_url + +asyncpg_pool: asyncpg.Pool = None # Connection pool for database operations +asyncio_loop: asyncio.AbstractEventLoop = None + +# Note: file_linking_engine is initialized after asyncpg_pool is created +# See initialization code that sets this up with the pool +file_linking_engine: FileLinkingEngine = None diff --git a/projects/file_enrichment/file_enrichment/logger.py b/projects/file_enrichment/file_enrichment/logger.py deleted file mode 100644 index d0e3b1e..0000000 --- a/projects/file_enrichment/file_enrichment/logger.py +++ /dev/null @@ -1,120 +0,0 @@ -import logging -import os -from importlib.metadata import version - -import colorlog -import structlog -from opentelemetry import trace -from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter -from opentelemetry.sdk.resources import Resource -from opentelemetry.sdk.trace import TracerProvider -from opentelemetry.sdk.trace.export import BatchSpanProcessor -from opentelemetry.semconv.resource import ResourceAttributes - - -# Create a processor to add worker ID -def add_worker_id(logger, method_name, event_dict): - try: - import multiprocessing - - event_dict["worker_id"] = multiprocessing.current_process().name - except (ImportError, AttributeError): - event_dict["worker_id"] = "unknown" - return event_dict - - -def configure_logging(): - log_level = os.getenv("LOG_LEVEL", "INFO").upper() - - # Validate the log level - numeric_level = getattr(logging, log_level, None) - if not isinstance(numeric_level, int): - raise ValueError(f"Invalid log level: {log_level}") - - # Set up colorlog handler - handler = colorlog.StreamHandler() - - # Create a ProcessorFormatter for structlog that includes color formatting - formatter = structlog.stdlib.ProcessorFormatter( - processor=structlog.dev.ConsoleRenderer(colors=True), - foreign_pre_chain=[ - structlog.stdlib.add_log_level, - structlog.stdlib.add_logger_name, - ], - ) - - # Set the formatter once - handler.setFormatter(formatter) - - # Configure root logger - root_logger = logging.getLogger() - # root_logger.setLevel(logging.DEBUG) - root_logger.setLevel(numeric_level) - - # Clear any existing handlers to prevent double logging - root_logger.handlers = [] - root_logger.addHandler(handler) - - # Configure specific loggers - logging.getLogger("plyara.core").setLevel(logging.WARN) - logging.getLogger("WorkflowRuntime").setLevel(logging.WARN) - logging.getLogger("urllib3.connectionpool").setLevel(logging.WARN) - logging.getLogger("asyncio").setLevel(logging.WARN) - logging.getLogger("opentelemetry.sdk.trace").setLevel(logging.ERROR) - - DaprWorkflowContext_logger = logging.getLogger("DaprWorkflowContext") - DaprWorkflowContext_logger.setLevel(logging.WARN) - DaprWorkflowContext_logger.handlers = [] - DaprWorkflowContext_logger.addHandler(handler) - DaprWorkflowContext_logger.propagate = False - - # Configure structlog to use the same handler - structlog.configure( - processors=[ - structlog.stdlib.filter_by_level, - # add_worker_id, - structlog.stdlib.add_log_level, - structlog.processors.format_exc_info, - structlog.stdlib.ProcessorFormatter.wrap_for_formatter, - ], - logger_factory=structlog.stdlib.LoggerFactory(), - wrapper_class=structlog.stdlib.BoundLogger, - cache_logger_on_first_use=False, - ) - - return handler, formatter - - -def get_instance_id(): - hostname = os.getenv("HOSTNAME", "unknown-host") # Docker: container ID, K8s: pod name - pid = os.getpid() # Uvicorn/Gunicorn worker PID - return f"{hostname}-{pid}" - - -def get_tracer(tracer_name: str, otel_exporter_enabled: bool = True): - """Initialize and return an OpenTelemetry tracer with the specified name.""" - - resource = Resource.create( - { - ResourceAttributes.SERVICE_NAME: "file-enrichment-controller", - ResourceAttributes.SERVICE_NAMESPACE: "nemesis", - ResourceAttributes.SERVICE_VERSION: version("file_enrichment"), - ResourceAttributes.SERVICE_INSTANCE_ID: get_instance_id(), - } - ) - - # Only setup OTLP exporter if monitoring is enabled - if os.getenv("NEMESIS_MONITORING", "").lower() == "enabled": - otlp_exporter = OTLPSpanExporter( - insecure=os.getenv("OTEL_EXPORTER_OTLP_TRACES_ENDPOINT_INSECURE", "true").lower() == "true", - ) - - trace_provider = TracerProvider(resource=resource) - span_processor = BatchSpanProcessor(otlp_exporter) - trace_provider.add_span_processor(span_processor) - trace.set_tracer_provider(trace_provider) - else: - trace_provider = TracerProvider(resource=resource) - trace.set_tracer_provider(trace_provider) - - return trace_provider.get_tracer(tracer_name) diff --git a/projects/file_enrichment/file_enrichment/noseyparker.py b/projects/file_enrichment/file_enrichment/noseyparker.py deleted file mode 100644 index d4767f3..0000000 --- a/projects/file_enrichment/file_enrichment/noseyparker.py +++ /dev/null @@ -1,234 +0,0 @@ -import asyncio -import base64 -import json -import time -import uuid -from typing import Any - -import psycopg -import structlog -from common.helpers import sanitize_for_jsonb -from common.models import EnrichmentResult, FileObject, Finding, FindingCategory, FindingOrigin - -logger = structlog.get_logger(module=__name__) - - -def is_jwt_expired(jwt_token: str) -> tuple[bool, dict[str, Any]]: - """ - Decode a JWT token and check if it's expired. - - Args: - jwt_token (str): The JWT token to check - - Returns: - Tuple[bool, Dict[str, Any]]: A tuple containing: - - Boolean indicating if the token is expired (True) or valid (False) - - Dictionary containing the decoded payload - """ - # Split the token into header, payload, and signature - try: - header_b64, payload_b64, signature = jwt_token.split(".") - except Exception as e: - logger.exception(e, message="Invalid JWT format. Expected three parts separated by dots.", jwt_token=jwt_token) - return False, {} - - # Decode the payload - # JWT uses base64url encoding, so we need to add padding - payload_b64 += "=" * ((4 - len(payload_b64) % 4) % 4) - # Replace URL-safe characters - payload_b64 = payload_b64.replace("-", "+").replace("_", "/") - - try: - payload_json = base64.b64decode(payload_b64).decode("utf-8") - payload = json.loads(payload_json) - except Exception as e: - logger.exception(e, message="Error decoding JWT payload", jwt_token=jwt_token) - return True, {} - - # Check if token is expired - current_time = int(time.time()) - - try: - # Check for "exp" claim - if "exp" not in payload: - # If no expiration time is specified, token doesn't expire - return False, payload - - return current_time > int(payload["exp"]), payload - except Exception as e: - logger.exception(e, message="Error processing jwt_token", jwt_token=jwt_token) - return True, payload - - -def create_finding_summary(match_info): - """ - Creates a markdown summary of a single NoseyParker finding. - - Args: - match_info (MatchInfo): The match information from NoseyParker - - Returns: - str: A markdown formatted summary of the finding - """ - # Generate a finding ID (using a UUID) - finding_id = str(uuid.uuid4()) - - summary = f"# {match_info.rule_name}\n\n" - summary += "### Metadata\n" - summary += f"* **Finding ID**: {finding_id}\n" - summary += f"* **Rule Type**: {match_info.rule_type}\n\n" - - summary += "### Detected Match\n\n" - summary += f"**Location**: Line {match_info.location.line}, Column {match_info.location.column}\n\n" - summary += "**Match**:\n" - summary += "```\n" - summary += f"{match_info.matched_content}\n" - summary += "```\n" - summary += "**Context**:\n" - summary += "```\n" - summary += f"{match_info.snippet}\n" - summary += "```\n" - - # Check if this is a JWT - if match_info.rule_type == "jwt" or "jwt" in match_info.rule_name.lower(): - jwt_token = match_info.matched_content.strip() - is_expired, payload = is_jwt_expired(jwt_token) - - # Add JWT expiration status and decoded payload to the summary - summary += "\n### JWT Analysis\n\n" - summary += f"**Expired**: {is_expired}\n\n" - summary += "**Decoded Payload**:\n" - summary += "```\n" - summary += json.dumps(payload, indent=2) - summary += "\n```\n" - - return summary - - -async def store_noseyparker_results( - object_id: str, matches: list, scan_stats=None, postgres_connection_string: str = None -): - """ - Store Nosey Parker results in the database, including creating findings. - - Args: - object_id (str): The object ID of the file that was scanned - matches (List[MatchInfo]): List of match information from Nosey Parker - scan_stats (dict, optional): Statistics about the scan - postgres_connection_string (str, optional): Database connection string - """ - try: - try: - with psycopg.connect(postgres_connection_string) as conn: - with conn.cursor() as cur: - cur.execute( - """ - UPDATE workflows - SET enrichments_success = array_append(enrichments_success, %s) - WHERE object_id = %s - """, - ("noseyparker", object_id), - ) - conn.commit() - except Exception as db_error: - logger.error(f"Failed to update noseyparker enrichment success in database: {str(db_error)}") - - if not matches: - logger.debug("No matches found, nothing to store", object_id=object_id) - return - - # Create an enrichment result to store - enrichment_result = EnrichmentResult(module_name="noseyparker") - enrichment_result.results = { - "matches": [sanitize_for_jsonb(match.dict() if hasattr(match, "dict") else match) for match in matches], - "stats": sanitize_for_jsonb( - scan_stats.dict() if scan_stats and hasattr(scan_stats, "dict") else scan_stats - ), - } - - # Create findings for each match - findings_list = [] - for match in matches: - # Generate summary for the finding (create_finding_summary should also be updated as shown above) - summary_markdown = create_finding_summary(match) - - # Create display data - display_data = FileObject( - type="finding_summary", - metadata={"summary": sanitize_for_jsonb(summary_markdown)}, # Sanitize the summary too - ) - - # Create the finding with sanitized raw_data - finding = Finding( - category=FindingCategory.CREDENTIAL, - finding_name=f"noseyparker_{match.rule_type if hasattr(match, 'rule_type') else 'match'}", - origin_type=FindingOrigin.ENRICHMENT_MODULE, - origin_name="noseyparker", - object_id=object_id, - severity=7, - raw_data=sanitize_for_jsonb({"match": match.dict() if hasattr(match, "dict") else match}), - data=[display_data], - ) - - findings_list.append(finding) - - # Add findings to enrichment result - enrichment_result.findings = findings_list - - def store_in_db(): - with psycopg.connect(postgres_connection_string) as conn: - with conn.cursor() as cur: - # Store main enrichment result - results_escaped = json.dumps(sanitize_for_jsonb(enrichment_result.model_dump(mode="json"))) - cur.execute( - """ - INSERT INTO enrichments (object_id, module_name, result_data) - VALUES (%s, %s, %s) - """, - (object_id, "noseyparker", results_escaped), - ) - - # Store any findings - for finding in findings_list: - # Convert each FileObject to a JSON string - data_as_strings = [] - for obj in finding.data: - # Convert the model to a dict first - if hasattr(obj, "model_dump"): - obj_dict = obj.model_dump() - else: - obj_dict = obj - sanitized_obj = sanitize_for_jsonb(obj_dict) - data_as_strings.append(json.dumps(sanitized_obj)) - - cur.execute( - """ - INSERT INTO findings ( - finding_name, category, severity, object_id, - origin_type, origin_name, raw_data, data - ) VALUES (%s, %s, %s, %s, %s, %s, %s, %s) - """, - ( - finding.finding_name, - finding.category, - finding.severity, - object_id, - finding.origin_type, - finding.origin_name, - json.dumps(sanitize_for_jsonb(finding.raw_data)), - json.dumps(data_as_strings), # Store as array of JSON strings - ), - ) - - conn.commit() - - # Run database operations in thread - await asyncio.to_thread(store_in_db) - - logger.info("Successfully stored NoseyParker results", object_id=object_id, match_count=len(matches)) - - return enrichment_result - - except Exception as e: - logger.exception(e, message="Error storing NoseyParker results", object_id=object_id) - return None diff --git a/projects/file_enrichment/file_enrichment/postgres_notifications.py b/projects/file_enrichment/file_enrichment/postgres_notifications.py new file mode 100644 index 0000000..74afd35 --- /dev/null +++ b/projects/file_enrichment/file_enrichment/postgres_notifications.py @@ -0,0 +1,109 @@ +# src/workflow/controller.py +import asyncio +import os + +import asyncpg +from common.logger import get_logger + +from .workflow import reload_yara_rules +from .workflow_manager import WorkflowManager + +logger = get_logger(__name__) + + +async def postgres_notify_listener(asyncpg_pool: asyncpg.Pool, workflow_manager: WorkflowManager) -> None: + """ + Listen for PostgreSQL NOTIFY events for yara reload and workflow reset. + Runs in background task to handle notifications across all workers/replicas. + """ + + logger.info("Starting PostgreSQL NOTIFY listener...") + + retry_delay = 1 # Start with 1 second retry delay + max_retry_delay = 60 # Max 60 seconds between retries + + while True: + conn = None + try: + # Acquire a dedicated connection from the pool for listening + conn = await asyncpg_pool.acquire() + logger.info("Connected to PostgreSQL for NOTIFY listening") + retry_delay = 1 # Reset retry delay on successful connection + + # Create a queue to receive notifications + notification_queue = asyncio.Queue() + + # Callback function to handle notifications + def notification_handler(connection, pid, channel, payload): + try: + notification_queue.put_nowait((channel, payload)) + except Exception as e: + logger.error("Error queuing notification", error=str(e)) + + # Add listeners for our notification channels + await conn.add_listener("nemesis_yara_reload", notification_handler) + await conn.add_listener("nemesis_workflow_reset", notification_handler) + + logger.info("Listening for PostgreSQL notifications on nemesis_yara_reload and nemesis_workflow_reset") + + # Process notifications + try: + while True: + try: + # Wait for notification with timeout to allow for cancellation checks + channel, payload = await asyncio.wait_for(notification_queue.get(), timeout=5.0) + + logger.info( + f"Received PostgreSQL notification: channel={channel}, payload={payload}, pid={os.getpid()}" + ) + + if channel == "nemesis_yara_reload": + logger.info("Processing yara reload notification") + reload_yara_rules() + + elif channel == "nemesis_workflow_reset": + logger.info("Processing workflow reset notification") + if workflow_manager is not None: + result = await workflow_manager.reset() + logger.info("Workflow manager reset completed", result=result) + else: + logger.warning("Workflow manager not initialized, skipping reset") + + except TimeoutError: + # No notification received, continue listening + continue + except Exception as e: + logger.exception( + "Error processing PostgreSQL notification", + error=str(e), + pid=os.getpid(), + ) + except asyncio.CancelledError: + logger.info("PostgreSQL NOTIFY listener cancelled") + # Remove listeners before breaking + try: + await conn.remove_listener("nemesis_yara_reload", notification_handler) + await conn.remove_listener("nemesis_workflow_reset", notification_handler) + except Exception: + pass + break + + except asyncio.CancelledError: + logger.info("PostgreSQL NOTIFY listener cancelled during connection") + break + except Exception as e: + logger.exception("PostgreSQL NOTIFY listener connection error", error=str(e)) + + # Exponential backoff with jitter + await asyncio.sleep(retry_delay + (retry_delay * 0.1)) # Add 10% jitter + retry_delay = min(retry_delay * 2, max_retry_delay) + + logger.info(f"Retrying PostgreSQL NOTIFY listener in {retry_delay} seconds...") + finally: + # Always release the connection back to the pool + if conn is not None: + try: + await asyncpg_pool.release(conn) + logger.debug("Released PostgreSQL connection back to pool") + except Exception as e: + logger.error("Error releasing connection to pool", error=str(e)) diff --git a/projects/file_enrichment/file_enrichment/routes/dpapi.py b/projects/file_enrichment/file_enrichment/routes/dpapi.py new file mode 100644 index 0000000..8c11ed0 --- /dev/null +++ b/projects/file_enrichment/file_enrichment/routes/dpapi.py @@ -0,0 +1,339 @@ +"""DPAPI credential submission routes.""" + +import asyncio +import base64 +import logging +import urllib.parse +from typing import Annotated +from uuid import UUID + +from chromium import ( + retry_decrypt_chrome_keys_for_masterkey, + retry_decrypt_chromium_data, + retry_decrypt_state_keys_for_masterkey, +) +from common.logger import get_logger +from common.models2.dpapi import ( + DomainBackupKeyCredential, + DpapiCredentialRequest, + DpapiSystemCredentialRequest, + MasterKeyGuidPairList, + NtlmHashCredentialKey, + PasswordCredentialKey, + Pbkdf2StrongCredentialKey, + Sha1CredentialKey, +) +from Crypto.Hash import SHA1 +from fastapi import APIRouter, Body, Depends, HTTPException, Request +from nemesis_dpapi import ( + DomainBackupKey, + DpapiManager, + DpapiSystemCredential, + MasterKey, + MasterKeyType, + NtlmHash, + Password, + Pbkdf2Hash, + Sha1Hash, +) +from nemesis_dpapi.eventing import ( + DpapiEvent, + DpapiObserver, + NewPlaintextMasterKeyEvent, +) +from nemesis_dpapi.masterkey_decryptor import MasterKeyDecryptorService + +logging.getLogger("nemesis_dpapi.eventing").setLevel(logging.DEBUG) +logging.getLogger("nemesis_dpapi.manager").setLevel(logging.DEBUG) +logger = get_logger(__name__) + + +class PlaintextMasterKeyMonitor(DpapiObserver): + """Observer that monitors for new plaintext masterkeys.""" + + def __init__(self, dpapi_manager: DpapiManager): + """Initialize the monitor with a reference to the DpapiManager.""" + self.dpapi_manager = dpapi_manager + + async def update(self, evnt: DpapiEvent) -> None: + """Called when a DPAPI event occurs.""" + if isinstance(evnt, NewPlaintextMasterKeyEvent): + logger.info( + "New plaintext masterkey detected, checking for state_keys to decrypt", + event_type=type(evnt).__name__, + masterkey_guid=evnt.masterkey_guid, + ) + + # Get the masterkey to check its type + masterkeys = await self.dpapi_manager.get_masterkeys(guid=evnt.masterkey_guid) + masterkey_type = masterkeys[0].masterkey_type.value if masterkeys else None + + # Try to decrypt chrome_keys with this masterkey + chrome_key_result = await retry_decrypt_chrome_keys_for_masterkey( + evnt.masterkey_guid, + self.dpapi_manager, + masterkey_type, + ) + + logger.debug( + "Completed retroactive chrome_key decryption", + masterkey_guid=evnt.masterkey_guid, + masterkey_type=masterkey_type, + result=chrome_key_result, + ) + + # Then try to decrypt any state keys with this masterkey + result = await retry_decrypt_state_keys_for_masterkey( + evnt.masterkey_guid, + self.dpapi_manager, + masterkey_type, + ) + + logger.debug( + "Completed retroactive state_key decryption", + masterkey_guid=evnt.masterkey_guid, + masterkey_type=masterkey_type, + result=result, + ) + + if result and result["state_keys_progressed"] > 0: + # Finally, try to decrypt chromium cookies and logins with newly available keys + chromium_data_result = await retry_decrypt_chromium_data( + evnt.masterkey_guid, + self.dpapi_manager, + masterkey_type, + ) + + logger.debug( + "Completed retroactive chromium data decryption", + masterkey_guid=evnt.masterkey_guid, + masterkey_type=masterkey_type, + result=chromium_data_result, + ) + + +def get_event_loop(request: Request): + return request.app.state.event_loop + + +AsyncLoopDep = Annotated[asyncio.AbstractEventLoop, Depends(get_event_loop)] + + +async def get_dpapi_manager(request: Request): + """Dependency that returns the global DpapiManager instance.""" + manager = request.app.state.dpapi_manager + if manager is None: + raise RuntimeError("DpapiManager not initialized") + return manager + + +DpapiManagerDep = Annotated[DpapiManager, Depends(get_dpapi_manager)] + + +async def get_masterkey_decryptor(dpapi_manager: DpapiManagerDep) -> MasterKeyDecryptorService: + return MasterKeyDecryptorService(dpapi_manager) + + +MasterKeyDecryptorDep = Annotated[MasterKeyDecryptorService, Depends(get_masterkey_decryptor)] + + +async def dpapi_background_monitor(dpapi_manager: DpapiManager) -> None: + logger.info("Starting DPAPI background monitor task") + + monitor = PlaintextMasterKeyMonitor(dpapi_manager) + logger.info("Subscribing PlaintextMasterKeyMonitor to DPAPI events") + await dpapi_manager.subscribe(monitor) + + # Add some sample masterkeys for testing + # for i in range(64): + # logger.info(f"Adding sample masterkey {i + 1}/64") + + # try: + # await dpapi_manager.upsert_masterkey( + # MasterKey( + # guid=UUID(f"ed93694f-5a6d-46e2-b821-219f2c0ecd{i:02x}"), + # encrypted_key_usercred=bytes.fromhex( + # "02000000978d9c959a6a9685a55270cb4fc103d7401f00000e800000106600004b7ee6c2475c4519f48a68c7bc81acb70c63aa1ded8014fd313a4787cd7e1306191d004f6a61e85524222a18ba71f97e1bd83c12ca4ce95054394f7c33c42bc6fddd26f2109e4afb404ca9fb96c6212cf5fda0243eafda0eabbd28002264f9d707e00996a682c30ca6749fb251c8a4c4182157aed0407560cd5b7d3368b59541a0bc13dc8ee141625961edde82bf693a" + # ), + # encrypted_key_backup=bytes.fromhex( + # "030000000001000090000000b151fa7e2325bf45acba2e15ecf4f1e7e20013019258acb6211540f5f8ed8c28d92dd09193ded077fe346386d06169d8d1a65b7d2ecc3264bca5ebae538efa74f8f4b99ec10fe0228daec5481c9c6132f3b2208e870dd0e0d6ee83450a255f588b5608f71978a66f5b4af640a5ffd456f51a36bd65468b8875eb73197db364417c3c6e599fede47b247f3067c5bff4ddd6c7ef9d8e3837b32c206d19d129fb4f666203fabfeb3356a19ed1c56597896c829a7148bac8cfe4ed40ee85c07436e1a73bdee3a379fec54714020bb069ba5a9e607c6323fcb9766a123772c832981610b5acc2e304fa5fe4789355dadd9f2765439e54d47cd187d66031bd9da07b82a8e17d430d87798bebbea80e0a60ac74132f05f592cec0c0e30e927c08a680740e7b27e7593daa59be3e0663c550f204cbc4e5248583fff4fd8489fb01a78ed17ba0b6857b1c800904666263987c8e7613f68cf44ba8807bddd36fa04932bf66e48663b6cb1c4fa5c1ac4875dccb52e4fc73f3a61b14cb5c764989050c480d70112583c519bcfbc5df83281d4da111a4e192d9b48cc8c7e0a1d0ac73f6df2d90" + # ), + # ) + # ) + + # await dpapi_manager.upsert_masterkey( + # MasterKey( + # guid=UUID(f"dd26f81a-4ed9-49fd-8b45-42723d8ae0{i:02x}"), + # encrypted_key_usercred=b'\x02\x00\x00\x00\xbd*N\x8a\x1ff\xc1\xc2\x9d\x97*\xd3%4\xa8\x01@\x1f\x00\x00\x0e\x80\x00\x00\x10f\x00\x00\xa6\xfd\xdb\xe7N+\x89u\xfe\x89l\x07[\xeea\xc5\xae\xe3\x11+5\xab\xc3\x9f\x96\xd8"\x9b<:\xfe\x92\xf9\xc1\xdb\x12B\xed\xcb\x84\xffa\xbc dict: + """Handle domain backup key credential submission.""" + + # Decode URL encoded value for string-based credentials + credential_value = urllib.parse.unquote(backup_key.value) + pvk_data = base64.b64decode(credential_value, validate=True) + backup_key_obj = DomainBackupKey( + guid=UUID(backup_key.guid), # Use the provided GUID + key_data=pvk_data, + domain_controller=backup_key.domain_controller, + ) + backup_key_id = await dpapi_manager.upsert_domain_backup_key(backup_key_obj) + return {"status": "success", "type": "domain_backup_key", "id": backup_key_id} + + +async def _handle_master_key_guid_pairs(dpapi_manager: DpapiManager, request: MasterKeyGuidPairList) -> dict: + """Handle decrypted master key credential submission.""" + + processed_guids = [] + existing_guids = [] + + # Process each master key data entry + for master_key_data in request.value: + # Extract strongly typed master key data + masterkey_guid = master_key_data.guid + masterkey_data = bytes.fromhex(master_key_data.key_hex) + + # Check if masterkey already exists + existing_masterkeys = await dpapi_manager.get_masterkeys(guid=masterkey_guid) + if existing_masterkeys and existing_masterkeys[0].is_decrypted: + logger.info(f"Master key {masterkey_guid} already exists, skipping") + existing_guids.append(str(masterkey_guid)) + continue + + if len(masterkey_data) == 20: + masterkey = MasterKey( + guid=masterkey_guid, + masterkey_type=MasterKeyType.UNKNOWN, + plaintext_key_sha1=masterkey_data, + ) + await dpapi_manager.upsert_masterkey(masterkey) + processed_guids.append(str(masterkey_guid)) + elif len(masterkey_data) == 64: + masterkey = MasterKey( + guid=masterkey_guid, + masterkey_type=MasterKeyType.UNKNOWN, + plaintext_key=masterkey_data, + plaintext_key_sha1=SHA1.new(masterkey_data).digest(), + ) + await dpapi_manager.upsert_masterkey(masterkey) + processed_guids.append(str(masterkey_guid)) + else: + logger.warning( + f"[_handle_master_key_guid_pairs] len(masterkey_data) is not 20 or 64, not handling: {len(masterkey_data)}" + ) + + return { + "status": "success", + "type": "master_key_guid_pair", + "added": processed_guids, + "already_exists": existing_guids, + } + + +async def _handle_dpapi_system_credential(dpapi_manager: DpapiManager, request: DpapiSystemCredentialRequest) -> dict: + """Handle DPAPI_SYSTEM LSA secret credential submission.""" + + dpapi_system_bytes = bytes.fromhex(request.value) + dpapi_system_key = DpapiSystemCredential.from_bytes(dpapi_system_bytes) + await dpapi_manager.upsert_system_credential(dpapi_system_key) + + return {"status": "success", "type": "dpapi_system"} + + +async def _handle_password_based_credential( + decryptor: MasterKeyDecryptorService, + request: PasswordCredentialKey | NtlmHashCredentialKey | Sha1CredentialKey | Pbkdf2StrongCredentialKey, +): + if isinstance(request, PasswordCredentialKey): + c = Password(value=request.value) + elif isinstance(request, NtlmHashCredentialKey): + c = NtlmHash(value=bytes.fromhex(request.value)) + elif isinstance(request, Sha1CredentialKey): + c = Sha1Hash(value=bytes.fromhex(request.value)) + elif isinstance(request, Pbkdf2StrongCredentialKey): + c = Pbkdf2Hash(value=bytes.fromhex(request.value)) + else: + raise ValueError(f"Unsupported password-based credential type: {type(request)}") + + result = await decryptor.process_password_based_credential(c, request.user_sid) + return result diff --git a/projects/file_enrichment/file_enrichment/routes/enrichments.py b/projects/file_enrichment/file_enrichment/routes/enrichments.py new file mode 100644 index 0000000..fba55d2 --- /dev/null +++ b/projects/file_enrichment/file_enrichment/routes/enrichments.py @@ -0,0 +1,152 @@ +"""Enrichment module routes.""" + +import asyncio +import json +import os +import uuid +from typing import TYPE_CHECKING + +import common.helpers as helpers +import file_enrichment.global_vars as global_vars +from common.logger import get_logger +from common.models2.enrichments import EnrichmentRequest, EnrichmentResponse, ModulesListResponse +from fastapi import APIRouter, Body, HTTPException, Path + +if TYPE_CHECKING: + pass + +logger = get_logger(__name__) + +router = APIRouter(tags=["enrichments"]) + + +@router.get("/llm_enrichments", response_model=ModulesListResponse) +async def list_enabled_llm_enrichments() -> ModulesListResponse: + """List the enabled LLM enrichments based on environment variables.""" + try: + if not global_vars.global_module_map: + raise HTTPException(status_code=503, detail="Modules not initialized") + + llm_enrichments = [] + if os.getenv("RIGGING_GENERATOR_CREDENTIALS"): + llm_enrichments.append("llm_credential_analysis") + if os.getenv("RIGGING_GENERATOR_SUMMARY"): + llm_enrichments.append("text_summarizer") + if os.getenv("RIGGING_GENERATOR_TRIAGE"): + llm_enrichments.append("finding_triage") + + return ModulesListResponse(modules=llm_enrichments) + + except Exception as e: + logger.exception(e, message="Error listing enabled LLM enrichment modules", pid=os.getpid()) + raise HTTPException(status_code=500, detail=f"Internal server error: {str(e)}") from e + + +@router.get("/enrichments", response_model=ModulesListResponse) +async def list_enrichments() -> ModulesListResponse: + """List all available enrichment modules.""" + try: + if not global_vars.global_module_map: + raise HTTPException(status_code=503, detail="Modules not initialized") + + module_names = list(global_vars.global_module_map.keys()) + return ModulesListResponse(modules=module_names) + + except Exception as e: + logger.exception(e, message="Error listing enrichment modules", pid=os.getpid()) + raise HTTPException(status_code=500, detail=f"Internal server error: {str(e)}") from e + + +@router.post("/enrichments/{enrichment_name}", response_model=EnrichmentResponse) +async def run_enrichment( + enrichment_name: str = Path(..., description="Name of the enrichment module to run"), + enrichment_request: EnrichmentRequest = Body(..., description="The enrichment request containing the object ID"), +) -> EnrichmentResponse: + """Run a specific enrichment module directly.""" + try: + if enrichment_name not in global_vars.global_module_map: + raise HTTPException(status_code=404, detail=f"Enrichment module '{enrichment_name}' not found") + + # Get the module + module = global_vars.global_module_map[enrichment_name] + + # Check if we should process this file - run in thread since it might use sync operations + should_process = await asyncio.to_thread(module.should_process, enrichment_request.object_id) + if not should_process: + return EnrichmentResponse( + status="skipped", + message=f"Module {enrichment_name} decided to skip processing", + object_id=enrichment_request.object_id, + instance_id="", + ) + + # Process the file in a separate thread to avoid event loop conflicts + result = await asyncio.to_thread(module.process, enrichment_request.object_id) + + if result: + # Store enrichment result in database + async with global_vars.asyncpg_pool.acquire() as conn: + # Store main enrichment result + results_escaped = json.dumps(helpers.sanitize_for_jsonb(result.model_dump(mode="json"))) + await conn.execute( + """ + INSERT INTO enrichments (object_id, module_name, result_data) + VALUES ($1, $2, $3) + """, + enrichment_request.object_id, + enrichment_name, + results_escaped, + ) + + # Store any transforms + if result.transforms: + for transform in result.transforms: + await conn.execute( + """ + INSERT INTO transforms (object_id, type, transform_object_id, metadata) + VALUES ($1, $2, $3, $4) + """, + enrichment_request.object_id, + transform.type, + transform.object_id, + json.dumps(transform.metadata) if transform.metadata else None, + ) + + # Store any findings + if result.findings: + for finding in result.findings: + await conn.execute( + """ + INSERT INTO findings ( + finding_name, category, severity, object_id, + origin_type, origin_name, raw_data, data + ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8) + """, + finding.finding_name, + finding.category, + finding.severity, + enrichment_request.object_id, + finding.origin_type, + finding.origin_name, + json.dumps(finding.raw_data), + json.dumps([obj.model_dump() for obj in finding.data]), + ) + + return EnrichmentResponse( + status="success", + message=f"Completed enrichment with module '{enrichment_name}'", + object_id=enrichment_request.object_id, + instance_id=str(uuid.uuid4()), # Generate a unique instance ID + ) + + except HTTPException: + raise + except Exception as e: + logger.exception( + e, + message="Error running enrichment module", + enrichment_name=enrichment_name, + object_id=enrichment_request.object_id, + pid=os.getpid(), + ) + raise HTTPException(status_code=500, detail=f"Internal server error: {str(e)}") from e diff --git a/projects/file_enrichment/file_enrichment/subscriptions/__init__.py b/projects/file_enrichment/file_enrichment/subscriptions/__init__.py new file mode 100644 index 0000000..55eb4ab --- /dev/null +++ b/projects/file_enrichment/file_enrichment/subscriptions/__init__.py @@ -0,0 +1 @@ +"""Subscription handlers for Dapr events.""" diff --git a/projects/file_enrichment/file_enrichment/subscriptions/bulk_enrichment.py b/projects/file_enrichment/file_enrichment/subscriptions/bulk_enrichment.py new file mode 100644 index 0000000..1d7baf1 --- /dev/null +++ b/projects/file_enrichment/file_enrichment/subscriptions/bulk_enrichment.py @@ -0,0 +1,46 @@ +"""Handler for bulk enrichment task subscription events.""" + +from common.logger import get_logger +from common.models import BulkEnrichmentEvent, SingleEnrichmentWorkflowInput +from file_enrichment.workflow_manager import WorkflowManager + +logger = get_logger(__name__) + + +async def process_bulk_enrichment_event( + evnt: BulkEnrichmentEvent, workflow_manager: WorkflowManager, global_module_map: dict +) -> None: + """Process individual bulk enrichment tasks + + Args: + task: The bulk enrichment task containing enrichment name and object ID + workflow_manager: The workflow manager to schedule enrichment workflows + global_module_map: Map of available enrichment modules + + Raises: + Exception: If task processing fails + """ + try: + logger.debug("Received bulk enrichment task", enrichment_name=evnt.enrichment_name, object_id=object_id) + + # Check if module exists + if not global_module_map: + logger.error("Modules not initialized") + return + + if evnt.enrichment_name not in global_module_map: + logger.error(f"Enrichment module '{evnt.enrichment_name}' not found") + return + + # Prepare workflow input for single enrichment + workflow_input = SingleEnrichmentWorkflowInput( + enrichment_name=evnt.enrichment_name, + object_id=evnt.object_id, + ) + + # This will block if we're at max capacity, providing natural backpressure + await workflow_manager.start_workflow_single_enrichment(workflow_input) + + except Exception: + logger.exception("Error processing bulk enrichment task") + raise diff --git a/projects/file_enrichment/file_enrichment/subscriptions/dotnet.py b/projects/file_enrichment/file_enrichment/subscriptions/dotnet.py new file mode 100644 index 0000000..3e042b2 --- /dev/null +++ b/projects/file_enrichment/file_enrichment/subscriptions/dotnet.py @@ -0,0 +1,324 @@ +"""Handler for .NET output subscription events.""" + +import json +import uuid +from typing import Any + +import file_enrichment.global_vars as global_vars +from common.helpers import sanitize_for_jsonb +from common.logger import get_logger +from common.models import ( + DotNetAssemblyAnalysis, + DotNetOutput, + EnrichmentResult, + File, + FileEnriched, + FileObject, + Finding, + FindingCategory, + FindingOrigin, + Transform, +) +from common.state_helpers import get_file_enriched_async +from dapr.clients import DaprClient +from file_enrichment.tracing import get_trace_injector + +logger = get_logger(__name__) + + +async def process_dotnet_event(dotnet_output: DotNetOutput) -> None: + """Process incoming .NET processing results from the dotnet_service""" + + logger.debug("Received DotNet output event", data=dotnet_output.model_dump_json()) + + # Try to parse the event data into our DotNetOutput model + try: + logger.debug("Processing dotnet results for object", object_id=dotnet_output.object_id) + + file_enriched = await get_file_enriched_async(dotnet_output.object_id) + + await store_dotnet_results( + dotnet_output=dotnet_output, + file_enriched=file_enriched, + ) + + except Exception: + logger.error( + "Failed to process DotNet output for object_id", + object_id=dotnet_output.object_id, + ) + + +def create_dotnet_finding_summary(analysis: DotNetAssemblyAnalysis) -> str: + """ + Creates a markdown summary of DotNet assembly analysis findings. + + Args: + analysis (DotNetAssemblyAnalysis): The assembly analysis results + + Returns: + str: A markdown formatted summary of the findings + """ + # Generate a finding ID (using a UUID) + finding_id = str(uuid.uuid4()) + + summary = f"# .NET Assembly Analysis: {analysis.AssemblyName}\n\n" + summary += "### Metadata\n" + summary += f"* **Finding ID**: {finding_id}\n" + summary += f"* **Assembly Name**: {analysis.AssemblyName}\n" + summary += f"* **WCF Server**: {analysis.IsWCFServer}\n" + summary += f"* **WCF Client**: {analysis.IsWCFClient}\n\n" + + # Remoting channels + if analysis.RemotingChannels: + summary += "### Remoting Channels\n" + for channel in analysis.RemotingChannels: + summary += f"* {channel}\n" + summary += "\n" + + # Helper function to add method calls section + def add_method_section(title: str, method_dict: dict[str, list[Any]]): + if method_dict: + summary_content = f"### {title}\n" + for category, methods in method_dict.items(): + if methods: + summary_content += f"\n#### {category}\n" + for method in methods: + if hasattr(method, "MethodName"): + summary_content += f"* `{method.MethodName}` (Level: {method.FilterLevel})\n" + else: + summary_content += f"* `{method}`\n" + summary_content += "\n" + return summary_content + return "" + + # Add various method call sections + summary += add_method_section("Serialization Gadget Calls", analysis.SerializationGadgetCalls) + summary += add_method_section("WCF Server Calls", analysis.WcfServerCalls) + summary += add_method_section("Client Calls", analysis.ClientCalls) + summary += add_method_section("Remoting Calls", analysis.RemotingCalls) + summary += add_method_section("Execution Calls", analysis.ExecutionCalls) + + return summary + + +async def store_dotnet_results( + dotnet_output: DotNetOutput, + file_enriched: FileEnriched | None = None, +): + """ + Store DotNet analysis results in the database, including creating findings and transforms. + + Args: + dotnet_output (DotNetOutput): The DotNet output containing object_id, decompilation, and analysis + file_enriched: The FileEnriched object for the original file + """ + object_id = dotnet_output.object_id + decompilation_object_id = dotnet_output.decompilation + analysis = dotnet_output.analysis + try: + # Update workflow success status + try: + async with global_vars.asyncpg_pool.acquire() as conn: + await conn.execute( + """ + UPDATE workflows + SET enrichments_success = array_append(enrichments_success, $1) + WHERE object_id = $2 + """, + "dotnet_service", + object_id, + ) + except Exception as db_error: + logger.error(f"Failed to update dotnet_service enrichment success in database: {str(db_error)}") + + # Create an enrichment result to store + enrichment_result = EnrichmentResult(module_name="dotnet_service") + enrichment_result.results = {} + + # Handle decompilation results + if decompilation_object_id: + if not file_enriched: + logger.warning("file_enriched is None, cannot create decompilation transform") + else: + # Create decompilation transform + decompilation = Transform( + type="decompilation", + object_id=decompilation_object_id, + metadata={ + "file_name": f"{file_enriched.file_name}.zip", + "offer_as_download": True, + "display_title": "Decompiled Source Code", + }, + ) + enrichment_result.transforms = [decompilation] + + # Publish the decompiled file as a new file message + file_message = File( + object_id=decompilation_object_id, + agent_id=file_enriched.agent_id, + project=file_enriched.project, + timestamp=file_enriched.timestamp, + expiration=file_enriched.expiration, + path=f"{file_enriched.path}/decompiled.zip", + originating_object_id=file_enriched.object_id, + nesting_level=(file_enriched.nesting_level or 0) + 1, + ) + + with DaprClient(headers_callback=get_trace_injector()) as dapr_client: + data = json.dumps(file_message.model_dump(exclude_unset=True, mode="json")) + dapr_client.publish_event( + pubsub_name="pubsub", + topic_name="file", + data=data, + data_content_type="application/json", + ) + + logger.info( + "Submitted decompiled source ZIP to Nemesis", + decompiled_object_id=decompilation_object_id, + originating_object_id=object_id, + ) + + # Handle analysis results + findings_list = [] + if analysis: + # Store the analysis results + enrichment_result.results["inspect_assembly"] = sanitize_for_jsonb(analysis.model_dump()) + + # Check if there was an error during analysis + if analysis.Error: + logger.error( + "DotNet assembly finished, but analysis failed", + object_id=object_id, + assembly_name=analysis.AssemblyName, + error=analysis.Error, + ) + + # Create a finding for the error + error_summary = f"# .NET Assembly Analysis Error: {analysis.AssemblyName}\n\n" + error_summary += f"**Error**: {analysis.Error}\n\n" + error_summary += "The assembly could not be analyzed.\n" + + display_data = FileObject( + type="finding_summary", + metadata={"summary": sanitize_for_jsonb(error_summary)}, + ) + + finding = Finding( + category=FindingCategory.INFORMATIONAL, + finding_name="dotnet_analysis_error", + origin_type=FindingOrigin.ENRICHMENT_MODULE, + origin_name="dotnet_service", + object_id=object_id, + severity=2, + raw_data=sanitize_for_jsonb(analysis.model_dump()), + data=[display_data], + ) + + findings_list.append(finding) + + # Check if there are any significant findings worth creating a finding for + has_significant_findings = ( + analysis.IsWCFServer + or analysis.IsWCFClient + or analysis.RemotingChannels + or analysis.SerializationGadgetCalls + or analysis.WcfServerCalls + or analysis.ClientCalls + or analysis.RemotingCalls + or analysis.ExecutionCalls + ) + + if has_significant_findings and not analysis.Error: + # Generate summary for the finding + summary_markdown = create_dotnet_finding_summary(analysis) + + # Create display data + display_data = FileObject( + type="finding_summary", + metadata={"summary": sanitize_for_jsonb(summary_markdown)}, + ) + + # Create the finding + finding = Finding( + category=FindingCategory.VULNERABILITY, + finding_name="dotnet_vulns", + origin_type=FindingOrigin.ENRICHMENT_MODULE, + origin_name="dotnet_service", + object_id=object_id, + severity=9, + raw_data=sanitize_for_jsonb(analysis.model_dump()), + data=[display_data], + ) + + findings_list.append(finding) + + # Add findings to enrichment result + enrichment_result.findings = findings_list + + # Store in database + async with global_vars.asyncpg_pool.acquire() as conn: + # Store main enrichment result + results_escaped = json.dumps(sanitize_for_jsonb(enrichment_result.model_dump(mode="json"))) + await conn.execute( + """ + INSERT INTO enrichments (object_id, module_name, result_data) + VALUES ($1, $2, $3) + """, + object_id, + "dotnet_service", + results_escaped, + ) + + # Store any transforms + if enrichment_result.transforms: + for transform in enrichment_result.transforms: + await conn.execute( + """ + INSERT INTO transforms (object_id, type, transform_object_id, metadata) + VALUES ($1, $2, $3, $4) + """, + object_id, + transform.type, + transform.object_id, + json.dumps(transform.metadata) if transform.metadata else None, + ) + + # Store any findings + for finding in findings_list: + # Convert each FileObject to a JSON string + data_as_strings = [] + for obj in finding.data: + # Convert the model to a dict first + if hasattr(obj, "model_dump"): + obj_dict = obj.model_dump() + else: + obj_dict = obj + sanitized_obj = sanitize_for_jsonb(obj_dict) + data_as_strings.append(json.dumps(sanitized_obj)) + + await conn.execute( + """ + INSERT INTO findings ( + finding_name, category, severity, object_id, + origin_type, origin_name, raw_data, data + ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8) + """, + finding.finding_name, + finding.category, + finding.severity, + object_id, + finding.origin_type, + finding.origin_name, + json.dumps(sanitize_for_jsonb(finding.raw_data)), + json.dumps(data_as_strings), # Store as array of JSON strings + ) + + logger.info("Successfully stored DotNet results", object_id=object_id, has_findings=len(findings_list) > 0) + + return enrichment_result + + except Exception as e: + logger.exception(e, message="Error storing DotNet results", object_id=object_id) + return None diff --git a/projects/file_enrichment/file_enrichment/subscriptions/file.py b/projects/file_enrichment/file_enrichment/subscriptions/file.py new file mode 100644 index 0000000..6e3f3fc --- /dev/null +++ b/projects/file_enrichment/file_enrichment/subscriptions/file.py @@ -0,0 +1,88 @@ +"""Handler for file subscription events.""" + +import os +from datetime import datetime + +import file_enrichment.global_vars as global_vars +from common.logger import get_logger +from common.models import File + +logger = get_logger(__name__) + + +async def process_file_event(file: File, workflow_manager, module_execution_order: list): + """Process incoming new file events""" + try: + await save_file_message(file) + + workflow_input = { + "file": file.model_dump(exclude_unset=True, mode="json"), + "execution_order": module_execution_order, + } + + await workflow_manager.start_workflow(workflow_input) + + except Exception as e: + logger.exception(e, message="Error processing file event", pid=os.getpid()) + raise + + +async def save_file_message(file: File): + """Save the file message to the database for recovery purposes""" + try: + # Only save files that are not nested (originating files) + if file.nesting_level and file.nesting_level > 0: + logger.debug( + "nesting_level > 0, not saving file message", + nesting_level=file.nesting_level, + object_id=file.object_id, + pid=os.getpid(), + ) + return + + query = """ + INSERT INTO files ( + object_id, agent_id, source, project, timestamp, expiration, + path, originating_object_id, originating_container_id, nesting_level, + file_creation_time, file_access_time, file_modification_time + ) VALUES ( + $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13 + ) ON CONFLICT (object_id) DO UPDATE SET + agent_id = EXCLUDED.agent_id, + source = EXCLUDED.source, + project = EXCLUDED.project, + timestamp = EXCLUDED.timestamp, + expiration = EXCLUDED.expiration, + path = EXCLUDED.path, + originating_object_id = EXCLUDED.originating_object_id, + originating_container_id = EXCLUDED.originating_container_id, + nesting_level = EXCLUDED.nesting_level, + file_creation_time = EXCLUDED.file_creation_time, + file_access_time = EXCLUDED.file_access_time, + file_modification_time = EXCLUDED.file_modification_time, + updated_at = CURRENT_TIMESTAMP; + """ + + async with global_vars.asyncpg_pool.acquire() as conn: + await conn.execute( + query, + file.object_id, + file.agent_id, + file.source, + file.project, + file.timestamp, + file.expiration, + file.path, + file.originating_object_id, + getattr(file, "originating_container_id", None), + file.nesting_level, + datetime.fromisoformat(file.creation_time) if file.creation_time else None, + datetime.fromisoformat(file.access_time) if file.access_time else None, + datetime.fromisoformat(file.modification_time) if file.modification_time else None, + ) + + logger.debug("Successfully saved file message to database", object_id=file.object_id, pid=os.getpid()) + + except Exception as e: + logger.exception(e, message="Error saving file message to database", object_id=file.object_id, pid=os.getpid()) + raise diff --git a/projects/file_enrichment/file_enrichment/subscriptions/noseyparker.py b/projects/file_enrichment/file_enrichment/subscriptions/noseyparker.py new file mode 100644 index 0000000..048cedc --- /dev/null +++ b/projects/file_enrichment/file_enrichment/subscriptions/noseyparker.py @@ -0,0 +1,314 @@ +"""Handler for Nosey Parker output subscription events.""" + +import base64 +import json +import os +import re +import time +import uuid +from datetime import datetime +from typing import Any + +import file_enrichment.global_vars as global_vars +from common.helpers import sanitize_for_jsonb +from common.logger import get_logger +from common.models import ( + EnrichmentResult, + FileObject, + Finding, + FindingCategory, + FindingOrigin, + MatchInfo, + NoseyParkerOutput, + ScanStats, +) + +logger = get_logger(__name__) + + +async def process_noseyparker_event(nosey_output: NoseyParkerOutput): + """Process incoming Nosey Parker scan results""" + try: + object_id = nosey_output.object_id + matches = nosey_output.scan_result.matches + stats = nosey_output.scan_result.stats + + logger.debug(f"Found {len(matches)} matches for object {object_id}", pid=os.getpid()) + + await store_noseyparker_results( + object_id=object_id, + matches=matches, + scan_stats=stats, + ) + + except Exception as e: + logger.exception(e, message="Error processing Nosey Parker output event", pid=os.getpid()) + raise + + +def is_jwt_expired(jwt_token: str) -> tuple[bool, dict[str, Any]]: + """ + Decode a JWT token and check if it's expired. + + Args: + jwt_token (str): The JWT token to check + + Returns: + Tuple[bool, Dict[str, Any]]: A tuple containing: + - Boolean indicating if the token is expired (True) or valid (False) + - Dictionary containing the decoded payload + """ + # Split the token into header, payload, and signature + try: + header_b64, payload_b64, signature = jwt_token.split(".") + except Exception as e: + logger.exception(e, message="Invalid JWT format. Expected three parts separated by dots.", jwt_token=jwt_token) + return False, {} + + # Decode the payload + # JWT uses base64url encoding, so we need to add padding + payload_b64 += "=" * ((4 - len(payload_b64) % 4) % 4) + # Replace URL-safe characters + payload_b64 = payload_b64.replace("-", "+").replace("_", "/") + + try: + payload_json = base64.b64decode(payload_b64).decode("utf-8") + payload = json.loads(payload_json) + except Exception as e: + logger.exception(e, message="Error decoding JWT payload", jwt_token=jwt_token) + return True, {} + + # Check if token is expired + current_time = int(time.time()) + + try: + # Check for "exp" claim + if "exp" not in payload: + # If no expiration time is specified, token doesn't expire + return False, payload + + return current_time > int(payload["exp"]), payload + except Exception as e: + logger.exception(e, message="Error processing jwt_token", jwt_token=jwt_token) + return True, payload + + +def format_commit_date(commit_date_str): + """ + Convert git commit date from Unix timestamp format to human-readable format. + + Args: + commit_date_str (str): Commit date in format "timestamp timezone" (e.g., "1753487005 -0700") + + Returns: + str: Human-readable date string, or original value if conversion fails + """ + try: + # Parse the timestamp and timezone using regex + match = re.match(r"^(\d+)\s*([-+]\d{4})$", commit_date_str.strip()) + if not match: + return commit_date_str + + timestamp_str, tz_offset = match.groups() + timestamp = int(timestamp_str) + + # Convert Unix timestamp to datetime object + dt = datetime.fromtimestamp(timestamp) + + # Format as human-readable string + formatted_date = dt.strftime("%Y-%m-%d %H:%M:%S") + + # Add timezone offset to the formatted string + return f"{formatted_date} {tz_offset}" + + except (ValueError, OSError, OverflowError): + # Return original value if any conversion fails + return commit_date_str + + +def create_finding_summary(match_info): + """ + Creates a markdown summary of a single NoseyParker finding. + + Args: + match_info (MatchInfo): The match information from NoseyParker + + Returns: + str: A markdown formatted summary of the finding + """ + # Generate a finding ID (using a UUID) + finding_id = str(uuid.uuid4()) + + summary = f"# {match_info.rule_name}\n\n" + summary += "### Metadata\n" + summary += f"* **Finding ID**: {finding_id}\n" + summary += f"* **Rule Type**: {match_info.rule_type}\n" + + # Add file path if available + if match_info.file_path: + summary += f"* **File Path**: `{match_info.file_path}`\n" + + # Add git commit information if available + if match_info.git_commit: + summary += f"* **Git Commit**: `{match_info.git_commit.commit_id}`\n" + summary += f"* **Author**: {match_info.git_commit.author} ({match_info.git_commit.author_email})\n" + + # Format the commit date with fallback + formatted_date = format_commit_date(match_info.git_commit.commit_date) + summary += f"* **Commit Date**: {formatted_date}\n" + + summary += f"* **Commit Message**: {match_info.git_commit.message[:100]}{'...' if len(match_info.git_commit.message) > 100 else ''}\n" + + summary += "\n" + + summary += "### Detected Match\n\n" + summary += f"**Location**: Line {match_info.location.line}, Column {match_info.location.column}\n\n" + summary += "**Match**:\n" + summary += "```\n" + summary += f"{match_info.matched_content}\n" + summary += "```\n" + summary += "**Context**:\n" + summary += "```\n" + summary += f"{match_info.snippet}\n" + summary += "```\n" + + # Check if this is a JWT + if match_info.rule_type == "secret" and "json web token" in match_info.rule_name.lower(): + jwt_token = match_info.matched_content.strip() + is_expired, payload = is_jwt_expired(jwt_token) + + # Add JWT expiration status and decoded payload to the summary + summary += "\n### JWT Analysis\n\n" + summary += f"**Expired**: {is_expired}\n\n" + summary += "**Decoded Payload**:\n" + summary += "```\n" + summary += json.dumps(payload, indent=2) + summary += "\n```\n" + + return summary + + +async def store_noseyparker_results( + object_id: str, + matches: list[MatchInfo], + scan_stats: ScanStats, +): + """ + Store Nosey Parker results in the database, including creating findings. + + Args: + object_id (str): The object ID of the file that was scanned + matches (List[MatchInfo]): List of match information from Nosey Parker + scan_stats (dict, optional): Statistics about the scan + pool (asyncpg.Pool): Database connection pool + """ + try: + try: + async with global_vars.asyncpg_pool.acquire() as conn: + await conn.execute( + """ + UPDATE workflows + SET enrichments_success = array_append(enrichments_failure, $1) + WHERE object_id = $2 + """, + "noseyparker", + object_id, + ) + except Exception as db_error: + logger.error(f"Failed to update noseyparker enrichment success in database: {str(db_error)}") + + if not matches: + logger.debug("No matches found, nothing to store", object_id=object_id) + return + + # Create an enrichment result to store + enrichment_result = EnrichmentResult(module_name="noseyparker") + enrichment_result.results = { + "matches": [ + sanitize_for_jsonb(match.model_dump() if hasattr(match, "model_dump") else match) for match in matches + ], + "stats": sanitize_for_jsonb( + scan_stats.model_dump() if scan_stats and hasattr(scan_stats, "model_dump") else scan_stats + ), + } + + # Create findings for each match + findings_list = [] + for match in matches: + # Generate summary for the finding (create_finding_summary should also be updated as shown above) + summary_markdown = create_finding_summary(match) + + # Create display data + display_data = FileObject( + type="finding_summary", + metadata={"summary": sanitize_for_jsonb(summary_markdown)}, # Sanitize the summary too + ) + + # Create the finding with sanitized raw_data + finding = Finding( + category=FindingCategory.CREDENTIAL, + finding_name=f"noseyparker_{match.rule_type if hasattr(match, 'rule_type') else 'match'}", + origin_type=FindingOrigin.ENRICHMENT_MODULE, + origin_name="noseyparker", + object_id=object_id, + severity=7, + raw_data=sanitize_for_jsonb({"match": match.model_dump() if hasattr(match, "model_dump") else match}), + data=[display_data], + ) + + findings_list.append(finding) + + # Add findings to enrichment result + enrichment_result.findings = findings_list + + # Store in database + async with global_vars.asyncpg_pool.acquire() as conn: + # Store main enrichment result + results_escaped = json.dumps(sanitize_for_jsonb(enrichment_result.model_dump(mode="json"))) + await conn.execute( + """ + INSERT INTO enrichments (object_id, module_name, result_data) + VALUES ($1, $2, $3) + """, + object_id, + "noseyparker", + results_escaped, + ) + + # Store any findings + for finding in findings_list: + # Convert each FileObject to a JSON string + data_as_strings = [] + for obj in finding.data: + # Convert the model to a dict first + if hasattr(obj, "model_dump"): + obj_dict = obj.model_dump() + else: + obj_dict = obj + sanitized_obj = sanitize_for_jsonb(obj_dict) + data_as_strings.append(json.dumps(sanitized_obj)) + + await conn.execute( + """ + INSERT INTO findings ( + finding_name, category, severity, object_id, + origin_type, origin_name, raw_data, data + ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8) + """, + finding.finding_name, + finding.category, + finding.severity, + object_id, + finding.origin_type, + finding.origin_name, + json.dumps(sanitize_for_jsonb(finding.raw_data)), + json.dumps(data_as_strings), # Store as array of JSON strings + ) + + logger.info("Successfully stored NoseyParker results", object_id=object_id, match_count=len(matches)) + + return enrichment_result + + except Exception as e: + logger.exception(e, message="Error storing NoseyParker results", object_id=object_id) + return None diff --git a/projects/file_enrichment/file_enrichment/tracing.py b/projects/file_enrichment/file_enrichment/tracing.py new file mode 100644 index 0000000..8cc09b3 --- /dev/null +++ b/projects/file_enrichment/file_enrichment/tracing.py @@ -0,0 +1,122 @@ +import os +from importlib.metadata import version + +from opentelemetry import trace +from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter +from opentelemetry.sdk.resources import Resource +from opentelemetry.sdk.trace import TracerProvider +from opentelemetry.sdk.trace.export import BatchSpanProcessor +from opentelemetry.semconv._incubating.attributes import service_attributes +from opentelemetry.trace.propagation.tracecontext import TraceContextTextMapPropagator + +# Module-level tracer singleton +_tracer = None + + +def get_instance_id(): + hostname = os.getenv("HOSTNAME", "unknown-host") # Docker: container ID, K8s: pod name + pid = os.getpid() # Uvicorn/Gunicorn worker PID + return f"{hostname}-{pid}" + + +def get_trace_injector(): + """ + Returns a callback that injects W3C trace context into Dapr headers. + + This enables distributed tracing across Dapr service boundaries by propagating + traceparent and tracestate headers per W3C Trace Context specification. + + The returned callback should be passed to DaprClient's headers_callback parameter + to automatically include trace context in all Dapr operations (pub/sub, service + invocation, state operations, etc.). + + Returns: + Callable that returns dict of trace headers with current trace context + + Example: + >>> from dapr.clients import DaprClient + >>> with DaprClient(headers_callback=get_trace_injector()) as client: + ... client.publish_event( + ... pubsub_name="pubsub", + ... topic_name="my-topic", + ... data=json.dumps({"key": "value"}) + ... ) + + Note: + This function must be called within an active OpenTelemetry span context + for trace propagation to work. If called outside a span, it returns empty + headers which is safe but provides no trace context. + """ + + def inject_trace_context(): + headers = {} + TraceContextTextMapPropagator().inject(carrier=headers) + return headers + + return inject_trace_context + + +def get_tracer(module_name: str = "file_enrichment"): + """ + Initialize and return an OpenTelemetry tracer for the file_enrichment service. + + This function uses a module-level singleton pattern to ensure the tracer is + initialized only once. Subsequent calls return the same tracer instance. + + Tracing behavior is controlled by the NEMESIS_MONITORING environment variable: + - When enabled: Spans are exported to an OTLP endpoint (e.g., Jaeger) + - When disabled: Spans are created but not exported (in-memory only) + + Args: + module_name: The module name used to identify the tracer and determine + the service version from package metadata (default: "file_enrichment") + + Returns: + A configured OpenTelemetry Tracer instance that can be used to create spans. + + Environment Variables: + NEMESIS_MONITORING: Set to "enabled" to export traces to OTLP endpoint + OTEL_EXPORTER_OTLP_TRACES_ENDPOINT_INSECURE: Set to "true" for insecure + connections (default: "true") + HOSTNAME: Used to construct the service instance ID + + Example: + >>> tracer = get_tracer() + >>> with tracer.start_as_current_span("my_operation") as span: + ... span.set_attribute("key", "value") + ... # ... do work ... + """ + global _tracer + + # Return existing tracer if already initialized + if _tracer is not None: + return _tracer + + # Create resource with service metadata + resource = Resource.create( + { + service_attributes.SERVICE_NAME: "file_enrichment", + service_attributes.SERVICE_NAMESPACE: "nemesis", + service_attributes.SERVICE_VERSION: version(module_name), + service_attributes.SERVICE_INSTANCE_ID: get_instance_id(), + } + ) + + # Create TracerProvider and configure export based on monitoring setting + trace_provider = TracerProvider(resource=resource) + + # Only setup OTLP exporter if monitoring is enabled + monitoring_enabled = os.getenv("NEMESIS_MONITORING", "").lower() == "enabled" + if monitoring_enabled: + otlp_exporter = OTLPSpanExporter( + insecure=os.getenv("OTEL_EXPORTER_OTLP_TRACES_ENDPOINT_INSECURE", "true").lower() == "true", + ) + span_processor = BatchSpanProcessor(otlp_exporter) + trace_provider.add_span_processor(span_processor) + + # Set as global tracer provider + trace.set_tracer_provider(trace_provider) + + # Cache and return tracer + _tracer = trace_provider.get_tracer(module_name) + return _tracer diff --git a/projects/file_enrichment/file_enrichment/workflow.py b/projects/file_enrichment/file_enrichment/workflow.py index de54f98..6cbc3e1 100644 --- a/projects/file_enrichment/file_enrichment/workflow.py +++ b/projects/file_enrichment/file_enrichment/workflow.py @@ -1,64 +1,24 @@ # src/workflow/workflow.py -import io -import json -import ntpath -import os -import pathlib -from datetime import datetime -from typing import BinaryIO +import asyncio -import common.helpers as helpers import dapr.ext.workflow as wf -import magic -import psycopg -import structlog -from common.helpers import is_container -from common.models import Alert, EnrichmentResult, File, NoseyParkerInput -from common.state_helpers import get_file_enriched -from common.storage import StorageMinio -from dapr.clients import DaprClient -from dapr.ext.workflow.logger.options import LoggerOptions -from dapr.ext.workflow.workflow_activity_context import WorkflowActivityContext +from common.logger import get_logger +from common.workflows.setup import wf_runtime from file_enrichment_modules.module_loader import ModuleLoader +from file_enrichment_modules.yara.yara_manager import YaraRuleManager +from nemesis_dpapi import DpapiManager -from .file_feature_extractor import FileFeatureExtractor -from .logger import configure_logging - -logger = structlog.get_logger(module=__name__) - -log_handler, log_formatter = configure_logging() -workflow_runtime_log_level = os.getenv("WORKFLOW_RUNTIME_LOG_LEVEL", "WARNING") -workflow_client_log_level = os.getenv("WORKFLOW_CLIENT_LOG_LEVEL", "WARNING") - -workflow_runtime = wf.WorkflowRuntime( - logger_options=LoggerOptions( - log_level=workflow_runtime_log_level, - log_handler=log_handler, - log_formatter=log_formatter, - ) +from . import global_vars +from .activities import ( + check_file_linkings, + get_basic_analysis, + handle_file_if_plaintext, + publish_enriched_file, + publish_findings_alerts, + run_enrichment_modules, ) - -workflow_client: wf.DaprWorkflowClient = None -activity_functions = {} -download_path = "/tmp/" -storage = StorageMinio() - -dapr_port = os.getenv("DAPR_HTTP_PORT", 3500) -gotenberg_url = f"http://localhost:{dapr_port}/v1.0/invoke/gotenberg/method/forms/libreoffice/convert" -max_parallel_enrichment_modules = int( - os.getenv("MAX_PARALLEL_ENRICHMENT_MODULES", 5) -) # maximum workflows that can run at a time -container_nesting_limit = 2 - -logger.info(f"max_parallel_enrichment_modules: {max_parallel_enrichment_modules}") -nemesis_url = os.getenv("NEMESIS_URL", "http://localhost/") -nemesis_url = f"{nemesis_url}/" if not nemesis_url.endswith("/") else nemesis_url - - -with DaprClient() as client: - secret = client.get_secret(store_name="nemesis-secret-store", key="POSTGRES_CONNECTION_STRING") - postgres_connection_string = secret.secret["POSTGRES_CONNECTION_STRING"] +logger = get_logger(__name__) ########################################## @@ -68,133 +28,6 @@ with DaprClient() as client: ########################################## -def create_enrichment_activity(module_name: str): - """Creates a unique activity function for each module""" - global activity_functions - activity_name = f"enrich_{module_name}" - - @workflow_runtime.activity(name=activity_name) - def activity_function(ctx, input_data: dict): - logger.debug("Starting enrichment activity", module_name=module_name) - object_id = input_data["object_id"] - result = None - - try: - if module_name not in workflow_runtime.modules: - raise KeyError(f"Module {module_name} not found in workflow_runtime.modules") - - module = workflow_runtime.modules[module_name] - - # check if we should process with this module - should_process = module.should_process(object_id) - - if not should_process: - logger.debug("Module decided to skip processing", module_name=module_name) - return None - - result: EnrichmentResult = module.process(object_id) - if result: - # Store enrichment result directly in database - with psycopg.connect(postgres_connection_string) as conn: - with conn.cursor() as cur: - # escape any \x0000 characters/etc. and then dump to a form we can index - results_escaped = json.dumps(helpers.sanitize_for_jsonb(result.model_dump(mode="json"))) - - # Store enrichment - cur.execute( - """ - INSERT INTO enrichments (object_id, module_name, result_data) - VALUES (%s, %s, %s) - """, - (object_id, module_name, results_escaped), - ) - - # Store any transforms - if result.transforms: - for transform in result.transforms: - cur.execute( - """ - INSERT INTO transforms (object_id, type, transform_object_id, metadata) - VALUES (%s, %s, %s, %s) - """, - ( - object_id, - transform.type, - transform.object_id, - json.dumps(transform.metadata) if transform.metadata else None, - ), - ) - - # Store any findings - if result.findings: - for finding in result.findings: - cur.execute( - """ - INSERT INTO findings ( - finding_name, category, severity, object_id, - origin_type, origin_name, raw_data, data - ) VALUES (%s, %s, %s, %s, %s, %s, %s, %s) - """, - ( - finding.finding_name, - finding.category, - finding.severity, - object_id, - finding.origin_type, - finding.origin_name, - json.dumps(finding.raw_data), - json.dumps([obj.model_dump_json() for obj in finding.data]), - ), - ) - - # Update workflow in database with successful module - logger.debug(f"Enrichment success: {module_name}") - cur.execute( - """ - UPDATE workflows - SET enrichments_success = array_append(enrichments_success, %s) - WHERE object_id = %s - """, - (module_name, object_id), - ) - - conn.commit() - - # Return minimal result to indicate success - return {"status": "success", "module": module_name} - - except Exception as e: - logger.exception( - e, - message="Error in enrichment module", - module_name=module_name, - object_id=object_id, - result=result if result else None, - exc_info=True, - ) - - # Update workflow in database with failed module - try: - with psycopg.connect(postgres_connection_string) as conn: - with conn.cursor() as cur: - cur.execute( - """ - UPDATE workflows - SET enrichments_failure = array_append(enrichments_failure, %s) - WHERE object_id = %s - """, - (f"{module_name}:{str(e)[:100]}", object_id), - ) - conn.commit() - except Exception as db_error: - logger.error(f"Failed to update workflow failure in database: {str(db_error)}") - - raise - - activity_functions[activity_name] = activity_function - return activity_name - - def build_dependency_graph(modules): """Build a dependency grap for enrichment modules and check for circular dependencies""" graph = {name: set() for name in modules.keys()} @@ -233,514 +66,6 @@ def topological_sort(graph): return order -# endregion - -########################################## -# -# region Postgres state -# -########################################## - - -@workflow_runtime.activity -def index_file_message(ctx: WorkflowActivityContext, activity_input: dict): - """Store the file message in PostgreSQL for later replay. Only indexes non-nested files.""" - try: - file = File.model_validate(activity_input) - - # we don't want to index files that were extracted from existing files that we've already processed - if file.nesting_level and file.nesting_level > 0: - logger.debug( - "nesting_level > 0, not indexing `file` message", - nesting_level=file.nesting_level, - object_id=file.object_id, - ) - return - - with psycopg.connect(postgres_connection_string) as conn: - with conn.cursor() as cur: - query = """ - INSERT INTO files ( - object_id, agent_id, project, timestamp, expiration, - path, originating_object_id, nesting_level, - file_creation_time, file_access_time, file_modification_time - ) VALUES ( - %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s - ) ON CONFLICT (object_id) DO UPDATE SET - agent_id = EXCLUDED.agent_id, - project = EXCLUDED.project, - timestamp = EXCLUDED.timestamp, - expiration = EXCLUDED.expiration, - path = EXCLUDED.path, - originating_object_id = EXCLUDED.originating_object_id, - nesting_level = EXCLUDED.nesting_level, - file_creation_time = EXCLUDED.file_creation_time, - file_access_time = EXCLUDED.file_access_time, - file_modification_time = EXCLUDED.file_modification_time, - updated_at = CURRENT_TIMESTAMP; - """ - - cur.execute( - query, - ( - file.object_id, - file.agent_id, - file.project, - file.timestamp, - file.expiration, - file.path, - file.originating_object_id, - file.nesting_level, - datetime.fromisoformat(file.creation_time) if file.creation_time else None, - datetime.fromisoformat(file.access_time) if file.access_time else None, - datetime.fromisoformat(file.modification_time) if file.modification_time else None, - ), - ) - conn.commit() - - logger.info("Successfully stored file data in PostgreSQL", object_id=file.object_id) - return {} - - except Exception as e: - logger.exception(e, message="Error indexing file message") - raise - - -@workflow_runtime.activity -def store_file_enriched(ctx, file_enriched): - """Store the file_enriched data in PostgreSQL.""" - try: - with psycopg.connect(postgres_connection_string) as conn: - with conn.cursor() as cur: - # Convert field names to match database schema - insert_query = """ - INSERT INTO files_enriched ( - object_id, agent_id, project, timestamp, expiration, path, - file_name, extension, size, magic_type, mime_type, - is_plaintext, is_container, originating_object_id, - nesting_level, file_creation_time, file_access_time, - file_modification_time, security_info, hashes - ) VALUES ( - %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, - %s, %s, %s, %s, %s, %s - ) - ON CONFLICT (object_id) DO UPDATE SET - agent_id = EXCLUDED.agent_id, - project = EXCLUDED.project, - timestamp = EXCLUDED.timestamp, - expiration = EXCLUDED.expiration, - path = EXCLUDED.path, - file_name = EXCLUDED.file_name, - extension = EXCLUDED.extension, - size = EXCLUDED.size, - magic_type = EXCLUDED.magic_type, - mime_type = EXCLUDED.mime_type, - is_plaintext = EXCLUDED.is_plaintext, - is_container = EXCLUDED.is_container, - originating_object_id = EXCLUDED.originating_object_id, - nesting_level = EXCLUDED.nesting_level, - file_creation_time = EXCLUDED.file_creation_time, - file_access_time = EXCLUDED.file_access_time, - file_modification_time = EXCLUDED.file_modification_time, - security_info = EXCLUDED.security_info, - hashes = EXCLUDED.hashes, - updated_at = CURRENT_TIMESTAMP - """ - - # Extract filename from path if it exists - file_name = os.path.basename(file_enriched.get("path", "")) if file_enriched.get("path") else None - - cur.execute( - insert_query, - ( - file_enriched["object_id"], - file_enriched.get("agent_id"), - file_enriched.get("project"), - file_enriched.get("timestamp"), - file_enriched.get("expiration"), - file_enriched.get("path"), - file_name, - file_enriched.get("extension"), - file_enriched.get("size"), - file_enriched.get("magic_type"), - file_enriched.get("mime_type"), - file_enriched.get("is_plaintext"), - file_enriched.get("is_container"), - file_enriched.get("originating_object_id"), - file_enriched.get("nesting_level"), - file_enriched.get("file_creation_time"), - file_enriched.get("file_access_time"), - file_enriched.get("file_modification_time"), - json.dumps(file_enriched.get("security_info")) if file_enriched.get("security_info") else None, - json.dumps(file_enriched.get("hashes")) if file_enriched.get("hashes") else None, - ), - ) - conn.commit() - logger.info("Stored file_enriched in PostgreSQL", object_id=file_enriched["object_id"]) - except Exception as e: - logger.exception(e, message="Error storing file_enriched in PostgreSQL", file_enriched=file_enriched) - raise - - -def index_plaintext_content(object_id: str, file_obj: io.TextIOWrapper, max_chunk_bytes: int = 800000): - """Used to index plaintext content with byte-based chunking to avoid tsvector limits""" - logger.info(f"indexing plaintext for {object_id}") - - with psycopg.connect(postgres_connection_string) as conn: - with conn.cursor() as cur: - cur.execute("DELETE FROM plaintext_content WHERE object_id = %s", (object_id,)) - - chunk_number = 0 - insert_query = """ - INSERT INTO plaintext_content (object_id, chunk_number, content) - VALUES (%s, %s, %s); - """ - - # Read file content - file_content = file_obj.read() - - # Process in chunks, ensuring we don't exceed byte limits - i = 0 - while i < len(file_content): - # Take a chunk that's guaranteed to be under the byte limit - chunk_end = min(i + max_chunk_bytes // 4, len(file_content)) # Div by 4 for worst-case UTF-8 - chunk_content = file_content[i:chunk_end] - - # If chunk is still too big in bytes, trim it down - while len(chunk_content.encode("utf-8")) > max_chunk_bytes and chunk_content: - chunk_content = chunk_content[:-100] # Remove 100 chars at a time - - if chunk_content: # Only insert non-empty chunks - actual_bytes = len(chunk_content.encode("utf-8")) - logger.debug(f"Inserting chunk {chunk_number} with {actual_bytes} bytes") - cur.execute(insert_query, (object_id, chunk_number, chunk_content)) - chunk_number += 1 - - # Move to next chunk - i = chunk_end - - conn.commit() - - logger.debug("Indexed chunked content", object_id=object_id, num_chunks=chunk_number) - - -# endregion - -########################################## -# -# region Dapr activities -# -########################################## - - -def get_file_extension(filepath): - # Get just the final filename component of the path - base_name = ntpath.basename(filepath) - - # Split on the last dot, but only if the dot isn't the first character - if base_name.startswith(".") or "." not in base_name: - return "" - - name_parts = base_name.split(".") - if len(name_parts) > 1: - return "." + name_parts[-1] - return "" - - -@workflow_runtime.activity -def get_basic_analysis(ctx, activity_input): - """Perform 'basic' analysis on a file. Run for every file.""" - - object_id = activity_input["object_id"] - path = activity_input.get("path", "") - - # download from Minio as a temporary file which will be cleaned up on exit - with storage.download(object_id) as file: - mime_type = magic.from_file(file.name, mime=True) - if mime_type == "text/plain" or helpers.is_text_file(file.name): - is_plaintext = True - else: - is_plaintext = False - basic_analysis = { - "file_name": ntpath.basename(path), - "extension": get_file_extension(path), - "size": pathlib.Path(file.name).stat().st_size, - "hashes": { - "md5": helpers.calculate_file_hash(file.name, "md5"), - "sha1": helpers.calculate_file_hash(file.name, "sha1"), - "sha256": helpers.calculate_file_hash(file.name, "sha256"), - }, - "magic_type": magic.from_file(file.name), - "mime_type": mime_type, - "is_plaintext": is_plaintext, - "is_container": is_container(mime_type), - } - - return basic_analysis - - -@workflow_runtime.activity -def extract_and_store_features(ctx, activity_input): - """Extract features from a file and store them in PostgreSQL.""" - try: - logger.info("Starting feature extraction") - object_id = activity_input["object_id"] - file_enriched = get_file_enriched(object_id) - - # we only want to process things that were submitted and not things extracted/post-processed - # so things that don't have an originating_object_id - if not file_enriched.originating_object_id: - # Initialize feature extractor - extractor = FileFeatureExtractor() - - # Default timestamp for missing values (Unix epoch) - DEFAULT_TIMESTAMP = datetime(1970, 1, 1, 0, 0, 0, tzinfo=datetime.now().astimezone().tzinfo) - - # Use file timestamps from file_enriched, using epoch if not available - creation_time = file_enriched.creation_time if file_enriched.creation_time else DEFAULT_TIMESTAMP - modification_time = ( - file_enriched.modification_time if file_enriched.modification_time else DEFAULT_TIMESTAMP - ) - access_time = file_enriched.access_time if file_enriched.access_time else DEFAULT_TIMESTAMP - - # Extract features - features = extractor.extract_indivdiual_features( - filepath=file_enriched.path, - size=file_enriched.size, - created_time=creation_time, - modified_time=modification_time, - accessed_time=access_time, - ) - - # Extract version and remove from features dict - features_version = features.pop("_features_version") - - # Create labels dictionary - labels = { - "has_finding": False, - "has_credential": False, - "has_dotnet_vulns": False, - "has_pii": False, - "has_yara_match": False, - "viewed": False, - } - - # Fetch findings from database and update labels - with psycopg.connect(postgres_connection_string) as conn: - with conn.cursor() as cur: - # First get the findings - cur.execute( - """ - SELECT category, finding_name - FROM findings - WHERE object_id = %s - """, - (file_enriched.object_id,), - ) - - findings = cur.fetchall() - - if findings: - labels["has_finding"] = True - for category, finding_name in findings: - if category == "credential": - labels["has_credential"] = True - elif category == "vulnerability" and finding_name == "dotnet_vulns": - labels["has_dotnet_vulns"] = True - elif category == "pii": - labels["has_pii"] = True - elif category == "yara_match": - labels["has_yara_match"] = True - - # Parse timestamps to datetime objects if they're strings - def parse_timestamp(ts): - if isinstance(ts, str): - return datetime.fromisoformat(ts) - return ts - - # Now insert into files_enriched_dataset - query = """ - INSERT INTO files_enriched_dataset ( - object_id, agent_id, project, timestamp, expiration, - path, file_creation_time, file_access_time, file_modification_time, - features_version, individual_features, labels - ) VALUES ( - %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s - ) ON CONFLICT (object_id) DO UPDATE SET - agent_id = EXCLUDED.agent_id, - project = EXCLUDED.project, - timestamp = EXCLUDED.timestamp, - expiration = EXCLUDED.expiration, - path = EXCLUDED.path, - file_creation_time = EXCLUDED.file_creation_time, - file_access_time = EXCLUDED.file_access_time, - file_modification_time = EXCLUDED.file_modification_time, - features_version = EXCLUDED.features_version, - individual_features = EXCLUDED.individual_features, - labels = EXCLUDED.labels; - """ - - cur.execute( - query, - [ - file_enriched.object_id, - file_enriched.agent_id, - file_enriched.project, - parse_timestamp(file_enriched.timestamp), - parse_timestamp(file_enriched.expiration) if file_enriched.expiration else None, - file_enriched.path, - creation_time, - access_time, - modification_time, - features_version, - json.dumps(features), - json.dumps(labels), - ], - ) - conn.commit() - - logger.info("Successfully stored file features in dataset", object_id=file_enriched.object_id) - - except Exception as e: - logger.exception(e, message="Error extracting and storing features", activity_input=activity_input) - raise - - -@workflow_runtime.activity -def publish_findings_alerts(ctx, activity_input): - """ - Activity to publish enriched file data to pubsub after retrieving from state store. - """ - object_id = activity_input["object_id"] - file_enriched = get_file_enriched(object_id) - - # Fetch findings from the database for this object_id - with psycopg.connect(postgres_connection_string) as conn: - with conn.cursor() as cur: - cur.execute( - """ - SELECT finding_name, category, severity, origin_name, raw_data - FROM findings - WHERE object_id = %s - """, - (object_id,), - ) - - findings = cur.fetchall() - - if findings: - with DaprClient() as client: - if file_enriched.path: - file_path = helpers.sanitize_file_path(file_enriched.path) - else: - file_path = "UNKNOWN" - - for finding in findings: - finding_name, category, severity, origin_name, raw_data = finding - - finding_message = f"- *Category:* {category} / *Severity:* {severity}\n" - file_message = f"- *File Path:* {file_path}\n" - nemesis_finding_url = f"{nemesis_url}findings?object_id={file_enriched.object_id}" - nemesis_file_url = f"{nemesis_url}files?object_id={file_enriched.object_id}" - nemesis_footer_finding = f"*<{nemesis_finding_url}|View Finding in Nemesis>* / " - nemesis_footer_file = f"*<{nemesis_file_url}|View File in Nemesis>*\n" - separator = "⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯" - - rule_message = "" - try: - if finding_name == "noseyparker_match" and raw_data: - if "match" in raw_data and "rule_name" in raw_data["match"]: - rule_name = raw_data["match"]["rule_name"] - rule_message = f"- *Rule name:* {rule_name}\n" - except (json.JSONDecodeError, KeyError) as e: - logger.warning("Error processing raw_data for noseyparker_match", error=str(e)) - - body = f"{finding_message}{rule_message}{file_message}{nemesis_footer_finding}{nemesis_footer_file}{separator}" - - alert = Alert(title=finding_name, body=body, service=origin_name) - client.publish_event( - pubsub_name="pubsub", - topic_name="alert", - data=json.dumps(alert.model_dump(exclude_unset=True)), - data_content_type="application/json", - ) - logger.debug("Published alert", alert=alert) - - -@workflow_runtime.activity -def handle_file_if_plaintext(ctx, activity_input): - """ - Activity to index a file's contents if it's plaintext and - send a pub/sub message to NoseyParker - """ - object_id = activity_input["object_id"] - file_enriched = get_file_enriched(object_id) - - # if the file is plaintext, make sure we index it - if file_enriched.is_plaintext: - with storage.download(object_id) as tmp_file: - with open(tmp_file.name, "rb") as binary_file: - with create_text_reader(binary_file) as text_file: - index_plaintext_content(f"{object_id}", text_file) - - nosey_parker_input = NoseyParkerInput(object_id=object_id) - with DaprClient() as client: - client.publish_event( - pubsub_name="pubsub", - topic_name="noseyparker-input", - data=json.dumps(nosey_parker_input.model_dump()), - data_content_type="application/json", - ) - logger.debug(f"Published noseyparker_input: {object_id}") - - -@workflow_runtime.activity -def publish_enriched_file(ctx, activity_input): - """ - Activity to publish enriched file data to pubsub after retrieving from state store. - """ - object_id = activity_input["object_id"] - file_enriched = get_file_enriched(object_id) - - try: - with DaprClient() as client: - data = file_enriched.model_dump( - exclude_unset=True, - mode="json", - ) - - # Publish to pubsub - client.publish_event( - pubsub_name="pubsub", - topic_name="file_enriched", - data=json.dumps(data), - data_content_type="application/json", - ) - - return True - - except Exception as e: - logger.exception(e, message="Error publishing enriched file data", object_id=object_id) - # Don't raise to ensure workflow can complete - return False - - -def create_text_reader(binary_file: BinaryIO) -> io.TextIOWrapper: - """Creates a text reader that handles BOMs and mixed content""" - - bom_check = binary_file.read(4) - binary_file.seek(0) # Reset to start - - if bom_check.startswith(b"\xff\xfe"): - return io.TextIOWrapper(binary_file, encoding="utf-16le") - elif bom_check.startswith(b"\xfe\xff"): - return io.TextIOWrapper(binary_file, encoding="utf-16be") - elif bom_check.startswith(b"\xef\xbb\xbf"): - return io.TextIOWrapper(binary_file, encoding="utf-8-sig") - else: - return io.TextIOWrapper(binary_file, encoding="utf-8", errors="replace") - - # endregion ########################################## @@ -750,154 +75,101 @@ def create_text_reader(binary_file: BinaryIO) -> io.TextIOWrapper: ########################################## -@workflow_runtime.workflow -def enrichment_module_workflow(ctx: wf.DaprWorkflowContext, workflow_input: dict): - """Child workflow that runs enrichment modules with a rolling window of parallel tasks.""" - - object_id = workflow_input["file"]["object_id"] - execution_order = workflow_input["execution_order"] - - try: - logger.info("Module execution order", execution_order=execution_order, instance_id=ctx.instance_id) - - # Track all results - results = [] - # Track in-flight tasks with their module names - in_flight_tasks = {} # task -> module_name mapping - - # execution_order = ["yara"] # for testing a single specific module - - # Process modules with rolling window - for module_name in execution_order: - activity_name = f"enrich_{module_name}" - if activity_name not in activity_functions: - raise KeyError(f"Activity {activity_name} not registered") - - # If we've hit max parallelism, wait for any task to complete - if len(in_flight_tasks) >= max_parallel_enrichment_modules: - # Use when_any to get the first completed task - completed_task = yield wf.when_any(list(in_flight_tasks.keys())) - completed_module = in_flight_tasks[completed_task] - - try: - result = completed_task.get_result() - logger.debug("Module completed", module_name=completed_module, result=result) - results.append((completed_module, result)) - logger.debug(f"Module COMPLETED: {completed_module}") - except Exception as e: - logger.exception(e, message="Error in module execution", module_name=completed_module) - # Continue with other modules even if one fails - results.append((completed_module, None)) - logger.error(f"Module ERROR: {completed_module}") - - # Remove completed task from in_flight - del in_flight_tasks[completed_task] - - # Start new task - task = ctx.call_activity(activity_functions[activity_name], input={"object_id": object_id}) - in_flight_tasks[task] = module_name - logger.debug("Started task for module", module_name=module_name) - - # Wait for remaining tasks to complete - while in_flight_tasks: - completed_task = yield wf.when_any(list(in_flight_tasks.keys())) - completed_module = in_flight_tasks[completed_task] - - try: - result = completed_task.get_result() - logger.debug("Module completed", module_name=completed_module, result=result) - results.append((completed_module, result)) - logger.debug(f"Module COMPLETED: {completed_module}") - except Exception as e: - logger.exception(e, message="Error in module execution", module_name=completed_module) - results.append((completed_module, None)) - logger.warning(f"Module ERROR: {completed_module}") - - del in_flight_tasks[completed_task] - - logger.info("All modules completed", total_modules=len(results), results=results) - - return results - - except Exception as e: - logger.error(f"Error in workflow execution: {str(e)}") - raise - - -@workflow_runtime.workflow +@wf_runtime.workflow def enrichment_workflow(ctx: wf.DaprWorkflowContext, workflow_input: dict): """Main workflow that orchestrates all enrichment activities.""" - logger.info("Starting main enrichment workflow") + if not ctx.is_replaying: + logger.debug("Starting main enrichment workflow", workflow_input=workflow_input) + + start_time = ctx.current_utc_datetime try: input_file = workflow_input["file"] object_id = input_file["object_id"] # the only guaranteed field to exist - logger.debug("Workflow input", object_id=object_id, workflow_input=workflow_input, instance_id=ctx.instance_id) + if not ctx.is_replaying: + logger.debug("Workflow input", object_id=object_id, workflow_input=workflow_input) - initial_tasks = [ - ctx.call_activity(index_file_message, input=input_file), - ctx.call_activity(get_basic_analysis, input=input_file), - ] - - try: - initial_tasks_results = yield wf.when_all(initial_tasks) - basic_analysis = initial_tasks_results[1] - logger.debug("Initial tasks complete - index_file_message, get_basic_analysis") - except Exception as e: - logger.exception("Error in index_file_message or get_basic_analysis", error=str(e)) - raise - - # create file_enriched object - file_enriched = { - **input_file, - **basic_analysis, - } - - # store the basic file analysis into the file_enriched object in Postgres - try: - yield ctx.call_activity(store_file_enriched, input=file_enriched) - logger.debug("Stored file_enriched in PostgreSQL") - except Exception: - logger.exception( - "Error in initial tasks - calling store_file_enriched_postgres", store_file_enriched=store_file_enriched + file_enriched = yield ctx.call_activity(get_basic_analysis, input=input_file) + if not ctx.is_replaying: + logger.debug( + "get_basic_analysis complete", + processing_time=f"{ctx.current_utc_datetime - start_time}", ) - raise enrichment_tasks = [ ctx.call_activity(handle_file_if_plaintext, input=file_enriched), - ctx.call_child_workflow(workflow=enrichment_module_workflow, input=workflow_input), + ctx.call_activity(check_file_linkings, input={"object_id": object_id}), + ctx.call_activity( + run_enrichment_modules, + input={"object_id": object_id, "execution_order": workflow_input["execution_order"]}, + ), ] try: yield wf.when_all(enrichment_tasks) - logger.debug("Enrichment tasks complete - handle_file_if_plaintext, enrichment_module_workflow") + + if not ctx.is_replaying: + logger.debug( + "Enrichment tasks complete - handle_file_if_plaintext, enrichment_module_workflow", + processing_time=f"{ctx.current_utc_datetime - start_time}", + ) except Exception as e: + import traceback + + # Extract detailed information about each task + task_details = [] + for i, task in enumerate(enrichment_tasks): + task_info = { + "index": i, + "type": type(task).__name__, + "repr": repr(task), + } + # Try to extract activity name if available + if hasattr(task, "_activity_name"): + task_info["activity_name"] = task._activity_name + if hasattr(task, "_input"): + task_info["input"] = str(task._input)[:200] # Truncate long inputs + task_details.append(task_info) + logger.exception( - "Error in enrichment tasks - handle_file_if_plaintext or enrichment_module_workflow", error=str(e) + "Error in enrichment tasks - handle_file_if_plaintext or enrichment_module_workflow", + error=str(e), + error_type=type(e).__name__, + error_args=e.args, + traceback=traceback.format_exc(), + enrichment_tasks_count=len(enrichment_tasks), + enrichment_tasks_details=task_details, ) raise final_tasks = [ ctx.call_activity(publish_enriched_file, input={"object_id": object_id}), - ctx.call_activity(extract_and_store_features, input={"object_id": object_id}), + # ctx.call_activity(extract_and_store_features, input={"object_id": object_id}), ctx.call_activity(publish_findings_alerts, input={"object_id": object_id}), ] try: yield wf.when_all(final_tasks) - logger.debug( - "Final tasks complete - publish_enriched_file, extract_and_store_features, publish_findings_alerts" - ) + + if not ctx.is_replaying: + logger.debug( + "Final tasks complete - publish_enriched_file, publish_findings_alerts", + processing_time=f"{ctx.current_utc_datetime - start_time}", + ) except Exception as e: logger.exception( - "Error in final tasks - publish_enriched_file, extract_and_store_features, publish_findings_alerts", + "Error in final tasks - publish_enriched_file, publish_findings_alerts", error=str(e), ) raise - logger.info("Workflow completed successfully", object_id=object_id) + if not ctx.is_replaying: + logger.debug( + "Workflow completed successfully", + processing_time=f"{ctx.current_utc_datetime - start_time}", + ) return {} except Exception: @@ -905,6 +177,51 @@ def enrichment_workflow(ctx: wf.DaprWorkflowContext, workflow_input: dict): raise +@wf_runtime.workflow +def single_enrichment_workflow(ctx: wf.DaprWorkflowContext, workflow_input: dict): + """Lightweight workflow that runs a single enrichment module for bulk operations.""" + + try: + enrichment_name = workflow_input["enrichment_name"] + object_id = workflow_input["object_id"] + + if not ctx.is_replaying: + logger.debug( + "Starting single enrichment workflow", + enrichment_name=enrichment_name, + object_id=object_id, + instance_id=ctx.instance_id, + ) + + # Get the activity name for this enrichment + activity_name = f"enrich_{enrichment_name}" + if activity_name not in global_vars.activity_functions: + raise KeyError(f"Activity {activity_name} not registered") + + # Run the single enrichment activity + result = yield ctx.call_activity(global_vars.activity_functions[activity_name], input={"object_id": object_id}) + + if not ctx.is_replaying: + logger.debug( + "Single enrichment workflow completed", + enrichment_name=enrichment_name, + object_id=object_id, + result=result, + instance_id=ctx.instance_id, + ) + + return result + + except Exception as e: + logger.exception( + "Error in single enrichment workflow", + enrichment_name=enrichment_name if "enrichment_name" in locals() else "unknown", + object_id=object_id if "object_id" in locals() else "unknown", + error=str(e), + ) + raise + + # endregion ########################################## @@ -914,69 +231,197 @@ def enrichment_workflow(ctx: wf.DaprWorkflowContext, workflow_input: dict): ########################################## -async def initialize_workflow_runtime(): +async def initialize_workflow_runtime(dpapi_manager: DpapiManager): """Initialize the workflow runtime and load modules. Returns the execution order for modules.""" - global workflow_runtime, workflow_client + + global wf_runtime, asyncio_loop # Load enrichment modules module_loader = ModuleLoader() await module_loader.load_modules() - workflow_runtime.modules = module_loader.modules + # Update the global_module_map in the enrichment_modules activity + + global_vars.global_module_map = module_loader.modules + + asyncio_loop = asyncio.get_running_loop() # Filter modules by workflow and determine execution order workflow_name = "default" # This could be made configurable later available_modules = { name: module - for name, module in workflow_runtime.modules.items() + for name, module in module_loader.modules.items() if hasattr(module, "workflows") and workflow_name in module.workflows } + # janky pass-through for any modules that have a 'dpapi_manager' property + for module in module_loader.modules.values(): + if hasattr(module, "dpapi_manager") and module.dpapi_manager is None: + logger.debug(f"Setting 'dpapi_manager' for '{module}'") + module.dpapi_manager = dpapi_manager # type: ignore + module.loop = asyncio.get_running_loop() # type: ignore + elif hasattr(wf_runtime, "dpapi_manager"): + logger.debug(f"'dpapi_manager' already set for for '{module}'") + # Build dependency graph from filtered modules graph = build_dependency_graph(available_modules) execution_order = topological_sort(graph) + + # execution_order = ["yara"] # for testing a single specific module + logger.info( "Determined module execution order", execution_order=execution_order, workflow=workflow_name, total_modules=len(available_modules), - filtered_from=len(workflow_runtime.modules), + filtered_from=len(module_loader.modules), ) - # Register each module as an activity - for module_name in available_modules.keys(): - activity_name = create_enrichment_activity(module_name) - logger.info("Registered activity", activity_name=activity_name) + logger.info("Modules loaded and ready for processing", total_modules=len(available_modules)) - # Start workflow runtime - workflow_runtime.start() - - # Initialize workflow client - workflow_client = wf.DaprWorkflowClient( - logger_options=LoggerOptions( - log_level=workflow_client_log_level, - log_handler=log_handler, - log_formatter=log_formatter, - ) - ) + wf_runtime.start() return execution_order -def shutdown_workflow_runtime(): - """Shutdown the workflow runtime""" - if workflow_runtime: - workflow_runtime.shutdown() - - -def get_workflow_client() -> wf.DaprWorkflowClient: - """Get the workflow client instance""" - return workflow_client - - def reload_yara_rules(): """Reloads all disk/state yara rules.""" + logger.debug("workflow/workflow.py reloading Yara rules") - workflow_runtime.modules["yara"].rule_manager.load_rules() + rule_manager = global_vars.global_module_map["yara"] + + if not isinstance(rule_manager, YaraRuleManager): + raise ValueError(f"Yara rule manager is incorrect type. Type: {type(rule_manager)}") + + rule_manager.load_rules() # endregion + + +# @workflow_runtime.activity +# async def extract_and_store_features(ctx, activity_input): +# """Extract features from a file and store them in PostgreSQL.""" +# try: +# logger.info("Starting feature extraction") +# object_id = activity_input["object_id"] +# file_enriched = get_file_enriched(object_id) + +# # we only want to process things that were submitted and not things extracted/post-processed +# # so things that don't have an originating_object_id +# if not file_enriched.originating_object_id: +# # Initialize feature extractor +# extractor = FileFeatureExtractor() + +# # Default timestamp for missing values (Unix epoch) +# DEFAULT_TIMESTAMP = datetime(1970, 1, 1, 0, 0, 0, tzinfo=datetime.now().astimezone().tzinfo) + +# # Use file timestamps from file_enriched, using epoch if not available +# creation_time = file_enriched.creation_time if file_enriched.creation_time else DEFAULT_TIMESTAMP +# modification_time = ( +# file_enriched.modification_time if file_enriched.modification_time else DEFAULT_TIMESTAMP +# ) +# access_time = file_enriched.access_time if file_enriched.access_time else DEFAULT_TIMESTAMP + +# # Extract features +# features = extractor.extract_indivdiual_features( +# filepath=file_enriched.path, +# size=file_enriched.size, +# created_time=creation_time, +# modified_time=modification_time, +# accessed_time=access_time, +# ) + +# # Extract version and remove from features dict +# features_version = features.pop("_features_version") + +# # Create labels dictionary +# labels = { +# "has_finding": False, +# "has_credential": False, +# "has_dotnet_vulns": False, +# "has_pii": False, +# "has_yara_match": False, +# "viewed": False, +# } + +# # Fetch findings from database and update labels +# with psycopg.connect(postgres_connection_string) as conn: +# with conn.cursor() as cur: +# # First get the findings +# cur.execute( +# """ +# SELECT category, finding_name +# FROM findings +# WHERE object_id = %s +# """, +# (file_enriched.object_id,), +# ) + +# findings = cur.fetchall() + +# if findings: +# labels["has_finding"] = True +# for category, finding_name in findings: +# if category == "credential": +# labels["has_credential"] = True +# elif category == "vulnerability" and finding_name == "dotnet_vulns": +# labels["has_dotnet_vulns"] = True +# elif category == "pii": +# labels["has_pii"] = True +# elif category == "yara_match": +# labels["has_yara_match"] = True + +# # Parse timestamps to datetime objects if they're strings +# def parse_timestamp(ts): +# if isinstance(ts, str): +# return datetime.fromisoformat(ts) +# return ts + +# # Now insert into files_enriched_dataset +# query = """ +# INSERT INTO files_enriched_dataset ( +# object_id, agent_id, source, project, timestamp, expiration, +# path, file_creation_time, file_access_time, file_modification_time, +# features_version, individual_features, labels +# ) VALUES ( +# %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s +# ) ON CONFLICT (object_id) DO UPDATE SET +# agent_id = EXCLUDED.agent_id, +# source = EXCLUDED.source, +# project = EXCLUDED.project, +# timestamp = EXCLUDED.timestamp, +# expiration = EXCLUDED.expiration, +# path = EXCLUDED.path, +# file_creation_time = EXCLUDED.file_creation_time, +# file_access_time = EXCLUDED.file_access_time, +# file_modification_time = EXCLUDED.file_modification_time, +# features_version = EXCLUDED.features_version, +# individual_features = EXCLUDED.individual_features, +# labels = EXCLUDED.labels; +# """ + +# cur.execute( +# query, +# [ +# file_enriched.object_id, +# file_enriched.agent_id, +# file_enriched.source, +# file_enriched.project, +# parse_timestamp(file_enriched.timestamp), +# parse_timestamp(file_enriched.expiration) if file_enriched.expiration else None, +# file_enriched.path, +# creation_time, +# access_time, +# modification_time, +# features_version, +# json.dumps(features), +# json.dumps(labels), +# ], +# ) +# conn.commit() + +# logger.info("Successfully stored file features in dataset", object_id=file_enriched.object_id) + +# except Exception as e: +# logger.exception(e, message="Error extracting and storing features", activity_input=activity_input) +# raise diff --git a/projects/file_enrichment/file_enrichment/workflow_manager.py b/projects/file_enrichment/file_enrichment/workflow_manager.py new file mode 100644 index 0000000..1cdcbb5 --- /dev/null +++ b/projects/file_enrichment/file_enrichment/workflow_manager.py @@ -0,0 +1,753 @@ +# src/workflow/workflow_manager.py +import asyncio +import json +import os +import time +import uuid +from datetime import datetime + +import asyncpg +from common.logger import get_logger +from common.models import SingleEnrichmentWorkflowInput +from dapr.clients import DaprClient +from dapr.ext.workflow.workflow_state import WorkflowStatus + +from .global_vars import workflow_client +from .tracing import get_trace_injector, get_tracer +from .workflow import enrichment_workflow + +logger = get_logger(__name__) + + +class WorkflowManager: + """WorkflowManager for workflow execution.""" + + def __init__(self, pool: asyncpg.Pool, max_execution_time=300): + """Initialize the workflow manager + + Args: + pool: asyncpg connection pool (externally managed) + max_execution_time: maximum time (in seconds) until a workflow is killed + """ + self.active_workflows = {} # {instance_id: workflow_info} + self.lock = asyncio.Lock() # For synchronizing access to active_workflows + self.max_execution_time = max_execution_time + self.background_tasks = set() # Track background tasks to prevent GC + self.pool = pool + + async def __aenter__(self): + """Async context manager entry - start background tasks""" + + # Start background cleanup task + cleanup_task = asyncio.create_task(self._cleanup_loop()) + self.background_tasks.add(cleanup_task) + cleanup_task.add_done_callback(self.background_tasks.discard) + + logger.info("WorkflowManager fully initialized") + + return self + + async def __aexit__(self, exc_type, exc_val, exc_tb): + """Async context manager exit - cleanup background tasks (pool is externally managed)""" + logger.info("Cleaning up WorkflowManager...") + + # Cancel all background tasks + for task in self.background_tasks: + if not task.done(): + task.cancel() + + # Wait for all tasks to complete/cancel + if self.background_tasks: + await asyncio.gather(*self.background_tasks, return_exceptions=True) + + self.background_tasks.clear() + logger.info("WorkflowManager cleanup completed") + + return False # Don't suppress exceptions + + async def _cleanup_loop(self): + """Run cleanup_stale_workflows every 60 seconds""" + while True: + await asyncio.sleep(60) + try: + await self.cleanup_stale_workflows() + except Exception as e: + logger.error(f"Background cleanup error: {e}") + + def _get_status_string(self, state_obj): + """Convert workflow state to string""" + if state_obj.runtime_status == WorkflowStatus.FAILED: + logger.warning( + "Workflow failed", + instance_id=state_obj.instance_id, + error=state_obj.failure_details.message if state_obj.failure_details else "Unknown", + pid=os.getpid(), + ) + + return state_obj.runtime_status.name + + async def publish_workflow_completion(self, instance_id, completed=True): + """ + Publish workflow completion event for container tracking. + + These events are consumed by the web_api so we can track the state of + large container processing. + + TODO: any way to eliminate the database reads by using the internal state? + + Args: + instance_id: The workflow instance ID + completed: True if workflow completed successfully, False if failed + """ + + try: + async with self.pool.acquire() as conn: + # Get object_id from workflow + row = await conn.fetchrow( + """ + SELECT object_id FROM workflows WHERE wf_id = $1 + """, + instance_id, + ) + + if not row or not row["object_id"]: + object_id, originating_container_id, file_size = None, None, 0 + else: + object_id = row["object_id"] + + # Get originating_container_id and file size from files table + file_row = await conn.fetchrow( + """ + SELECT fe.originating_container_id, fe.size + FROM files_enriched fe + WHERE fe.object_id = $1 + """, + object_id, + ) + + if file_row: + originating_container_id = file_row["originating_container_id"] + file_size = file_row["size"] or 0 + else: + # Fallback to files table if not in files_enriched yet + fallback_row = await conn.fetchrow( + """ + SELECT f.originating_container_id, 0 as size + FROM files f + WHERE f.object_id = $1 + """, + object_id, + ) + + if fallback_row: + originating_container_id = fallback_row["originating_container_id"] + file_size = fallback_row["size"] or 0 + else: + originating_container_id = None + file_size = 0 + logger.debug( + f"publish_workflow_completion - object_id: {object_id}, originating_container_id: {originating_container_id}, file_size: {file_size}", + pid=os.getpid(), + ) + + # Only publish if we have a container ID to track + if object_id and originating_container_id: + with DaprClient(headers_callback=get_trace_injector()) as client: + completion_data = { + "object_id": str(object_id), + "originating_container_id": str(originating_container_id), + "workflow_id": instance_id, + "completed": completed, + "file_size": file_size, + "timestamp": datetime.now().isoformat(), + } + + client.publish_event( + pubsub_name="pubsub", + topic_name="workflow-completed", + data=json.dumps(completion_data), + data_content_type="application/json", + ) + + logger.debug( + "Published workflow completion event", + object_id=object_id, + container_id=originating_container_id, + completed=completed, + workflow_id=instance_id, + pid=os.getpid(), + ) + + except Exception as e: + logger.error("Error publishing workflow completion event", workflow_id=instance_id, error=str(e)) + + async def cleanup_stale_workflows(self): + """Clean up workflows that were left running from previous service instances""" + try: + async with self.pool.acquire() as conn: + # Find workflows that have been running for longer than max execution time + stale_workflows = await conn.fetch( + """ + SELECT wf_id, object_id, + EXTRACT(EPOCH FROM (CURRENT_TIMESTAMP - start_time)) as runtime_seconds + FROM workflows + WHERE status = 'RUNNING' + AND EXTRACT(EPOCH FROM (CURRENT_TIMESTAMP - start_time)) > $1 + """, + self.max_execution_time, + ) + + if stale_workflows: + logger.warning(f"Found {len(stale_workflows)} stale workflows, cleaning up...") + + for wf_id, _object_id, runtime_seconds in stale_workflows: + logger.info(f"Cleaning up stale workflow {wf_id}, runtime: {runtime_seconds:.2f}s") + + # Try to terminate the workflow in Dapr + try: + await asyncio.to_thread(workflow_client.terminate_workflow, wf_id) + except Exception as e: + logger.warning(f"Could not terminate workflow {wf_id}: {e}") + + # Update database status + await self.update_workflow_status( + wf_id, "TIMEOUT", runtime_seconds, "cleaned up by cleanup_stale_workflows" + ) + + # Publish completion event + await self.publish_workflow_completion(wf_id, completed=False) + + except Exception as e: + logger.error(f"Error during stale workflow cleanup: {e}") + + async def update_workflow_status(self, instance_id, status, runtime_seconds=None, error_message=None): + """ + Generalized function to update workflow status in database. + + Args: + instance_id: The workflow instance ID + status: The status to set (COMPLETED, FAILED, ERROR, TIMEOUT, etc.) + runtime_seconds: Runtime in seconds (optional) + error_message: Error message to append to enrichments_failure (optional) + """ + + try: + async with self.pool.acquire() as conn: + if error_message: + # Update with error message appended to enrichments_failure + await conn.execute( + """ + UPDATE workflows + SET status = $1, + runtime_seconds = COALESCE($2, runtime_seconds), + enrichments_failure = array_append(enrichments_failure, $3) + WHERE wf_id = $4 + """, + status, + runtime_seconds, + error_message[:100], + instance_id, + ) + else: + # Update without modifying enrichments_failure + await conn.execute( + """ + UPDATE workflows + SET status = $1, + runtime_seconds = COALESCE($2, runtime_seconds) + WHERE wf_id = $3 + """, + status, + runtime_seconds, + instance_id, + ) + + logger.debug( + "Updated workflow status", + instance_id=instance_id, + status=status, + runtime_seconds=runtime_seconds, + has_error=bool(error_message), + pid=os.getpid(), + ) + except Exception as e: + logger.error( + "Failed to update workflow status in database", + instance_id=instance_id, + status=status, + error=str(e), + pid=os.getpid(), + ) + + async def reset(self): + """Reset the workflow manager's state.""" + async with self.lock: + # Clear active workflows + self.active_workflows.clear() + + # Reset workflows in database + try: + async with self.pool.acquire() as conn: + # Clear existing workflows + # TODO: should this only include running workflows? + await conn.execute("DELETE FROM workflows") + except Exception as e: + logger.exception(e, message="Error resetting workflows in database") + + logger.info("WorkflowManager reset", active_count=len(self.active_workflows)) + + return { + "status": "success", + "message": "Workflow manager reset successfully", + "timestamp": datetime.now().isoformat(), + } + + async def start_workflow(self, workflow_input): + """Start a workflow""" + start_time = time.time() + tracer = get_tracer() + + try: + # Generate the workflow ID first so we can schedule the workflow after + # initializing it in the database + instance_id = str(uuid.uuid4()).replace("-", "") + + with tracer.start_as_current_span("start_workflow") as current_span: + # Add workflow ID to trace for Jaeger queries + current_span.set_attribute("workflow.instance_id", instance_id) + current_span.set_attribute("workflow.type", "enrichment_workflow") + + if "file" in workflow_input and "object_id" in workflow_input["file"]: + current_span.set_attribute("workflow.object_id", workflow_input["file"]["object_id"]) + + # Extract metadata for tracking + base_filename = None + object_id = None + if "file" in workflow_input: + if "path" in workflow_input["file"]: + filepath = workflow_input["file"]["path"] + base_filename = os.path.basename(filepath) + if "object_id" in workflow_input["file"]: + object_id = workflow_input["file"].get("object_id") + + # Store workflow in database + async with self.pool.acquire() as conn: + await conn.execute( + """ + INSERT INTO workflows (wf_id, object_id, filename, status, start_time) + VALUES ($1, $2, $3, $4, $5) + """, + instance_id, + object_id, + base_filename, + "RUNNING", + datetime.fromtimestamp(start_time), + ) + + # Add to active workflows tracking + async with self.lock: + self.active_workflows[instance_id] = { + "object_id": object_id, + "start_time": start_time, + "filename": base_filename, + } + + logger.info( + "Scheduling workflow", + instance_id=instance_id, + object_id=object_id, + active_count=len(self.active_workflows), + pid=os.getpid(), + ) + + # Actually schedule the workflow + await asyncio.to_thread( + workflow_client.schedule_new_workflow, + instance_id=instance_id, + workflow=enrichment_workflow, + input=workflow_input, + ) + + # Start a task to monitor this workflow for completion/failure/timeout + monitor_task = asyncio.create_task(self._monitor_workflow(instance_id, start_time)) + self.background_tasks.add(monitor_task) + monitor_task.add_done_callback(self.background_tasks.discard) + + return instance_id + + except Exception as e: + logger.exception(e, message="Error starting workflow") + raise + + async def _monitor_workflow(self, instance_id, workflow_start_time: float): + """Monitor a workflow until completion or timeout""" + tracer = get_tracer() + + with tracer.start_as_current_span("monitor_workflow") as current_span: + current_span.set_attribute("workflow.instance_id", instance_id) + current_span.set_attribute("workflow.monitor", True) + + try: + try: + # Use wait_for to implement a timeout + processing_time = time.time() - workflow_start_time + logger.debug( + "Monitoring for workflow completion", + processing_time=f"{processing_time:.4f}s", + ) + + final_status = await asyncio.wait_for( + self._wait_for_completion(instance_id, workflow_start_time), + timeout=self.max_execution_time, + ) + + processing_time = time.time() - workflow_start_time + logger.info( + "Updating workflow status", + processing_time=f"{processing_time:.4f}s", + instance_id=instance_id, + status=final_status, + ) + await self.update_workflow_status(instance_id, final_status, processing_time) + + logger.info( + f"Workflow finished: {'completed successfully' if final_status == 'COMPLETED' else 'completed with failure'}", + instance_id=instance_id, + processing_time=f"{processing_time:.4f}s", + final_status=final_status, + pid=os.getpid(), + ) + + except TimeoutError: + processing_time = time.time() - workflow_start_time + logger.warning( + "Workflow timed out after exceeding maximum execution time", + instance_id=instance_id, + max_execution_time=f"{self.max_execution_time}s", + actual_time=f"{processing_time:.4f}s", + pid=os.getpid(), + ) + + await asyncio.to_thread( + workflow_client.terminate_workflow, + instance_id, + recursive=True, + ) # recursive == terminate all child workflows + logger.info("Workflow terminated due to timeout", instance_id=instance_id) + + await self.update_workflow_status(instance_id, "TIMEOUT", processing_time, "timeout") + + except Exception as e: + # Handle any other misc. failures + processing_time = time.time() - workflow_start_time + + logger.exception( + "Workflow monitoring failed", + instance_id=instance_id, + processing_time=f"{processing_time:.4f}s", + error=str(e), + pid=os.getpid(), + ) + + # Update workflow status for error + await self.update_workflow_status(instance_id, "ERROR", processing_time, str(e)) + + finally: + # Always clean up + async with self.lock: + if instance_id in self.active_workflows: + del self.active_workflows[instance_id] + + async def _wait_for_completion(self, instance_id, workflow_start_time: float): + """Wait for workflow to complete and return the final status""" + + error_count = 0 + tracer = get_tracer() + + # Add trace attributes for workflow status monitoring + with tracer.start_as_current_span("wait_for_completion") as current_span: + current_span.set_attribute("workflow.instance_id", instance_id) + current_span.set_attribute("workflow.wait_for_completion", True) + + while True: + try: + state = await asyncio.to_thread(workflow_client.get_workflow_state, instance_id) + status = self._get_status_string(state) + error_count = 0 # Reset on successful check + + if status in ["COMPLETED", "FAILED", "TERMINATED", "ERROR"]: + runtime_seconds = time.time() - workflow_start_time + logger.info( + "Workflow finished", + instance_id=instance_id, + final_status=status, + runtime=f"{runtime_seconds:.4f}s", + pid=os.getpid(), + ) + + # For failed workflows, capture the error message and update status + if status in ["FAILED", "TERMINATED", "ERROR"]: + error_msg = "" + if status == "FAILED" and state.failure_details: + error_msg = state.failure_details.message + + logger.debug( + "Updating FAILED workflow status", + processing_time=f"{time.time() - workflow_start_time:.4f}s", + ) + await self.update_workflow_status( + instance_id, status, runtime_seconds, error_msg[:100] if error_msg else status.lower() + ) + else: + logger.debug( + "Updating SUCCESSFUL workflow status", + processing_time=f"{time.time() - workflow_start_time:.4f}s", + ) + await self.update_workflow_status(instance_id, status, runtime_seconds, "") + + logger.debug( + "Publishing workflow status", + processing_time=f"{time.time() - workflow_start_time:.4f}s", + ) + # Publish workflow completion event for container tracking + await self.publish_workflow_completion(instance_id, status == "COMPLETED") + + logger.debug( + "Done publishing workflow status", + processing_time=f"{time.time() - workflow_start_time:.4f}s", + ) + + # Return the actual status so _monitor_workflow knows what happened + return status + + logger.debug( + "Waiting for workflow completion", + processing_time=f"{time.time() - workflow_start_time:.4f}s", + ) + await asyncio.sleep(5) + + except Exception as e: + # specific case when we're standing the system down, so want to mark this as still running + # [error ] Unhandled RPC error while fetching workflow state: StatusCode.UNAVAILABLE - failed to connect to all addresses; last error: UNKNOWN: ipv4:127.0.0.1:50003: Failed to connect to remote host: connect: Connection refused (111) [DaprWorkflowClient] + if "StatusCode.UNAVAILABLE" in f"{e}": + return "RUNNING" + + error_count += 1 + logger.warning( + f"Error checking workflow status: {str(e)}", + instance_id=instance_id, + error_count=error_count, + pid=os.getpid(), + ) + + if error_count >= 3: # Break after 3 consecutive errors + logger.error( + "Too many consecutive errors checking workflow status", + instance_id=instance_id, + error_count=error_count, + pid=os.getpid(), + ) + # Return ERROR status so the monitoring can handle it appropriately + return "ERROR" + + await asyncio.sleep(0.3) + + async def start_workflow_single_enrichment( + self, workflow_input: SingleEnrichmentWorkflowInput | dict[str, str] + ) -> str: + """Start a single enrichment workflow + + Args: + workflow_input: Input for the single enrichment workflow containing + enrichment_name and object_id + + Returns: + The workflow instance ID (UUID string without hyphens) + + Raises: + Exception: If workflow scheduling fails + """ + tracer = get_tracer() + + try: + start_time = time.time() + + # Generate the workflow ID + instance_id = str(uuid.uuid4()).replace("-", "") + + # Normalize input to SingleEnrichmentWorkflowInput if dict + if isinstance(workflow_input, dict): + workflow_input = SingleEnrichmentWorkflowInput(**workflow_input) + + with tracer.start_as_current_span("start_single_enrichment_workflow") as span: + # Add workflow ID to trace for Jaeger queries + span.set_attribute("workflow.instance_id", instance_id) + span.set_attribute("workflow.start", True) + span.set_attribute("workflow.type", "single_enrichment_workflow") + span.set_attribute("workflow.enrichment_name", workflow_input.enrichment_name) + span.set_attribute("workflow.object_id", workflow_input.object_id) + + # Extract metadata for tracking + enrichment_name = workflow_input.enrichment_name + object_id = workflow_input.object_id + + # Store workflow in database (simplified - just for monitoring) + async with self.pool.acquire() as conn: + await conn.execute( + """ + INSERT INTO workflows (wf_id, object_id, filename, status, start_time) + VALUES ($1, $2, $3, $4, $5) + """, + instance_id, + object_id, + f"bulk:{enrichment_name} ({object_id})", # Use enrichment name as filename + "RUNNING", + datetime.fromtimestamp(start_time), + ) + + # Add to active workflows tracking + async with self.lock: + self.active_workflows[instance_id] = { + "object_id": object_id, + "start_time": start_time, + "filename": f"bulk:{enrichment_name} ({object_id})", + "enrichment_name": enrichment_name, + } + + logger.debug( + "Triggering single enrichment workflow", + instance_id=instance_id, + enrichment_name=enrichment_name, + object_id=object_id, + active_count=len(self.active_workflows), + pid=os.getpid(), + ) + + # Actually schedule the workflow + # Import here to avoid circular import + from .workflow import single_enrichment_workflow + + # Convert Pydantic model to dict for Dapr workflow + workflow_input_dict = ( + workflow_input.model_dump() if isinstance(workflow_input, SingleEnrichmentWorkflowInput) else workflow_input + ) + + # Use asyncio.to_thread() to prevent blocking the event loop + await asyncio.to_thread( + workflow_client.schedule_new_workflow, + instance_id=instance_id, + workflow=single_enrichment_workflow, + input=workflow_input_dict, + ) + + # Start a task to monitor this workflow for completion/failure/timeout + monitor_task = asyncio.create_task(self._monitor_single_enrichment_workflow(instance_id, start_time)) + self.background_tasks.add(monitor_task) + monitor_task.add_done_callback(self.background_tasks.discard) + + return instance_id + + except Exception as e: + logger.exception(e, message="Error starting single enrichment workflow") + raise + + async def _monitor_single_enrichment_workflow(self, instance_id: str, workflow_start_time: float) -> None: + """Monitor a single enrichment workflow until completion or timeout + + Args: + instance_id: The workflow instance ID (UUID string without hyphens) + workflow_start_time: Timestamp when the workflow was started (from time.time()) + """ + tracer = get_tracer() + + with tracer.start_as_current_span("monitor_single_enrichment_workflow") as current_span: + current_span.set_attribute("workflow.instance_id", instance_id) + current_span.set_attribute("workflow.monitor", True) + + try: + try: + # Use wait_for to implement a timeout + final_status = await asyncio.wait_for( + self._wait_for_completion(instance_id, workflow_start_time), + timeout=self.max_execution_time, + ) + + processing_time = time.time() - workflow_start_time + + await self.update_workflow_status(instance_id, final_status, processing_time) + + logger.info( + "Single enrichment workflow completed", + instance_id=instance_id, + processing_time=f"{processing_time:.4f}s", + final_status=final_status, + pid=os.getpid(), + ) + + except TimeoutError: + processing_time = time.time() - workflow_start_time + + logger.warning( + "Single enrichment workflow timed out after exceeding maximum execution time", + instance_id=instance_id, + max_execution_time=f"{self.max_execution_time}s", + actual_time=f"{processing_time:.4f}s", + pid=os.getpid(), + ) + + try: + await asyncio.to_thread(workflow_client.terminate_workflow, instance_id, recursive=True) + logger.info( + "Single enrichment workflow terminated due to timeout", + instance_id=instance_id, + pid=os.getpid(), + ) + except Exception as e: + logger.error( + "Failed to terminate timed-out single enrichment workflow", + instance_id=instance_id, + error=str(e), + ) + + # Update workflow status for timeout + await self.update_workflow_status(instance_id, "TIMEOUT", processing_time, "timeout") + + except Exception as e: + # Handle any other misc. failures + processing_time = time.time() - workflow_start_time + + logger.exception( + "Single enrichment workflow monitoring failed", + instance_id=instance_id, + processing_time=f"{processing_time:.4f}s", + error=str(e), + pid=os.getpid(), + ) + + # Update workflow status for error + await self.update_workflow_status(instance_id, "ERROR", processing_time, str(e)) + + finally: + # Always clean up + async with self.lock: + if instance_id in self.active_workflows: + del self.active_workflows[instance_id] + + async def cleanup(self): + """Clean up background tasks during shutdown (pool is externally managed)""" + logger.info("Cleaning up WorkflowManager background tasks") + + # Cancel all background tasks + for task in self.background_tasks: + if not task.done(): + task.cancel() + + # Wait for all tasks to complete/cancel + if self.background_tasks: + await asyncio.gather(*self.background_tasks, return_exceptions=True) + + self.background_tasks.clear() + + # Note: Pool is externally managed and will be closed by the caller + + logger.info("WorkflowManager cleanup completed") diff --git a/projects/file_enrichment/file_enrichment/workflow_recovery.py b/projects/file_enrichment/file_enrichment/workflow_recovery.py new file mode 100644 index 0000000..a081167 --- /dev/null +++ b/projects/file_enrichment/file_enrichment/workflow_recovery.py @@ -0,0 +1,121 @@ +import asyncio +import json +import os +import random + +from common.logger import get_logger +from common.models import File +from dapr.clients import DaprClient + +from .tracing import get_trace_injector + +logger = get_logger(__name__) + + +async def recover_interrupted_workflows(pool) -> None: + """ + Recover workflows that were interrupted during system shutdown. + + NOTE/TODO: if using multiple replicas or k8s, this process should be moved + into a single instance and not replicated multiple times + """ + try: + # Tandom sleep delay to help with the worker overlap on recovery + # This, combined with the single atomic DELETE query, should + # ensure that only one worker will recover the workflows. + delay = random.uniform(0, 10) + logger.info(f"Workflow recovery starting in {delay:.1f} seconds...", pid=os.getpid()) + await asyncio.sleep(delay) + + logger.info("Starting workflow recovery process...", pid=os.getpid()) + + # Get interrupted workflows atomically using asyncpg + async with pool.acquire() as conn: + # Atomic DELETE with RETURNING - only one worker will get the interrupted workflows + running_ids = await conn.fetch(""" + DELETE FROM workflows + WHERE status = 'RUNNING' + RETURNING object_id + """) + running_object_ids = [row['object_id'] for row in running_ids] + + if running_object_ids: + logger.info(f"Atomically claimed {len(running_object_ids)} interrupted workflows", pid=os.getpid()) + + if not running_object_ids: + logger.info("No interrupted workflows found", pid=os.getpid()) + return + + logger.info(f"Found {len(running_object_ids)} interrupted workflows to recover", pid=os.getpid()) + + # Get file data and clean up partial results + recovered_files = [] + async with pool.acquire() as conn: + for object_id in running_object_ids: + # Get file data for reconstruction + row = await conn.fetchrow( + """ + SELECT object_id, agent_id, source, project, timestamp, expiration, + path, originating_object_id, originating_container_id, nesting_level, + file_creation_time, file_access_time, file_modification_time + FROM files WHERE object_id = $1 + """, + object_id, + ) + + if row: + # Convert database row to File-compatible dict + file_data = { + "object_id": str(row['object_id']), + "agent_id": row['agent_id'], + "source": row['source'], + "project": row['project'], + "timestamp": row['timestamp'], + "expiration": row['expiration'], + "path": row['path'], + "originating_object_id": str(row['originating_object_id']) if row['originating_object_id'] else None, + "originating_container_id": str(row['originating_container_id']) if row['originating_container_id'] else None, + "nesting_level": row['nesting_level'], + "creation_time": row['file_creation_time'].isoformat() if row['file_creation_time'] else None, + "access_time": row['file_access_time'].isoformat() if row['file_access_time'] else None, + "modification_time": row['file_modification_time'].isoformat() if row['file_modification_time'] else None, + } + recovered_files.append(file_data) + logger.debug("Recovered file data for workflow", object_id=object_id, pid=os.getpid()) + else: + logger.warning("No file data found for workflow", object_id=object_id, pid=os.getpid()) + + if not recovered_files: + logger.warning("No file data found for interrupted workflows", pid=os.getpid()) + return + + # Republish recovered files with priority + with DaprClient(headers_callback=get_trace_injector()) as client: + for file_data in recovered_files: + try: + # Filter out None values for File object creation + clean_file_data = {k: v for k, v in file_data.items() if v is not None} + + # Create File object from recovered data + file_obj = File(**clean_file_data) + + # Publish with priority=3 for immediate processing + client.publish_event( + pubsub_name="pubsub", + topic_name="file", + data=json.dumps(file_obj.model_dump(exclude_unset=True, mode="json")), + data_content_type="application/json", + metadata=(("priority", "3"),), + ) + + logger.info("Republished interrupted workflow", object_id=file_data["object_id"], pid=os.getpid()) + + except Exception as e: + logger.exception(f"Failed to republish workflow {file_data['object_id']}: {e}") + logger.error("File data that caused error", file_data=file_data) + + logger.info(f"Successfully recovered {len(recovered_files)} interrupted workflows", pid=os.getpid()) + + except Exception as e: + logger.exception("Error during workflow recovery", error=str(e), pid=os.getpid()) + # Don't raise - we want the service to continue even if recovery fails diff --git a/projects/file_enrichment/poetry.lock b/projects/file_enrichment/poetry.lock index 5e89a86..f9d1151 100644 --- a/projects/file_enrichment/poetry.lock +++ b/projects/file_enrichment/poetry.lock @@ -2,20 +2,20 @@ [[package]] name = "aesedb" -version = "0.1.6" +version = "0.1.7" description = "NTDS parser toolkit" optional = false python-versions = ">=3.6" groups = ["main"] files = [ - {file = "aesedb-0.1.6-py3-none-any.whl", hash = "sha256:9dad54792b7d792715fd95379516b27e3a31de318199ba7cff51e5d0c8739228"}, + {file = "aesedb-0.1.7-py3-none-any.whl", hash = "sha256:a2612707f9b38c505252a7f56e59c40047d03498b990ca491d519e8a8083eeb8"}, ] [package.dependencies] -aiowinreg = ">=0.0.7" +aiowinreg = ">=0.0.12" colorama = "*" tqdm = "*" -unicrypto = ">=0.0.9" +unicrypto = ">=0.0.11" [[package]] name = "aiohappyeyeballs" @@ -31,103 +31,137 @@ files = [ [[package]] name = "aiohttp" -version = "3.12.13" +version = "3.13.0" description = "Async http client/server framework (asyncio)" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "aiohttp-3.12.13-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5421af8f22a98f640261ee48aae3a37f0c41371e99412d55eaf2f8a46d5dad29"}, - {file = "aiohttp-3.12.13-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0fcda86f6cb318ba36ed8f1396a6a4a3fd8f856f84d426584392083d10da4de0"}, - {file = "aiohttp-3.12.13-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4cd71c9fb92aceb5a23c4c39d8ecc80389c178eba9feab77f19274843eb9412d"}, - {file = "aiohttp-3.12.13-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34ebf1aca12845066c963016655dac897651e1544f22a34c9b461ac3b4b1d3aa"}, - {file = "aiohttp-3.12.13-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:893a4639694c5b7edd4bdd8141be296042b6806e27cc1d794e585c43010cc294"}, - {file = "aiohttp-3.12.13-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:663d8ee3ffb3494502ebcccb49078faddbb84c1d870f9c1dd5a29e85d1f747ce"}, - {file = "aiohttp-3.12.13-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f0f8f6a85a0006ae2709aa4ce05749ba2cdcb4b43d6c21a16c8517c16593aabe"}, - {file = "aiohttp-3.12.13-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1582745eb63df267c92d8b61ca655a0ce62105ef62542c00a74590f306be8cb5"}, - {file = "aiohttp-3.12.13-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d59227776ee2aa64226f7e086638baa645f4b044f2947dbf85c76ab11dcba073"}, - {file = "aiohttp-3.12.13-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:06b07c418bde1c8e737d8fa67741072bd3f5b0fb66cf8c0655172188c17e5fa6"}, - {file = "aiohttp-3.12.13-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:9445c1842680efac0f81d272fd8db7163acfcc2b1436e3f420f4c9a9c5a50795"}, - {file = "aiohttp-3.12.13-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:09c4767af0b0b98c724f5d47f2bf33395c8986995b0a9dab0575ca81a554a8c0"}, - {file = "aiohttp-3.12.13-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f3854fbde7a465318ad8d3fc5bef8f059e6d0a87e71a0d3360bb56c0bf87b18a"}, - {file = "aiohttp-3.12.13-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2332b4c361c05ecd381edb99e2a33733f3db906739a83a483974b3df70a51b40"}, - {file = "aiohttp-3.12.13-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:1561db63fa1b658cd94325d303933553ea7d89ae09ff21cc3bcd41b8521fbbb6"}, - {file = "aiohttp-3.12.13-cp310-cp310-win32.whl", hash = "sha256:a0be857f0b35177ba09d7c472825d1b711d11c6d0e8a2052804e3b93166de1ad"}, - {file = "aiohttp-3.12.13-cp310-cp310-win_amd64.whl", hash = "sha256:fcc30ad4fb5cb41a33953292d45f54ef4066746d625992aeac33b8c681173178"}, - {file = "aiohttp-3.12.13-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7c229b1437aa2576b99384e4be668af1db84b31a45305d02f61f5497cfa6f60c"}, - {file = "aiohttp-3.12.13-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:04076d8c63471e51e3689c93940775dc3d12d855c0c80d18ac5a1c68f0904358"}, - {file = "aiohttp-3.12.13-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:55683615813ce3601640cfaa1041174dc956d28ba0511c8cbd75273eb0587014"}, - {file = "aiohttp-3.12.13-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:921bc91e602d7506d37643e77819cb0b840d4ebb5f8d6408423af3d3bf79a7b7"}, - {file = "aiohttp-3.12.13-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e72d17fe0974ddeae8ed86db297e23dba39c7ac36d84acdbb53df2e18505a013"}, - {file = "aiohttp-3.12.13-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0653d15587909a52e024a261943cf1c5bdc69acb71f411b0dd5966d065a51a47"}, - {file = "aiohttp-3.12.13-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a77b48997c66722c65e157c06c74332cdf9c7ad00494b85ec43f324e5c5a9b9a"}, - {file = "aiohttp-3.12.13-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d6946bae55fd36cfb8e4092c921075cde029c71c7cb571d72f1079d1e4e013bc"}, - {file = "aiohttp-3.12.13-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f95db8c8b219bcf294a53742c7bda49b80ceb9d577c8e7aa075612b7f39ffb7"}, - {file = "aiohttp-3.12.13-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:03d5eb3cfb4949ab4c74822fb3326cd9655c2b9fe22e4257e2100d44215b2e2b"}, - {file = "aiohttp-3.12.13-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:6383dd0ffa15515283c26cbf41ac8e6705aab54b4cbb77bdb8935a713a89bee9"}, - {file = "aiohttp-3.12.13-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:6548a411bc8219b45ba2577716493aa63b12803d1e5dc70508c539d0db8dbf5a"}, - {file = "aiohttp-3.12.13-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:81b0fcbfe59a4ca41dc8f635c2a4a71e63f75168cc91026c61be665945739e2d"}, - {file = "aiohttp-3.12.13-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:6a83797a0174e7995e5edce9dcecc517c642eb43bc3cba296d4512edf346eee2"}, - {file = "aiohttp-3.12.13-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a5734d8469a5633a4e9ffdf9983ff7cdb512524645c7a3d4bc8a3de45b935ac3"}, - {file = "aiohttp-3.12.13-cp311-cp311-win32.whl", hash = "sha256:fef8d50dfa482925bb6b4c208b40d8e9fa54cecba923dc65b825a72eed9a5dbd"}, - {file = "aiohttp-3.12.13-cp311-cp311-win_amd64.whl", hash = "sha256:9a27da9c3b5ed9d04c36ad2df65b38a96a37e9cfba6f1381b842d05d98e6afe9"}, - {file = "aiohttp-3.12.13-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0aa580cf80558557285b49452151b9c69f2fa3ad94c5c9e76e684719a8791b73"}, - {file = "aiohttp-3.12.13-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b103a7e414b57e6939cc4dece8e282cfb22043efd0c7298044f6594cf83ab347"}, - {file = "aiohttp-3.12.13-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:78f64e748e9e741d2eccff9597d09fb3cd962210e5b5716047cbb646dc8fe06f"}, - {file = "aiohttp-3.12.13-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29c955989bf4c696d2ededc6b0ccb85a73623ae6e112439398935362bacfaaf6"}, - {file = "aiohttp-3.12.13-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d640191016763fab76072c87d8854a19e8e65d7a6fcfcbf017926bdbbb30a7e5"}, - {file = "aiohttp-3.12.13-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4dc507481266b410dede95dd9f26c8d6f5a14315372cc48a6e43eac652237d9b"}, - {file = "aiohttp-3.12.13-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8a94daa873465d518db073bd95d75f14302e0208a08e8c942b2f3f1c07288a75"}, - {file = "aiohttp-3.12.13-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:177f52420cde4ce0bb9425a375d95577fe082cb5721ecb61da3049b55189e4e6"}, - {file = "aiohttp-3.12.13-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0f7df1f620ec40f1a7fbcb99ea17d7326ea6996715e78f71a1c9a021e31b96b8"}, - {file = "aiohttp-3.12.13-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3062d4ad53b36e17796dce1c0d6da0ad27a015c321e663657ba1cc7659cfc710"}, - {file = "aiohttp-3.12.13-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:8605e22d2a86b8e51ffb5253d9045ea73683d92d47c0b1438e11a359bdb94462"}, - {file = "aiohttp-3.12.13-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:54fbbe6beafc2820de71ece2198458a711e224e116efefa01b7969f3e2b3ddae"}, - {file = "aiohttp-3.12.13-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:050bd277dfc3768b606fd4eae79dd58ceda67d8b0b3c565656a89ae34525d15e"}, - {file = "aiohttp-3.12.13-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:2637a60910b58f50f22379b6797466c3aa6ae28a6ab6404e09175ce4955b4e6a"}, - {file = "aiohttp-3.12.13-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e986067357550d1aaa21cfe9897fa19e680110551518a5a7cf44e6c5638cb8b5"}, - {file = "aiohttp-3.12.13-cp312-cp312-win32.whl", hash = "sha256:ac941a80aeea2aaae2875c9500861a3ba356f9ff17b9cb2dbfb5cbf91baaf5bf"}, - {file = "aiohttp-3.12.13-cp312-cp312-win_amd64.whl", hash = "sha256:671f41e6146a749b6c81cb7fd07f5a8356d46febdaaaf07b0e774ff04830461e"}, - {file = "aiohttp-3.12.13-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d4a18e61f271127465bdb0e8ff36e8f02ac4a32a80d8927aa52371e93cd87938"}, - {file = "aiohttp-3.12.13-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:532542cb48691179455fab429cdb0d558b5e5290b033b87478f2aa6af5d20ace"}, - {file = "aiohttp-3.12.13-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d7eea18b52f23c050ae9db5d01f3d264ab08f09e7356d6f68e3f3ac2de9dfabb"}, - {file = "aiohttp-3.12.13-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad7c8e5c25f2a26842a7c239de3f7b6bfb92304593ef997c04ac49fb703ff4d7"}, - {file = "aiohttp-3.12.13-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6af355b483e3fe9d7336d84539fef460120c2f6e50e06c658fe2907c69262d6b"}, - {file = "aiohttp-3.12.13-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a95cf9f097498f35c88e3609f55bb47b28a5ef67f6888f4390b3d73e2bac6177"}, - {file = "aiohttp-3.12.13-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b8ed8c38a1c584fe99a475a8f60eefc0b682ea413a84c6ce769bb19a7ff1c5ef"}, - {file = "aiohttp-3.12.13-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a0b9170d5d800126b5bc89d3053a2363406d6e327afb6afaeda2d19ee8bb103"}, - {file = "aiohttp-3.12.13-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:372feeace612ef8eb41f05ae014a92121a512bd5067db8f25101dd88a8db11da"}, - {file = "aiohttp-3.12.13-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a946d3702f7965d81f7af7ea8fb03bb33fe53d311df48a46eeca17e9e0beed2d"}, - {file = "aiohttp-3.12.13-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:a0c4725fae86555bbb1d4082129e21de7264f4ab14baf735278c974785cd2041"}, - {file = "aiohttp-3.12.13-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:9b28ea2f708234f0a5c44eb6c7d9eb63a148ce3252ba0140d050b091b6e842d1"}, - {file = "aiohttp-3.12.13-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d4f5becd2a5791829f79608c6f3dc745388162376f310eb9c142c985f9441cc1"}, - {file = "aiohttp-3.12.13-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:60f2ce6b944e97649051d5f5cc0f439360690b73909230e107fd45a359d3e911"}, - {file = "aiohttp-3.12.13-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:69fc1909857401b67bf599c793f2183fbc4804717388b0b888f27f9929aa41f3"}, - {file = "aiohttp-3.12.13-cp313-cp313-win32.whl", hash = "sha256:7d7e68787a2046b0e44ba5587aa723ce05d711e3a3665b6b7545328ac8e3c0dd"}, - {file = "aiohttp-3.12.13-cp313-cp313-win_amd64.whl", hash = "sha256:5a178390ca90419bfd41419a809688c368e63c86bd725e1186dd97f6b89c2706"}, - {file = "aiohttp-3.12.13-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:36f6c973e003dc9b0bb4e8492a643641ea8ef0e97ff7aaa5c0f53d68839357b4"}, - {file = "aiohttp-3.12.13-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6cbfc73179bd67c229eb171e2e3745d2afd5c711ccd1e40a68b90427f282eab1"}, - {file = "aiohttp-3.12.13-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1e8b27b2d414f7e3205aa23bb4a692e935ef877e3a71f40d1884f6e04fd7fa74"}, - {file = "aiohttp-3.12.13-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eabded0c2b2ef56243289112c48556c395d70150ce4220d9008e6b4b3dd15690"}, - {file = "aiohttp-3.12.13-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:003038e83f1a3ff97409999995ec02fe3008a1d675478949643281141f54751d"}, - {file = "aiohttp-3.12.13-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1b6f46613031dbc92bdcaad9c4c22c7209236ec501f9c0c5f5f0b6a689bf50f3"}, - {file = "aiohttp-3.12.13-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c332c6bb04650d59fb94ed96491f43812549a3ba6e7a16a218e612f99f04145e"}, - {file = "aiohttp-3.12.13-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3fea41a2c931fb582cb15dc86a3037329e7b941df52b487a9f8b5aa960153cbd"}, - {file = "aiohttp-3.12.13-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:846104f45d18fb390efd9b422b27d8f3cf8853f1218c537f36e71a385758c896"}, - {file = "aiohttp-3.12.13-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5d6c85ac7dd350f8da2520bac8205ce99df4435b399fa7f4dc4a70407073e390"}, - {file = "aiohttp-3.12.13-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:5a1ecce0ed281bec7da8550da052a6b89552db14d0a0a45554156f085a912f48"}, - {file = "aiohttp-3.12.13-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:5304d74867028cca8f64f1cc1215eb365388033c5a691ea7aa6b0dc47412f495"}, - {file = "aiohttp-3.12.13-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:64d1f24ee95a2d1e094a4cd7a9b7d34d08db1bbcb8aa9fb717046b0a884ac294"}, - {file = "aiohttp-3.12.13-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:119c79922a7001ca6a9e253228eb39b793ea994fd2eccb79481c64b5f9d2a055"}, - {file = "aiohttp-3.12.13-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:bb18f00396d22e2f10cd8825d671d9f9a3ba968d708a559c02a627536b36d91c"}, - {file = "aiohttp-3.12.13-cp39-cp39-win32.whl", hash = "sha256:0022de47ef63fd06b065d430ac79c6b0bd24cdae7feaf0e8c6bac23b805a23a8"}, - {file = "aiohttp-3.12.13-cp39-cp39-win_amd64.whl", hash = "sha256:29e08111ccf81b2734ae03f1ad1cb03b9615e7d8f616764f22f71209c094f122"}, - {file = "aiohttp-3.12.13.tar.gz", hash = "sha256:47e2da578528264a12e4e3dd8dd72a7289e5f812758fe086473fab037a10fcce"}, + {file = "aiohttp-3.13.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ca69ec38adf5cadcc21d0b25e2144f6a25b7db7bea7e730bac25075bc305eff0"}, + {file = "aiohttp-3.13.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:240f99f88a9a6beb53ebadac79a2e3417247aa756202ed234b1dbae13d248092"}, + {file = "aiohttp-3.13.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a4676b978a9711531e7cea499d4cdc0794c617a1c0579310ab46c9fdf5877702"}, + {file = "aiohttp-3.13.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:48fcdd5bc771cbbab8ccc9588b8b6447f6a30f9fe00898b1a5107098e00d6793"}, + {file = "aiohttp-3.13.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:eeea0cdd2f687e210c8f605f322d7b0300ba55145014a5dbe98bd4be6fff1f6c"}, + {file = "aiohttp-3.13.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:10b3f01d5aeb632adaaf39c5e93f040a550464a768d54c514050c635adcbb9d0"}, + {file = "aiohttp-3.13.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a4dc0b83e25267f42ef065ea57653de4365b56d7bc4e4cfc94fabe56998f8ee6"}, + {file = "aiohttp-3.13.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:72714919ed9b90f030f761c20670e529c4af96c31bd000917dd0c9afd1afb731"}, + {file = "aiohttp-3.13.0-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:564be41e85318403fdb176e9e5b3e852d528392f42f2c1d1efcbeeed481126d7"}, + {file = "aiohttp-3.13.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:84912962071087286333f70569362e10793f73f45c48854e6859df11001eb2d3"}, + {file = "aiohttp-3.13.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:90b570f1a146181c3d6ae8f755de66227ded49d30d050479b5ae07710f7894c5"}, + {file = "aiohttp-3.13.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:2d71ca30257ce756e37a6078b1dff2d9475fee13609ad831eac9a6531bea903b"}, + {file = "aiohttp-3.13.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:cd45eb70eca63f41bb156b7dffbe1a7760153b69892d923bdb79a74099e2ed90"}, + {file = "aiohttp-3.13.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:5ae3a19949a27982c7425a7a5a963c1268fdbabf0be15ab59448cbcf0f992519"}, + {file = "aiohttp-3.13.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ea6df292013c9f050cbf3f93eee9953d6e5acd9e64a0bf4ca16404bfd7aa9bcc"}, + {file = "aiohttp-3.13.0-cp310-cp310-win32.whl", hash = "sha256:3b64f22fbb6dcd5663de5ef2d847a5638646ef99112503e6f7704bdecb0d1c4d"}, + {file = "aiohttp-3.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:f8d877aa60d80715b2afc565f0f1aea66565824c229a2d065b31670e09fed6d7"}, + {file = "aiohttp-3.13.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:99eb94e97a42367fef5fc11e28cb2362809d3e70837f6e60557816c7106e2e20"}, + {file = "aiohttp-3.13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4696665b2713021c6eba3e2b882a86013763b442577fe5d2056a42111e732eca"}, + {file = "aiohttp-3.13.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3e6a38366f7f0d0f6ed7a1198055150c52fda552b107dad4785c0852ad7685d1"}, + {file = "aiohttp-3.13.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aab715b1a0c37f7f11f9f1f579c6fbaa51ef569e47e3c0a4644fba46077a9409"}, + {file = "aiohttp-3.13.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7972c82bed87d7bd8e374b60a6b6e816d75ba4f7c2627c2d14eed216e62738e1"}, + {file = "aiohttp-3.13.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ca8313cb852af788c78d5afdea24c40172cbfff8b35e58b407467732fde20390"}, + {file = "aiohttp-3.13.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6c333a2385d2a6298265f4b3e960590f787311b87f6b5e6e21bb8375914ef504"}, + {file = "aiohttp-3.13.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cc6d5fc5edbfb8041d9607f6a417997fa4d02de78284d386bea7ab767b5ea4f3"}, + {file = "aiohttp-3.13.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7ddedba3d0043349edc79df3dc2da49c72b06d59a45a42c1c8d987e6b8d175b8"}, + {file = "aiohttp-3.13.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:23ca762140159417a6bbc959ca1927f6949711851e56f2181ddfe8d63512b5ad"}, + {file = "aiohttp-3.13.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:bfe824d6707a5dc3c5676685f624bc0c63c40d79dc0239a7fd6c034b98c25ebe"}, + {file = "aiohttp-3.13.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:3c11fa5dd2ef773a8a5a6daa40243d83b450915992eab021789498dc87acc114"}, + {file = "aiohttp-3.13.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:00fdfe370cffede3163ba9d3f190b32c0cfc8c774f6f67395683d7b0e48cdb8a"}, + {file = "aiohttp-3.13.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:6475e42ef92717a678bfbf50885a682bb360a6f9c8819fb1a388d98198fdcb80"}, + {file = "aiohttp-3.13.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:77da5305a410910218b99f2a963092f4277d8a9c1f429c1ff1b026d1826bd0b6"}, + {file = "aiohttp-3.13.0-cp311-cp311-win32.whl", hash = "sha256:2f9d9ea547618d907f2ee6670c9a951f059c5994e4b6de8dcf7d9747b420c820"}, + {file = "aiohttp-3.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:0f19f7798996d4458c669bd770504f710014926e9970f4729cf55853ae200469"}, + {file = "aiohttp-3.13.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1c272a9a18a5ecc48a7101882230046b83023bb2a662050ecb9bfcb28d9ab53a"}, + {file = "aiohttp-3.13.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:97891a23d7fd4e1afe9c2f4473e04595e4acb18e4733b910b6577b74e7e21985"}, + {file = "aiohttp-3.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:475bd56492ce5f4cffe32b5533c6533ee0c406d1d0e6924879f83adcf51da0ae"}, + {file = "aiohttp-3.13.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c32ada0abb4bc94c30be2b681c42f058ab104d048da6f0148280a51ce98add8c"}, + {file = "aiohttp-3.13.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:4af1f8877ca46ecdd0bc0d4a6b66d4b2bddc84a79e2e8366bc0d5308e76bceb8"}, + {file = "aiohttp-3.13.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e04ab827ec4f775817736b20cdc8350f40327f9b598dec4e18c9ffdcbea88a93"}, + {file = "aiohttp-3.13.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a6d9487b9471ec36b0faedf52228cd732e89be0a2bbd649af890b5e2ce422353"}, + {file = "aiohttp-3.13.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e66c57416352f36bf98f6641ddadd47c93740a22af7150d3e9a1ef6e983f9a8"}, + {file = "aiohttp-3.13.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:469167d5372f5bb3aedff4fc53035d593884fff2617a75317740e885acd48b04"}, + {file = "aiohttp-3.13.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a9f3546b503975a69b547c9fd1582cad10ede1ce6f3e313a2f547c73a3d7814f"}, + {file = "aiohttp-3.13.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:6b4174fcec98601f0cfdf308ee29a6ae53c55f14359e848dab4e94009112ee7d"}, + {file = "aiohttp-3.13.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a533873a7a4ec2270fb362ee5a0d3b98752e4e1dc9042b257cd54545a96bd8ed"}, + {file = "aiohttp-3.13.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:ce887c5e54411d607ee0959cac15bb31d506d86a9bcaddf0b7e9d63325a7a802"}, + {file = "aiohttp-3.13.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:d871f6a30d43e32fc9252dc7b9febe1a042b3ff3908aa83868d7cf7c9579a59b"}, + {file = "aiohttp-3.13.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:222c828243b4789d79a706a876910f656fad4381661691220ba57b2ab4547865"}, + {file = "aiohttp-3.13.0-cp312-cp312-win32.whl", hash = "sha256:682d2e434ff2f1108314ff7f056ce44e457f12dbed0249b24e106e385cf154b9"}, + {file = "aiohttp-3.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:0a2be20eb23888df130214b91c262a90e2de1553d6fb7de9e9010cec994c0ff2"}, + {file = "aiohttp-3.13.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:00243e51f16f6ec0fb021659d4af92f675f3cf9f9b39efd142aa3ad641d8d1e6"}, + {file = "aiohttp-3.13.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:059978d2fddc462e9211362cbc8446747ecd930537fa559d3d25c256f032ff54"}, + {file = "aiohttp-3.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:564b36512a7da3b386143c611867e3f7cfb249300a1bf60889bd9985da67ab77"}, + {file = "aiohttp-3.13.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4aa995b9156ae499393d949a456a7ab0b994a8241a96db73a3b73c7a090eff6a"}, + {file = "aiohttp-3.13.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:55ca0e95a3905f62f00900255ed807c580775174252999286f283e646d675a49"}, + {file = "aiohttp-3.13.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:49ce7525853a981fc35d380aa2353536a01a9ec1b30979ea4e35966316cace7e"}, + {file = "aiohttp-3.13.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2117be9883501eaf95503bd313eb4c7a23d567edd44014ba15835a1e9ec6d852"}, + {file = "aiohttp-3.13.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d169c47e40c911f728439da853b6fd06da83761012e6e76f11cb62cddae7282b"}, + {file = "aiohttp-3.13.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:703ad3f742fc81e543638a7bebddd35acadaa0004a5e00535e795f4b6f2c25ca"}, + {file = "aiohttp-3.13.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5bf635c3476f4119b940cc8d94ad454cbe0c377e61b4527f0192aabeac1e9370"}, + {file = "aiohttp-3.13.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:cfe6285ef99e7ee51cef20609be2bc1dd0e8446462b71c9db8bb296ba632810a"}, + {file = "aiohttp-3.13.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:34d8af6391c5f2e69749d7f037b614b8c5c42093c251f336bdbfa4b03c57d6c4"}, + {file = "aiohttp-3.13.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:12f5d820fadc5848d4559ea838aef733cf37ed2a1103bba148ac2f5547c14c29"}, + {file = "aiohttp-3.13.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:0f1338b61ea66f4757a0544ed8a02ccbf60e38d9cfb3225888888dd4475ebb96"}, + {file = "aiohttp-3.13.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:582770f82513419512da096e8df21ca44f86a2e56e25dc93c5ab4df0fe065bf0"}, + {file = "aiohttp-3.13.0-cp313-cp313-win32.whl", hash = "sha256:3194b8cab8dbc882f37c13ef1262e0a3d62064fa97533d3aa124771f7bf1ecee"}, + {file = "aiohttp-3.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:7897298b3eedc790257fef8a6ec582ca04e9dbe568ba4a9a890913b925b8ea21"}, + {file = "aiohttp-3.13.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:c417f8c2e1137775569297c584a8a7144e5d1237789eae56af4faf1894a0b861"}, + {file = "aiohttp-3.13.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:f84b53326abf8e56ebc28a35cebf4a0f396a13a76300f500ab11fe0573bf0b52"}, + {file = "aiohttp-3.13.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:990a53b9d6a30b2878789e490758e568b12b4a7fb2527d0c89deb9650b0e5813"}, + {file = "aiohttp-3.13.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c811612711e01b901e18964b3e5dec0d35525150f5f3f85d0aee2935f059910a"}, + {file = "aiohttp-3.13.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:ee433e594d7948e760b5c2a78cc06ac219df33b0848793cf9513d486a9f90a52"}, + {file = "aiohttp-3.13.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:19bb08e56f57c215e9572cd65cb6f8097804412c54081d933997ddde3e5ac579"}, + {file = "aiohttp-3.13.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f27b7488144eb5dd9151cf839b195edd1569629d90ace4c5b6b18e4e75d1e63a"}, + {file = "aiohttp-3.13.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d812838c109757a11354a161c95708ae4199c4fd4d82b90959b20914c1d097f6"}, + {file = "aiohttp-3.13.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7c20db99da682f9180fa5195c90b80b159632fb611e8dbccdd99ba0be0970620"}, + {file = "aiohttp-3.13.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:cf8b0870047900eb1f17f453b4b3953b8ffbf203ef56c2f346780ff930a4d430"}, + {file = "aiohttp-3.13.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:5b8a5557d5af3f4e3add52a58c4cf2b8e6e59fc56b261768866f5337872d596d"}, + {file = "aiohttp-3.13.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:052bcdd80c1c54b8a18a9ea0cd5e36f473dc8e38d51b804cea34841f677a9971"}, + {file = "aiohttp-3.13.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:76484ba17b2832776581b7ab466d094e48eba74cb65a60aea20154dae485e8bd"}, + {file = "aiohttp-3.13.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:62d8a0adcdaf62ee56bfb37737153251ac8e4b27845b3ca065862fb01d99e247"}, + {file = "aiohttp-3.13.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5004d727499ecb95f7c9147dd0bfc5b5670f71d355f0bd26d7af2d3af8e07d2f"}, + {file = "aiohttp-3.13.0-cp314-cp314-win32.whl", hash = "sha256:a1c20c26af48aea984f63f96e5d7af7567c32cb527e33b60a0ef0a6313cf8b03"}, + {file = "aiohttp-3.13.0-cp314-cp314-win_amd64.whl", hash = "sha256:56f7d230ec66e799fbfd8350e9544f8a45a4353f1cf40c1fea74c1780f555b8f"}, + {file = "aiohttp-3.13.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:2fd35177dc483ae702f07b86c782f4f4b100a8ce4e7c5778cea016979023d9fd"}, + {file = "aiohttp-3.13.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:4df1984c8804ed336089e88ac81a9417b1fd0db7c6f867c50a9264488797e778"}, + {file = "aiohttp-3.13.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:e68c0076052dd911a81d3acc4ef2911cc4ef65bf7cadbfbc8ae762da24da858f"}, + {file = "aiohttp-3.13.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bc95c49853cd29613e4fe4ff96d73068ff89b89d61e53988442e127e8da8e7ba"}, + {file = "aiohttp-3.13.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3b3bdc89413117b40cc39baae08fd09cbdeb839d421c4e7dce6a34f6b54b3ac1"}, + {file = "aiohttp-3.13.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3e77a729df23be2116acc4e9de2767d8e92445fbca68886dd991dc912f473755"}, + {file = "aiohttp-3.13.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e88ab34826d6eeb6c67e6e92400b9ec653faf5092a35f07465f44c9f1c429f82"}, + {file = "aiohttp-3.13.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:019dbef24fe28ce2301419dd63a2b97250d9760ca63ee2976c2da2e3f182f82e"}, + {file = "aiohttp-3.13.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:2c4aeaedd20771b7b4bcdf0ae791904445df6d856c02fc51d809d12d17cffdc7"}, + {file = "aiohttp-3.13.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:b3a8e6a2058a0240cfde542b641d0e78b594311bc1a710cbcb2e1841417d5cb3"}, + {file = "aiohttp-3.13.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:f8e38d55ca36c15f36d814ea414ecb2401d860de177c49f84a327a25b3ee752b"}, + {file = "aiohttp-3.13.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:a921edbe971aade1bf45bcbb3494e30ba6863a5c78f28be992c42de980fd9108"}, + {file = "aiohttp-3.13.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:474cade59a447cb4019c0dce9f0434bf835fb558ea932f62c686fe07fe6db6a1"}, + {file = "aiohttp-3.13.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:99a303ad960747c33b65b1cb65d01a62ac73fa39b72f08a2e1efa832529b01ed"}, + {file = "aiohttp-3.13.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:bb34001fc1f05f6b323e02c278090c07a47645caae3aa77ed7ed8a3ce6abcce9"}, + {file = "aiohttp-3.13.0-cp314-cp314t-win32.whl", hash = "sha256:dea698b64235d053def7d2f08af9302a69fcd760d1c7bd9988fd5d3b6157e657"}, + {file = "aiohttp-3.13.0-cp314-cp314t-win_amd64.whl", hash = "sha256:1f164699a060c0b3616459d13c1464a981fddf36f892f0a5027cbd45121fb14b"}, + {file = "aiohttp-3.13.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:fcc425fb6fd2a00c6d91c85d084c6b75a61bc8bc12159d08e17c5711df6c5ba4"}, + {file = "aiohttp-3.13.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7c2c4c9ce834801651f81d6760d0a51035b8b239f58f298de25162fcf6f8bb64"}, + {file = "aiohttp-3.13.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f91e8f9053a07177868e813656ec57599cd2a63238844393cd01bd69c2e40147"}, + {file = "aiohttp-3.13.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:df46d9a3d78ec19b495b1107bf26e4fcf97c900279901f4f4819ac5bb2a02a4c"}, + {file = "aiohttp-3.13.0-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3b1eb9871cbe43b6ca6fac3544682971539d8a1d229e6babe43446279679609d"}, + {file = "aiohttp-3.13.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:62a3cddf8d9a2eae1f79585fa81d32e13d0c509bb9e7ad47d33c83b45a944df7"}, + {file = "aiohttp-3.13.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0f735e680c323ee7e9ef8e2ea26425c7dbc2ede0086fa83ce9d7ccab8a089f26"}, + {file = "aiohttp-3.13.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6a51839f778b0e283b43cd82bb17f1835ee2cc1bf1101765e90ae886e53e751c"}, + {file = "aiohttp-3.13.0-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ac90cfab65bc281d6752f22db5fa90419e33220af4b4fa53b51f5948f414c0e7"}, + {file = "aiohttp-3.13.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:62fd54f3e6f17976962ba67f911d62723c760a69d54f5d7b74c3ceb1a4e9ef8d"}, + {file = "aiohttp-3.13.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:cf2b60b65df05b6b2fa0d887f2189991a0dbf44a0dd18359001dc8fcdb7f1163"}, + {file = "aiohttp-3.13.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:1ccedfe280e804d9a9d7fe8b8c4309d28e364b77f40309c86596baa754af50b1"}, + {file = "aiohttp-3.13.0-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:ea01ffbe23df53ece0c8732d1585b3d6079bb8c9ee14f3745daf000051415a31"}, + {file = "aiohttp-3.13.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:19ba8625fa69523627b67f7e9901b587a4952470f68814d79cdc5bc460e9b885"}, + {file = "aiohttp-3.13.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:4b14bfae90598d331b5061fd15a7c290ea0c15b34aeb1cf620464bb5ec02a602"}, + {file = "aiohttp-3.13.0-cp39-cp39-win32.whl", hash = "sha256:cf7a4b976da219e726d0043fc94ae8169c0dba1d3a059b3c1e2c964bafc5a77d"}, + {file = "aiohttp-3.13.0-cp39-cp39-win_amd64.whl", hash = "sha256:6b9697d15231aeaed4786f090c9c8bc3ab5f0e0a6da1e76c135a310def271020"}, + {file = "aiohttp-3.13.0.tar.gz", hash = "sha256:378dbc57dd8cf341ce243f13fa1fa5394d68e2e02c15cd5f28eae35a70ec7f67"}, ] [package.dependencies] aiohappyeyeballs = ">=2.5.0" -aiosignal = ">=1.1.2" +aiosignal = ">=1.4.0" attrs = ">=17.3.0" frozenlist = ">=1.1.1" multidict = ">=4.5,<7.0" @@ -135,39 +169,40 @@ propcache = ">=0.2.0" yarl = ">=1.17.0,<2.0" [package.extras] -speedups = ["Brotli", "aiodns (>=3.3.0)", "brotlicffi"] +speedups = ["Brotli", "aiodns (>=3.3.0)", "brotlicffi", "zstandard"] [[package]] name = "aiosignal" -version = "1.3.2" +version = "1.4.0" description = "aiosignal: a list of registered asynchronous callbacks" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "aiosignal-1.3.2-py2.py3-none-any.whl", hash = "sha256:45cde58e409a301715980c2b01d0c28bdde3770d8290b5eb2173759d9acb31a5"}, - {file = "aiosignal-1.3.2.tar.gz", hash = "sha256:a8c255c66fafb1e499c9351d0bf32ff2d8a0321595ebac3b93713656d2436f54"}, + {file = "aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e"}, + {file = "aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7"}, ] [package.dependencies] frozenlist = ">=1.1.0" +typing-extensions = {version = ">=4.2", markers = "python_version < \"3.13\""} [[package]] name = "aiosmb" -version = "0.4.11" +version = "0.4.13" description = "Asynchronous SMB protocol implementation" optional = false python-versions = ">=3.7" groups = ["main"] files = [ - {file = "aiosmb-0.4.11-py3-none-any.whl", hash = "sha256:a3b84893cded7aa1ebf048c0f5267024f2c030e5d918e4d8d8b86f8974a4011a"}, - {file = "aiosmb-0.4.11.tar.gz", hash = "sha256:6d66f51ed2354f76f206613eac0d63f37cfd9ed44be9f8a06594d410244273d7"}, + {file = "aiosmb-0.4.13-py3-none-any.whl", hash = "sha256:ce1feca22650a4af4be68cc286687e9fc3748f1305e033a936d394d44984982e"}, + {file = "aiosmb-0.4.13.tar.gz", hash = "sha256:cad9f3a549ec87029ccd782a34f2f4be8b81312818e76cce34eaf0ada5159e4f"}, ] [package.dependencies] asn1crypto = "*" -asyauth = ">=0.0.16" -asysocks = ">=0.2.9" +asyauth = ">=0.0.22" +asysocks = ">=0.2.17" colorama = "*" cryptography = "*" prompt-toolkit = ">=3.0.2" @@ -175,7 +210,7 @@ six = "*" tqdm = "*" unicrypto = ">=0.0.10" wcwidth = "*" -winacl = ">=0.1.8" +winacl = ">=0.1.9" [[package]] name = "aiowinreg" @@ -206,14 +241,14 @@ files = [ [[package]] name = "anyio" -version = "4.9.0" -description = "High level compatibility layer for multiple asynchronous event loop implementations" +version = "4.11.0" +description = "High-level concurrency and networking framework on top of asyncio or Trio" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "anyio-4.9.0-py3-none-any.whl", hash = "sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c"}, - {file = "anyio-4.9.0.tar.gz", hash = "sha256:673c0c244e15788651a4ff38710fea9675823028a6f08a5eda409e0c9840a028"}, + {file = "anyio-4.11.0-py3-none-any.whl", hash = "sha256:0287e96f4d26d4149305414d4e3bc32f0dcd0862365a4bddea19d7a1ec38c4fc"}, + {file = "anyio-4.11.0.tar.gz", hash = "sha256:82a8d0b81e318cc5ce71a5f1f8b5c4e63619620b63141ef8c995fa0db95a57c4"}, ] [package.dependencies] @@ -222,9 +257,7 @@ sniffio = ">=1.1" typing_extensions = {version = ">=4.5", markers = "python_version < \"3.13\""} [package.extras] -doc = ["Sphinx (>=8.2,<9.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx_rtd_theme"] -test = ["anyio[trio]", "blockbuster (>=1.5.23)", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "trustme", "truststore (>=0.9.1)", "uvloop (>=0.21)"] -trio = ["trio (>=0.26.1)"] +trio = ["trio (>=0.31.0)"] [[package]] name = "argon2-cffi" @@ -243,57 +276,46 @@ argon2-cffi-bindings = "*" [[package]] name = "argon2-cffi-bindings" -version = "21.2.0" +version = "25.1.0" description = "Low-level CFFI bindings for Argon2" optional = false -python-versions = ">=3.6" +python-versions = ">=3.9" groups = ["main"] files = [ - {file = "argon2-cffi-bindings-21.2.0.tar.gz", hash = "sha256:bb89ceffa6c791807d1305ceb77dbfacc5aa499891d2c55661c6459651fc39e3"}, - {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ccb949252cb2ab3a08c02024acb77cfb179492d5701c7cbdbfd776124d4d2367"}, - {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9524464572e12979364b7d600abf96181d3541da11e23ddf565a32e70bd4dc0d"}, - {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b746dba803a79238e925d9046a63aa26bf86ab2a2fe74ce6b009a1c3f5c8f2ae"}, - {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:58ed19212051f49a523abb1dbe954337dc82d947fb6e5a0da60f7c8471a8476c"}, - {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:bd46088725ef7f58b5a1ef7ca06647ebaf0eb4baff7d1d0d177c6cc8744abd86"}, - {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_i686.whl", hash = "sha256:8cd69c07dd875537a824deec19f978e0f2078fdda07fd5c42ac29668dda5f40f"}, - {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:f1152ac548bd5b8bcecfb0b0371f082037e47128653df2e8ba6e914d384f3c3e"}, - {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-win32.whl", hash = "sha256:603ca0aba86b1349b147cab91ae970c63118a0f30444d4bc80355937c950c082"}, - {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-win_amd64.whl", hash = "sha256:b2ef1c30440dbbcba7a5dc3e319408b59676e2e039e2ae11a8775ecf482b192f"}, - {file = "argon2_cffi_bindings-21.2.0-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:e415e3f62c8d124ee16018e491a009937f8cf7ebf5eb430ffc5de21b900dad93"}, - {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3e385d1c39c520c08b53d63300c3ecc28622f076f4c2b0e6d7e796e9f6502194"}, - {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c3e3cc67fdb7d82c4718f19b4e7a87123caf8a93fde7e23cf66ac0337d3cb3f"}, - {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6a22ad9800121b71099d0fb0a65323810a15f2e292f2ba450810a7316e128ee5"}, - {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f9f8b450ed0547e3d473fdc8612083fd08dd2120d6ac8f73828df9b7d45bb351"}, - {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:93f9bf70084f97245ba10ee36575f0c3f1e7d7724d67d8e5b08e61787c320ed7"}, - {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3b9ef65804859d335dc6b31582cad2c5166f0c3e7975f324d9ffaa34ee7e6583"}, - {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4966ef5848d820776f5f562a7d45fdd70c2f330c961d0d745b784034bd9f48d"}, - {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20ef543a89dee4db46a1a6e206cd015360e5a75822f76df533845c3cbaf72670"}, - {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed2937d286e2ad0cc79a7087d3c272832865f779430e0cc2b4f3718d3159b0cb"}, - {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:5e00316dabdaea0b2dd82d141cc66889ced0cdcbfa599e8b471cf22c620c329a"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:3d3f05610594151994ca9ccb3c771115bdb4daef161976a266f0dd8aa9996b8f"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:8b8efee945193e667a396cbc7b4fb7d357297d6234d30a489905d96caabde56b"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:3c6702abc36bf3ccba3f802b799505def420a1b7039862014a65db3205967f5a"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a1c70058c6ab1e352304ac7e3b52554daadacd8d453c1752e547c76e9c99ac44"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e2fd3bfbff3c5d74fef31a722f729bf93500910db650c925c2d6ef879a7e51cb"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c4f9665de60b1b0e99bcd6be4f17d90339698ce954cfd8d9cf4f91c995165a92"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ba92837e4a9aa6a508c8d2d7883ed5a8f6c308c89a4790e1e447a220deb79a85"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-win32.whl", hash = "sha256:84a461d4d84ae1295871329b346a97f68eade8c53b6ed9a7ca2d7467f3c8ff6f"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b55aec3565b65f56455eebc9b9f34130440404f27fe21c3b375bf1ea4d8fbae6"}, + {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:87c33a52407e4c41f3b70a9c2d3f6056d88b10dad7695be708c5021673f55623"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:aecba1723ae35330a008418a91ea6cfcedf6d31e5fbaa056a166462ff066d500"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:2630b6240b495dfab90aebe159ff784d08ea999aa4b0d17efa734055a07d2f44"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:7aef0c91e2c0fbca6fc68e7555aa60ef7008a739cbe045541e438373bc54d2b0"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e021e87faa76ae0d413b619fe2b65ab9a037f24c60a1e6cc43457ae20de6dc6"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d3e924cfc503018a714f94a49a149fdc0b644eaead5d1f089330399134fa028a"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:c87b72589133f0346a1cb8d5ecca4b933e3c9b64656c9d175270a000e73b288d"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1db89609c06afa1a214a69a462ea741cf735b29a57530478c06eb81dd403de99"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-win32.whl", hash = "sha256:473bcb5f82924b1becbb637b63303ec8d10e84c8d241119419897a26116515d2"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-win_amd64.whl", hash = "sha256:a98cd7d17e9f7ce244c0803cad3c23a7d379c301ba618a5fa76a67d116618b98"}, + {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-win_arm64.whl", hash = "sha256:b0fdbcf513833809c882823f98dc2f931cf659d9a1429616ac3adebb49f5db94"}, + {file = "argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:6dca33a9859abf613e22733131fc9194091c1fa7cb3e131c143056b4856aa47e"}, + {file = "argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:21378b40e1b8d1655dd5310c84a40fc19a9aa5e6366e835ceb8576bf0fea716d"}, + {file = "argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d588dec224e2a83edbdc785a5e6f3c6cd736f46bfd4b441bbb5aa1f5085e584"}, + {file = "argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5acb4e41090d53f17ca1110c3427f0a130f944b896fc8c83973219c97f57b690"}, + {file = "argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:da0c79c23a63723aa5d782250fbf51b768abca630285262fb5144ba5ae01e520"}, + {file = "argon2_cffi_bindings-25.1.0.tar.gz", hash = "sha256:b957f3e6ea4d55d820e40ff76f450952807013d361a65d7f28acc0acbf29229d"}, ] [package.dependencies] -cffi = ">=1.0.1" - -[package.extras] -dev = ["cogapp", "pre-commit", "pytest", "wheel"] -tests = ["pytest"] - -[[package]] -name = "asgiref" -version = "3.8.1" -description = "ASGI specs, helper code, and adapters" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "asgiref-3.8.1-py3-none-any.whl", hash = "sha256:3e1e3ecc849832fe52ccf2cb6686b7a55f82bb1d6aee72a58826471390335e47"}, - {file = "asgiref-3.8.1.tar.gz", hash = "sha256:c343bd80a0bec947a9860adb4c432ffa7db769836c64238fc34bdc3fec84d590"}, +cffi = [ + {version = ">=1.0.1", markers = "python_version < \"3.14\""}, + {version = ">=2.0.0b1", markers = "python_version >= \"3.14\""}, ] -[package.extras] -tests = ["mypy (>=0.800)", "pytest", "pytest-asyncio"] - [[package]] name = "asn1crypto" version = "1.5.1" @@ -308,22 +330,34 @@ files = [ [[package]] name = "asyauth" -version = "0.0.21" +version = "0.0.22" description = "Unified authentication library" optional = false python-versions = ">=3.7" groups = ["main"] files = [ - {file = "asyauth-0.0.21-py3-none-any.whl", hash = "sha256:1098ced8f4dfda74db535bc961e7667714154a440761821e26c8b637c95a2775"}, - {file = "asyauth-0.0.21.tar.gz", hash = "sha256:34cc10c5f8628ff2e25b5116dc98efc5ca45532f163ccd3f9147a3e02dd810eb"}, + {file = "asyauth-0.0.22-py3-none-any.whl", hash = "sha256:34f315fec2ba1b9304c94bae2eed6580cee654c68e767b4ad18a35e311444ec4"}, + {file = "asyauth-0.0.22.tar.gz", hash = "sha256:faa6834032d3ce44bf660602bfc82973c0de46806a768762b3eb931281759f0b"}, ] [package.dependencies] asn1crypto = ">=1.3.0" -asysocks = ">=0.2.11" -minikerberos = ">=0.4.4" +asysocks = ">=0.2.17" +minikerberos = ">=0.4.7" unicrypto = ">=0.0.10" +[[package]] +name = "asyncio" +version = "4.0.0" +description = "Deprecated backport of asyncio; use the stdlib package instead" +optional = false +python-versions = ">=3.4" +groups = ["main"] +files = [ + {file = "asyncio-4.0.0-py3-none-any.whl", hash = "sha256:c1eddb0659231837046809e68103969b2bef8b0400d59cfa6363f6b5ed8cc88b"}, + {file = "asyncio-4.0.0.tar.gz", hash = "sha256:570cd9e50db83bc1629152d4d0b7558d6451bb1bfd5dfc2e935d96fc2f40329b"}, +] + [[package]] name = "asyncpg" version = "0.30.0" @@ -390,14 +424,14 @@ test = ["distro (>=1.9.0,<1.10.0)", "flake8 (>=6.1,<7.0)", "flake8-pyi (>=24.1.0 [[package]] name = "asysocks" -version = "0.2.13" +version = "0.2.17" description = "" optional = false python-versions = ">=3.6" groups = ["main"] files = [ - {file = "asysocks-0.2.13-py3-none-any.whl", hash = "sha256:e32f478eac58566162d3e5af02ed6b6625317d9ddf83af22109bd13a24ef721a"}, - {file = "asysocks-0.2.13.tar.gz", hash = "sha256:44185b2c471e63b7293173967eef3b0f5e60ed5cc1b7650a30a9569e49ff25f8"}, + {file = "asysocks-0.2.17-py3-none-any.whl", hash = "sha256:649a994d1d5c819700afac22d034e43fdb9907724375aeaf0ab0b46f18fda272"}, + {file = "asysocks-0.2.17.tar.gz", hash = "sha256:afa17b49e97c0f79e805013c7c3610e64f4dcc814e994593fb5086ad6542593c"}, ] [package.dependencies] @@ -407,24 +441,16 @@ h11 = ">=0.14.0" [[package]] name = "attrs" -version = "25.3.0" +version = "25.4.0" description = "Classes Without Boilerplate" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["main"] files = [ - {file = "attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3"}, - {file = "attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b"}, + {file = "attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373"}, + {file = "attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11"}, ] -[package.extras] -benchmark = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -cov = ["cloudpickle", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier"] -tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] -tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"] - [[package]] name = "binaryornot" version = "0.4.4" @@ -452,491 +478,6 @@ files = [ {file = "blinker-1.9.0.tar.gz", hash = "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf"}, ] -[[package]] -name = "boto3" -version = "1.38.36" -description = "The AWS SDK for Python" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "boto3-1.38.36-py3-none-any.whl", hash = "sha256:34c27d7317cadb62c0e9856e5d5aa0271ef47202d340584831048bc7ac904136"}, - {file = "boto3-1.38.36.tar.gz", hash = "sha256:efe0aaa060f8fedd76e5c942055f051aee0432fc722d79d8830a9fd9db83593e"}, -] - -[package.dependencies] -botocore = ">=1.38.36,<1.39.0" -jmespath = ">=0.7.1,<2.0.0" -s3transfer = ">=0.13.0,<0.14.0" - -[package.extras] -crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] - -[[package]] -name = "boto3-stubs" -version = "1.38.36" -description = "Type annotations for boto3 1.38.36 generated with mypy-boto3-builder 8.11.0" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "boto3_stubs-1.38.36-py3-none-any.whl", hash = "sha256:1f79b85f93772df94854e9e570a917f1d762f4080a88ed16b9f2e4b98b24f1f1"}, - {file = "boto3_stubs-1.38.36.tar.gz", hash = "sha256:8de916b9433e9224f3bd4a79ed41e508dc532bfee7db34bfd3752901bcd7e69f"}, -] - -[package.dependencies] -botocore-stubs = "*" -mypy-boto3-s3 = {version = ">=1.38.0,<1.39.0", optional = true, markers = "extra == \"s3\""} -types-s3transfer = "*" - -[package.extras] -accessanalyzer = ["mypy-boto3-accessanalyzer (>=1.38.0,<1.39.0)"] -account = ["mypy-boto3-account (>=1.38.0,<1.39.0)"] -acm = ["mypy-boto3-acm (>=1.38.0,<1.39.0)"] -acm-pca = ["mypy-boto3-acm-pca (>=1.38.0,<1.39.0)"] -all = ["mypy-boto3-accessanalyzer (>=1.38.0,<1.39.0)", "mypy-boto3-account (>=1.38.0,<1.39.0)", "mypy-boto3-acm (>=1.38.0,<1.39.0)", "mypy-boto3-acm-pca (>=1.38.0,<1.39.0)", "mypy-boto3-amp (>=1.38.0,<1.39.0)", "mypy-boto3-amplify (>=1.38.0,<1.39.0)", "mypy-boto3-amplifybackend (>=1.38.0,<1.39.0)", "mypy-boto3-amplifyuibuilder (>=1.38.0,<1.39.0)", "mypy-boto3-apigateway (>=1.38.0,<1.39.0)", "mypy-boto3-apigatewaymanagementapi (>=1.38.0,<1.39.0)", "mypy-boto3-apigatewayv2 (>=1.38.0,<1.39.0)", "mypy-boto3-appconfig (>=1.38.0,<1.39.0)", "mypy-boto3-appconfigdata (>=1.38.0,<1.39.0)", "mypy-boto3-appfabric (>=1.38.0,<1.39.0)", "mypy-boto3-appflow (>=1.38.0,<1.39.0)", "mypy-boto3-appintegrations (>=1.38.0,<1.39.0)", "mypy-boto3-application-autoscaling (>=1.38.0,<1.39.0)", "mypy-boto3-application-insights (>=1.38.0,<1.39.0)", "mypy-boto3-application-signals (>=1.38.0,<1.39.0)", "mypy-boto3-applicationcostprofiler (>=1.38.0,<1.39.0)", "mypy-boto3-appmesh (>=1.38.0,<1.39.0)", "mypy-boto3-apprunner (>=1.38.0,<1.39.0)", "mypy-boto3-appstream (>=1.38.0,<1.39.0)", "mypy-boto3-appsync (>=1.38.0,<1.39.0)", "mypy-boto3-apptest (>=1.38.0,<1.39.0)", "mypy-boto3-arc-zonal-shift (>=1.38.0,<1.39.0)", "mypy-boto3-artifact (>=1.38.0,<1.39.0)", "mypy-boto3-athena (>=1.38.0,<1.39.0)", "mypy-boto3-auditmanager (>=1.38.0,<1.39.0)", "mypy-boto3-autoscaling (>=1.38.0,<1.39.0)", "mypy-boto3-autoscaling-plans (>=1.38.0,<1.39.0)", "mypy-boto3-b2bi (>=1.38.0,<1.39.0)", "mypy-boto3-backup (>=1.38.0,<1.39.0)", "mypy-boto3-backup-gateway (>=1.38.0,<1.39.0)", "mypy-boto3-backupsearch (>=1.38.0,<1.39.0)", "mypy-boto3-batch (>=1.38.0,<1.39.0)", "mypy-boto3-bcm-data-exports (>=1.38.0,<1.39.0)", "mypy-boto3-bcm-pricing-calculator (>=1.38.0,<1.39.0)", "mypy-boto3-bedrock (>=1.38.0,<1.39.0)", "mypy-boto3-bedrock-agent (>=1.38.0,<1.39.0)", "mypy-boto3-bedrock-agent-runtime (>=1.38.0,<1.39.0)", "mypy-boto3-bedrock-data-automation (>=1.38.0,<1.39.0)", "mypy-boto3-bedrock-data-automation-runtime (>=1.38.0,<1.39.0)", "mypy-boto3-bedrock-runtime (>=1.38.0,<1.39.0)", "mypy-boto3-billing (>=1.38.0,<1.39.0)", "mypy-boto3-billingconductor (>=1.38.0,<1.39.0)", "mypy-boto3-braket (>=1.38.0,<1.39.0)", "mypy-boto3-budgets (>=1.38.0,<1.39.0)", "mypy-boto3-ce (>=1.38.0,<1.39.0)", "mypy-boto3-chatbot (>=1.38.0,<1.39.0)", "mypy-boto3-chime (>=1.38.0,<1.39.0)", "mypy-boto3-chime-sdk-identity (>=1.38.0,<1.39.0)", "mypy-boto3-chime-sdk-media-pipelines (>=1.38.0,<1.39.0)", "mypy-boto3-chime-sdk-meetings (>=1.38.0,<1.39.0)", "mypy-boto3-chime-sdk-messaging (>=1.38.0,<1.39.0)", "mypy-boto3-chime-sdk-voice (>=1.38.0,<1.39.0)", "mypy-boto3-cleanrooms (>=1.38.0,<1.39.0)", "mypy-boto3-cleanroomsml (>=1.38.0,<1.39.0)", "mypy-boto3-cloud9 (>=1.38.0,<1.39.0)", "mypy-boto3-cloudcontrol (>=1.38.0,<1.39.0)", "mypy-boto3-clouddirectory (>=1.38.0,<1.39.0)", "mypy-boto3-cloudformation (>=1.38.0,<1.39.0)", "mypy-boto3-cloudfront (>=1.38.0,<1.39.0)", "mypy-boto3-cloudfront-keyvaluestore (>=1.38.0,<1.39.0)", "mypy-boto3-cloudhsm (>=1.38.0,<1.39.0)", "mypy-boto3-cloudhsmv2 (>=1.38.0,<1.39.0)", "mypy-boto3-cloudsearch (>=1.38.0,<1.39.0)", "mypy-boto3-cloudsearchdomain (>=1.38.0,<1.39.0)", "mypy-boto3-cloudtrail (>=1.38.0,<1.39.0)", "mypy-boto3-cloudtrail-data (>=1.38.0,<1.39.0)", "mypy-boto3-cloudwatch (>=1.38.0,<1.39.0)", "mypy-boto3-codeartifact (>=1.38.0,<1.39.0)", "mypy-boto3-codebuild (>=1.38.0,<1.39.0)", "mypy-boto3-codecatalyst (>=1.38.0,<1.39.0)", "mypy-boto3-codecommit (>=1.38.0,<1.39.0)", "mypy-boto3-codeconnections (>=1.38.0,<1.39.0)", "mypy-boto3-codedeploy (>=1.38.0,<1.39.0)", "mypy-boto3-codeguru-reviewer (>=1.38.0,<1.39.0)", "mypy-boto3-codeguru-security (>=1.38.0,<1.39.0)", "mypy-boto3-codeguruprofiler (>=1.38.0,<1.39.0)", "mypy-boto3-codepipeline (>=1.38.0,<1.39.0)", "mypy-boto3-codestar-connections (>=1.38.0,<1.39.0)", "mypy-boto3-codestar-notifications (>=1.38.0,<1.39.0)", "mypy-boto3-cognito-identity (>=1.38.0,<1.39.0)", "mypy-boto3-cognito-idp (>=1.38.0,<1.39.0)", "mypy-boto3-cognito-sync (>=1.38.0,<1.39.0)", "mypy-boto3-comprehend (>=1.38.0,<1.39.0)", "mypy-boto3-comprehendmedical (>=1.38.0,<1.39.0)", "mypy-boto3-compute-optimizer (>=1.38.0,<1.39.0)", "mypy-boto3-config (>=1.38.0,<1.39.0)", "mypy-boto3-connect (>=1.38.0,<1.39.0)", "mypy-boto3-connect-contact-lens (>=1.38.0,<1.39.0)", "mypy-boto3-connectcampaigns (>=1.38.0,<1.39.0)", "mypy-boto3-connectcampaignsv2 (>=1.38.0,<1.39.0)", "mypy-boto3-connectcases (>=1.38.0,<1.39.0)", "mypy-boto3-connectparticipant (>=1.38.0,<1.39.0)", "mypy-boto3-controlcatalog (>=1.38.0,<1.39.0)", "mypy-boto3-controltower (>=1.38.0,<1.39.0)", "mypy-boto3-cost-optimization-hub (>=1.38.0,<1.39.0)", "mypy-boto3-cur (>=1.38.0,<1.39.0)", "mypy-boto3-customer-profiles (>=1.38.0,<1.39.0)", "mypy-boto3-databrew (>=1.38.0,<1.39.0)", "mypy-boto3-dataexchange (>=1.38.0,<1.39.0)", "mypy-boto3-datapipeline (>=1.38.0,<1.39.0)", "mypy-boto3-datasync (>=1.38.0,<1.39.0)", "mypy-boto3-datazone (>=1.38.0,<1.39.0)", "mypy-boto3-dax (>=1.38.0,<1.39.0)", "mypy-boto3-deadline (>=1.38.0,<1.39.0)", "mypy-boto3-detective (>=1.38.0,<1.39.0)", "mypy-boto3-devicefarm (>=1.38.0,<1.39.0)", "mypy-boto3-devops-guru (>=1.38.0,<1.39.0)", "mypy-boto3-directconnect (>=1.38.0,<1.39.0)", "mypy-boto3-discovery (>=1.38.0,<1.39.0)", "mypy-boto3-dlm (>=1.38.0,<1.39.0)", "mypy-boto3-dms (>=1.38.0,<1.39.0)", "mypy-boto3-docdb (>=1.38.0,<1.39.0)", "mypy-boto3-docdb-elastic (>=1.38.0,<1.39.0)", "mypy-boto3-drs (>=1.38.0,<1.39.0)", "mypy-boto3-ds (>=1.38.0,<1.39.0)", "mypy-boto3-ds-data (>=1.38.0,<1.39.0)", "mypy-boto3-dsql (>=1.38.0,<1.39.0)", "mypy-boto3-dynamodb (>=1.38.0,<1.39.0)", "mypy-boto3-dynamodbstreams (>=1.38.0,<1.39.0)", "mypy-boto3-ebs (>=1.38.0,<1.39.0)", "mypy-boto3-ec2 (>=1.38.0,<1.39.0)", "mypy-boto3-ec2-instance-connect (>=1.38.0,<1.39.0)", "mypy-boto3-ecr (>=1.38.0,<1.39.0)", "mypy-boto3-ecr-public (>=1.38.0,<1.39.0)", "mypy-boto3-ecs (>=1.38.0,<1.39.0)", "mypy-boto3-efs (>=1.38.0,<1.39.0)", "mypy-boto3-eks (>=1.38.0,<1.39.0)", "mypy-boto3-eks-auth (>=1.38.0,<1.39.0)", "mypy-boto3-elasticache (>=1.38.0,<1.39.0)", "mypy-boto3-elasticbeanstalk (>=1.38.0,<1.39.0)", "mypy-boto3-elastictranscoder (>=1.38.0,<1.39.0)", "mypy-boto3-elb (>=1.38.0,<1.39.0)", "mypy-boto3-elbv2 (>=1.38.0,<1.39.0)", "mypy-boto3-emr (>=1.38.0,<1.39.0)", "mypy-boto3-emr-containers (>=1.38.0,<1.39.0)", "mypy-boto3-emr-serverless (>=1.38.0,<1.39.0)", "mypy-boto3-entityresolution (>=1.38.0,<1.39.0)", "mypy-boto3-es (>=1.38.0,<1.39.0)", "mypy-boto3-events (>=1.38.0,<1.39.0)", "mypy-boto3-evidently (>=1.38.0,<1.39.0)", "mypy-boto3-evs (>=1.38.0,<1.39.0)", "mypy-boto3-finspace (>=1.38.0,<1.39.0)", "mypy-boto3-finspace-data (>=1.38.0,<1.39.0)", "mypy-boto3-firehose (>=1.38.0,<1.39.0)", "mypy-boto3-fis (>=1.38.0,<1.39.0)", "mypy-boto3-fms (>=1.38.0,<1.39.0)", "mypy-boto3-forecast (>=1.38.0,<1.39.0)", "mypy-boto3-forecastquery (>=1.38.0,<1.39.0)", "mypy-boto3-frauddetector (>=1.38.0,<1.39.0)", "mypy-boto3-freetier (>=1.38.0,<1.39.0)", "mypy-boto3-fsx (>=1.38.0,<1.39.0)", "mypy-boto3-gamelift (>=1.38.0,<1.39.0)", "mypy-boto3-gameliftstreams (>=1.38.0,<1.39.0)", "mypy-boto3-geo-maps (>=1.38.0,<1.39.0)", "mypy-boto3-geo-places (>=1.38.0,<1.39.0)", "mypy-boto3-geo-routes (>=1.38.0,<1.39.0)", "mypy-boto3-glacier (>=1.38.0,<1.39.0)", "mypy-boto3-globalaccelerator (>=1.38.0,<1.39.0)", "mypy-boto3-glue (>=1.38.0,<1.39.0)", "mypy-boto3-grafana (>=1.38.0,<1.39.0)", "mypy-boto3-greengrass (>=1.38.0,<1.39.0)", "mypy-boto3-greengrassv2 (>=1.38.0,<1.39.0)", "mypy-boto3-groundstation (>=1.38.0,<1.39.0)", "mypy-boto3-guardduty (>=1.38.0,<1.39.0)", "mypy-boto3-health (>=1.38.0,<1.39.0)", "mypy-boto3-healthlake (>=1.38.0,<1.39.0)", "mypy-boto3-iam (>=1.38.0,<1.39.0)", "mypy-boto3-identitystore (>=1.38.0,<1.39.0)", "mypy-boto3-imagebuilder (>=1.38.0,<1.39.0)", "mypy-boto3-importexport (>=1.38.0,<1.39.0)", "mypy-boto3-inspector (>=1.38.0,<1.39.0)", "mypy-boto3-inspector-scan (>=1.38.0,<1.39.0)", "mypy-boto3-inspector2 (>=1.38.0,<1.39.0)", "mypy-boto3-internetmonitor (>=1.38.0,<1.39.0)", "mypy-boto3-invoicing (>=1.38.0,<1.39.0)", "mypy-boto3-iot (>=1.38.0,<1.39.0)", "mypy-boto3-iot-data (>=1.38.0,<1.39.0)", "mypy-boto3-iot-jobs-data (>=1.38.0,<1.39.0)", "mypy-boto3-iot-managed-integrations (>=1.38.0,<1.39.0)", "mypy-boto3-iotanalytics (>=1.38.0,<1.39.0)", "mypy-boto3-iotdeviceadvisor (>=1.38.0,<1.39.0)", "mypy-boto3-iotevents (>=1.38.0,<1.39.0)", "mypy-boto3-iotevents-data (>=1.38.0,<1.39.0)", "mypy-boto3-iotfleethub (>=1.38.0,<1.39.0)", "mypy-boto3-iotfleetwise (>=1.38.0,<1.39.0)", "mypy-boto3-iotsecuretunneling (>=1.38.0,<1.39.0)", "mypy-boto3-iotsitewise (>=1.38.0,<1.39.0)", "mypy-boto3-iotthingsgraph (>=1.38.0,<1.39.0)", "mypy-boto3-iottwinmaker (>=1.38.0,<1.39.0)", "mypy-boto3-iotwireless (>=1.38.0,<1.39.0)", "mypy-boto3-ivs (>=1.38.0,<1.39.0)", "mypy-boto3-ivs-realtime (>=1.38.0,<1.39.0)", "mypy-boto3-ivschat (>=1.38.0,<1.39.0)", "mypy-boto3-kafka (>=1.38.0,<1.39.0)", "mypy-boto3-kafkaconnect (>=1.38.0,<1.39.0)", "mypy-boto3-kendra (>=1.38.0,<1.39.0)", "mypy-boto3-kendra-ranking (>=1.38.0,<1.39.0)", "mypy-boto3-keyspaces (>=1.38.0,<1.39.0)", "mypy-boto3-kinesis (>=1.38.0,<1.39.0)", "mypy-boto3-kinesis-video-archived-media (>=1.38.0,<1.39.0)", "mypy-boto3-kinesis-video-media (>=1.38.0,<1.39.0)", "mypy-boto3-kinesis-video-signaling (>=1.38.0,<1.39.0)", "mypy-boto3-kinesis-video-webrtc-storage (>=1.38.0,<1.39.0)", "mypy-boto3-kinesisanalytics (>=1.38.0,<1.39.0)", "mypy-boto3-kinesisanalyticsv2 (>=1.38.0,<1.39.0)", "mypy-boto3-kinesisvideo (>=1.38.0,<1.39.0)", "mypy-boto3-kms (>=1.38.0,<1.39.0)", "mypy-boto3-lakeformation (>=1.38.0,<1.39.0)", "mypy-boto3-lambda (>=1.38.0,<1.39.0)", "mypy-boto3-launch-wizard (>=1.38.0,<1.39.0)", "mypy-boto3-lex-models (>=1.38.0,<1.39.0)", "mypy-boto3-lex-runtime (>=1.38.0,<1.39.0)", "mypy-boto3-lexv2-models (>=1.38.0,<1.39.0)", "mypy-boto3-lexv2-runtime (>=1.38.0,<1.39.0)", "mypy-boto3-license-manager (>=1.38.0,<1.39.0)", "mypy-boto3-license-manager-linux-subscriptions (>=1.38.0,<1.39.0)", "mypy-boto3-license-manager-user-subscriptions (>=1.38.0,<1.39.0)", "mypy-boto3-lightsail (>=1.38.0,<1.39.0)", "mypy-boto3-location (>=1.38.0,<1.39.0)", "mypy-boto3-logs (>=1.38.0,<1.39.0)", "mypy-boto3-lookoutequipment (>=1.38.0,<1.39.0)", "mypy-boto3-lookoutmetrics (>=1.38.0,<1.39.0)", "mypy-boto3-lookoutvision (>=1.38.0,<1.39.0)", "mypy-boto3-m2 (>=1.38.0,<1.39.0)", "mypy-boto3-machinelearning (>=1.38.0,<1.39.0)", "mypy-boto3-macie2 (>=1.38.0,<1.39.0)", "mypy-boto3-mailmanager (>=1.38.0,<1.39.0)", "mypy-boto3-managedblockchain (>=1.38.0,<1.39.0)", "mypy-boto3-managedblockchain-query (>=1.38.0,<1.39.0)", "mypy-boto3-marketplace-agreement (>=1.38.0,<1.39.0)", "mypy-boto3-marketplace-catalog (>=1.38.0,<1.39.0)", "mypy-boto3-marketplace-deployment (>=1.38.0,<1.39.0)", "mypy-boto3-marketplace-entitlement (>=1.38.0,<1.39.0)", "mypy-boto3-marketplace-reporting (>=1.38.0,<1.39.0)", "mypy-boto3-marketplacecommerceanalytics (>=1.38.0,<1.39.0)", "mypy-boto3-mediaconnect (>=1.38.0,<1.39.0)", "mypy-boto3-mediaconvert (>=1.38.0,<1.39.0)", "mypy-boto3-medialive (>=1.38.0,<1.39.0)", "mypy-boto3-mediapackage (>=1.38.0,<1.39.0)", "mypy-boto3-mediapackage-vod (>=1.38.0,<1.39.0)", "mypy-boto3-mediapackagev2 (>=1.38.0,<1.39.0)", "mypy-boto3-mediastore (>=1.38.0,<1.39.0)", "mypy-boto3-mediastore-data (>=1.38.0,<1.39.0)", "mypy-boto3-mediatailor (>=1.38.0,<1.39.0)", "mypy-boto3-medical-imaging (>=1.38.0,<1.39.0)", "mypy-boto3-memorydb (>=1.38.0,<1.39.0)", "mypy-boto3-meteringmarketplace (>=1.38.0,<1.39.0)", "mypy-boto3-mgh (>=1.38.0,<1.39.0)", "mypy-boto3-mgn (>=1.38.0,<1.39.0)", "mypy-boto3-migration-hub-refactor-spaces (>=1.38.0,<1.39.0)", "mypy-boto3-migrationhub-config (>=1.38.0,<1.39.0)", "mypy-boto3-migrationhuborchestrator (>=1.38.0,<1.39.0)", "mypy-boto3-migrationhubstrategy (>=1.38.0,<1.39.0)", "mypy-boto3-mq (>=1.38.0,<1.39.0)", "mypy-boto3-mturk (>=1.38.0,<1.39.0)", "mypy-boto3-mwaa (>=1.38.0,<1.39.0)", "mypy-boto3-neptune (>=1.38.0,<1.39.0)", "mypy-boto3-neptune-graph (>=1.38.0,<1.39.0)", "mypy-boto3-neptunedata (>=1.38.0,<1.39.0)", "mypy-boto3-network-firewall (>=1.38.0,<1.39.0)", "mypy-boto3-networkflowmonitor (>=1.38.0,<1.39.0)", "mypy-boto3-networkmanager (>=1.38.0,<1.39.0)", "mypy-boto3-networkmonitor (>=1.38.0,<1.39.0)", "mypy-boto3-notifications (>=1.38.0,<1.39.0)", "mypy-boto3-notificationscontacts (>=1.38.0,<1.39.0)", "mypy-boto3-oam (>=1.38.0,<1.39.0)", "mypy-boto3-observabilityadmin (>=1.38.0,<1.39.0)", "mypy-boto3-omics (>=1.38.0,<1.39.0)", "mypy-boto3-opensearch (>=1.38.0,<1.39.0)", "mypy-boto3-opensearchserverless (>=1.38.0,<1.39.0)", "mypy-boto3-opsworks (>=1.38.0,<1.39.0)", "mypy-boto3-opsworkscm (>=1.38.0,<1.39.0)", "mypy-boto3-organizations (>=1.38.0,<1.39.0)", "mypy-boto3-osis (>=1.38.0,<1.39.0)", "mypy-boto3-outposts (>=1.38.0,<1.39.0)", "mypy-boto3-panorama (>=1.38.0,<1.39.0)", "mypy-boto3-partnercentral-selling (>=1.38.0,<1.39.0)", "mypy-boto3-payment-cryptography (>=1.38.0,<1.39.0)", "mypy-boto3-payment-cryptography-data (>=1.38.0,<1.39.0)", "mypy-boto3-pca-connector-ad (>=1.38.0,<1.39.0)", "mypy-boto3-pca-connector-scep (>=1.38.0,<1.39.0)", "mypy-boto3-pcs (>=1.38.0,<1.39.0)", "mypy-boto3-personalize (>=1.38.0,<1.39.0)", "mypy-boto3-personalize-events (>=1.38.0,<1.39.0)", "mypy-boto3-personalize-runtime (>=1.38.0,<1.39.0)", "mypy-boto3-pi (>=1.38.0,<1.39.0)", "mypy-boto3-pinpoint (>=1.38.0,<1.39.0)", "mypy-boto3-pinpoint-email (>=1.38.0,<1.39.0)", "mypy-boto3-pinpoint-sms-voice (>=1.38.0,<1.39.0)", "mypy-boto3-pinpoint-sms-voice-v2 (>=1.38.0,<1.39.0)", "mypy-boto3-pipes (>=1.38.0,<1.39.0)", "mypy-boto3-polly (>=1.38.0,<1.39.0)", "mypy-boto3-pricing (>=1.38.0,<1.39.0)", "mypy-boto3-proton (>=1.38.0,<1.39.0)", "mypy-boto3-qapps (>=1.38.0,<1.39.0)", "mypy-boto3-qbusiness (>=1.38.0,<1.39.0)", "mypy-boto3-qconnect (>=1.38.0,<1.39.0)", "mypy-boto3-qldb (>=1.38.0,<1.39.0)", "mypy-boto3-qldb-session (>=1.38.0,<1.39.0)", "mypy-boto3-quicksight (>=1.38.0,<1.39.0)", "mypy-boto3-ram (>=1.38.0,<1.39.0)", "mypy-boto3-rbin (>=1.38.0,<1.39.0)", "mypy-boto3-rds (>=1.38.0,<1.39.0)", "mypy-boto3-rds-data (>=1.38.0,<1.39.0)", "mypy-boto3-redshift (>=1.38.0,<1.39.0)", "mypy-boto3-redshift-data (>=1.38.0,<1.39.0)", "mypy-boto3-redshift-serverless (>=1.38.0,<1.39.0)", "mypy-boto3-rekognition (>=1.38.0,<1.39.0)", "mypy-boto3-repostspace (>=1.38.0,<1.39.0)", "mypy-boto3-resiliencehub (>=1.38.0,<1.39.0)", "mypy-boto3-resource-explorer-2 (>=1.38.0,<1.39.0)", "mypy-boto3-resource-groups (>=1.38.0,<1.39.0)", "mypy-boto3-resourcegroupstaggingapi (>=1.38.0,<1.39.0)", "mypy-boto3-robomaker (>=1.38.0,<1.39.0)", "mypy-boto3-rolesanywhere (>=1.38.0,<1.39.0)", "mypy-boto3-route53 (>=1.38.0,<1.39.0)", "mypy-boto3-route53-recovery-cluster (>=1.38.0,<1.39.0)", "mypy-boto3-route53-recovery-control-config (>=1.38.0,<1.39.0)", "mypy-boto3-route53-recovery-readiness (>=1.38.0,<1.39.0)", "mypy-boto3-route53domains (>=1.38.0,<1.39.0)", "mypy-boto3-route53profiles (>=1.38.0,<1.39.0)", "mypy-boto3-route53resolver (>=1.38.0,<1.39.0)", "mypy-boto3-rum (>=1.38.0,<1.39.0)", "mypy-boto3-s3 (>=1.38.0,<1.39.0)", "mypy-boto3-s3control (>=1.38.0,<1.39.0)", "mypy-boto3-s3outposts (>=1.38.0,<1.39.0)", "mypy-boto3-s3tables (>=1.38.0,<1.39.0)", "mypy-boto3-sagemaker (>=1.38.0,<1.39.0)", "mypy-boto3-sagemaker-a2i-runtime (>=1.38.0,<1.39.0)", "mypy-boto3-sagemaker-edge (>=1.38.0,<1.39.0)", "mypy-boto3-sagemaker-featurestore-runtime (>=1.38.0,<1.39.0)", "mypy-boto3-sagemaker-geospatial (>=1.38.0,<1.39.0)", "mypy-boto3-sagemaker-metrics (>=1.38.0,<1.39.0)", "mypy-boto3-sagemaker-runtime (>=1.38.0,<1.39.0)", "mypy-boto3-savingsplans (>=1.38.0,<1.39.0)", "mypy-boto3-scheduler (>=1.38.0,<1.39.0)", "mypy-boto3-schemas (>=1.38.0,<1.39.0)", "mypy-boto3-sdb (>=1.38.0,<1.39.0)", "mypy-boto3-secretsmanager (>=1.38.0,<1.39.0)", "mypy-boto3-security-ir (>=1.38.0,<1.39.0)", "mypy-boto3-securityhub (>=1.38.0,<1.39.0)", "mypy-boto3-securitylake (>=1.38.0,<1.39.0)", "mypy-boto3-serverlessrepo (>=1.38.0,<1.39.0)", "mypy-boto3-service-quotas (>=1.38.0,<1.39.0)", "mypy-boto3-servicecatalog (>=1.38.0,<1.39.0)", "mypy-boto3-servicecatalog-appregistry (>=1.38.0,<1.39.0)", "mypy-boto3-servicediscovery (>=1.38.0,<1.39.0)", "mypy-boto3-ses (>=1.38.0,<1.39.0)", "mypy-boto3-sesv2 (>=1.38.0,<1.39.0)", "mypy-boto3-shield (>=1.38.0,<1.39.0)", "mypy-boto3-signer (>=1.38.0,<1.39.0)", "mypy-boto3-simspaceweaver (>=1.38.0,<1.39.0)", "mypy-boto3-sms (>=1.38.0,<1.39.0)", "mypy-boto3-snow-device-management (>=1.38.0,<1.39.0)", "mypy-boto3-snowball (>=1.38.0,<1.39.0)", "mypy-boto3-sns (>=1.38.0,<1.39.0)", "mypy-boto3-socialmessaging (>=1.38.0,<1.39.0)", "mypy-boto3-sqs (>=1.38.0,<1.39.0)", "mypy-boto3-ssm (>=1.38.0,<1.39.0)", "mypy-boto3-ssm-contacts (>=1.38.0,<1.39.0)", "mypy-boto3-ssm-guiconnect (>=1.38.0,<1.39.0)", "mypy-boto3-ssm-incidents (>=1.38.0,<1.39.0)", "mypy-boto3-ssm-quicksetup (>=1.38.0,<1.39.0)", "mypy-boto3-ssm-sap (>=1.38.0,<1.39.0)", "mypy-boto3-sso (>=1.38.0,<1.39.0)", "mypy-boto3-sso-admin (>=1.38.0,<1.39.0)", "mypy-boto3-sso-oidc (>=1.38.0,<1.39.0)", "mypy-boto3-stepfunctions (>=1.38.0,<1.39.0)", "mypy-boto3-storagegateway (>=1.38.0,<1.39.0)", "mypy-boto3-sts (>=1.38.0,<1.39.0)", "mypy-boto3-supplychain (>=1.38.0,<1.39.0)", "mypy-boto3-support (>=1.38.0,<1.39.0)", "mypy-boto3-support-app (>=1.38.0,<1.39.0)", "mypy-boto3-swf (>=1.38.0,<1.39.0)", "mypy-boto3-synthetics (>=1.38.0,<1.39.0)", "mypy-boto3-taxsettings (>=1.38.0,<1.39.0)", "mypy-boto3-textract (>=1.38.0,<1.39.0)", "mypy-boto3-timestream-influxdb (>=1.38.0,<1.39.0)", "mypy-boto3-timestream-query (>=1.38.0,<1.39.0)", "mypy-boto3-timestream-write (>=1.38.0,<1.39.0)", "mypy-boto3-tnb (>=1.38.0,<1.39.0)", "mypy-boto3-transcribe (>=1.38.0,<1.39.0)", "mypy-boto3-transfer (>=1.38.0,<1.39.0)", "mypy-boto3-translate (>=1.38.0,<1.39.0)", "mypy-boto3-trustedadvisor (>=1.38.0,<1.39.0)", "mypy-boto3-verifiedpermissions (>=1.38.0,<1.39.0)", "mypy-boto3-voice-id (>=1.38.0,<1.39.0)", "mypy-boto3-vpc-lattice (>=1.38.0,<1.39.0)", "mypy-boto3-waf (>=1.38.0,<1.39.0)", "mypy-boto3-waf-regional (>=1.38.0,<1.39.0)", "mypy-boto3-wafv2 (>=1.38.0,<1.39.0)", "mypy-boto3-wellarchitected (>=1.38.0,<1.39.0)", "mypy-boto3-wisdom (>=1.38.0,<1.39.0)", "mypy-boto3-workdocs (>=1.38.0,<1.39.0)", "mypy-boto3-workmail (>=1.38.0,<1.39.0)", "mypy-boto3-workmailmessageflow (>=1.38.0,<1.39.0)", "mypy-boto3-workspaces (>=1.38.0,<1.39.0)", "mypy-boto3-workspaces-thin-client (>=1.38.0,<1.39.0)", "mypy-boto3-workspaces-web (>=1.38.0,<1.39.0)", "mypy-boto3-xray (>=1.38.0,<1.39.0)"] -amp = ["mypy-boto3-amp (>=1.38.0,<1.39.0)"] -amplify = ["mypy-boto3-amplify (>=1.38.0,<1.39.0)"] -amplifybackend = ["mypy-boto3-amplifybackend (>=1.38.0,<1.39.0)"] -amplifyuibuilder = ["mypy-boto3-amplifyuibuilder (>=1.38.0,<1.39.0)"] -apigateway = ["mypy-boto3-apigateway (>=1.38.0,<1.39.0)"] -apigatewaymanagementapi = ["mypy-boto3-apigatewaymanagementapi (>=1.38.0,<1.39.0)"] -apigatewayv2 = ["mypy-boto3-apigatewayv2 (>=1.38.0,<1.39.0)"] -appconfig = ["mypy-boto3-appconfig (>=1.38.0,<1.39.0)"] -appconfigdata = ["mypy-boto3-appconfigdata (>=1.38.0,<1.39.0)"] -appfabric = ["mypy-boto3-appfabric (>=1.38.0,<1.39.0)"] -appflow = ["mypy-boto3-appflow (>=1.38.0,<1.39.0)"] -appintegrations = ["mypy-boto3-appintegrations (>=1.38.0,<1.39.0)"] -application-autoscaling = ["mypy-boto3-application-autoscaling (>=1.38.0,<1.39.0)"] -application-insights = ["mypy-boto3-application-insights (>=1.38.0,<1.39.0)"] -application-signals = ["mypy-boto3-application-signals (>=1.38.0,<1.39.0)"] -applicationcostprofiler = ["mypy-boto3-applicationcostprofiler (>=1.38.0,<1.39.0)"] -appmesh = ["mypy-boto3-appmesh (>=1.38.0,<1.39.0)"] -apprunner = ["mypy-boto3-apprunner (>=1.38.0,<1.39.0)"] -appstream = ["mypy-boto3-appstream (>=1.38.0,<1.39.0)"] -appsync = ["mypy-boto3-appsync (>=1.38.0,<1.39.0)"] -apptest = ["mypy-boto3-apptest (>=1.38.0,<1.39.0)"] -arc-zonal-shift = ["mypy-boto3-arc-zonal-shift (>=1.38.0,<1.39.0)"] -artifact = ["mypy-boto3-artifact (>=1.38.0,<1.39.0)"] -athena = ["mypy-boto3-athena (>=1.38.0,<1.39.0)"] -auditmanager = ["mypy-boto3-auditmanager (>=1.38.0,<1.39.0)"] -autoscaling = ["mypy-boto3-autoscaling (>=1.38.0,<1.39.0)"] -autoscaling-plans = ["mypy-boto3-autoscaling-plans (>=1.38.0,<1.39.0)"] -b2bi = ["mypy-boto3-b2bi (>=1.38.0,<1.39.0)"] -backup = ["mypy-boto3-backup (>=1.38.0,<1.39.0)"] -backup-gateway = ["mypy-boto3-backup-gateway (>=1.38.0,<1.39.0)"] -backupsearch = ["mypy-boto3-backupsearch (>=1.38.0,<1.39.0)"] -batch = ["mypy-boto3-batch (>=1.38.0,<1.39.0)"] -bcm-data-exports = ["mypy-boto3-bcm-data-exports (>=1.38.0,<1.39.0)"] -bcm-pricing-calculator = ["mypy-boto3-bcm-pricing-calculator (>=1.38.0,<1.39.0)"] -bedrock = ["mypy-boto3-bedrock (>=1.38.0,<1.39.0)"] -bedrock-agent = ["mypy-boto3-bedrock-agent (>=1.38.0,<1.39.0)"] -bedrock-agent-runtime = ["mypy-boto3-bedrock-agent-runtime (>=1.38.0,<1.39.0)"] -bedrock-data-automation = ["mypy-boto3-bedrock-data-automation (>=1.38.0,<1.39.0)"] -bedrock-data-automation-runtime = ["mypy-boto3-bedrock-data-automation-runtime (>=1.38.0,<1.39.0)"] -bedrock-runtime = ["mypy-boto3-bedrock-runtime (>=1.38.0,<1.39.0)"] -billing = ["mypy-boto3-billing (>=1.38.0,<1.39.0)"] -billingconductor = ["mypy-boto3-billingconductor (>=1.38.0,<1.39.0)"] -boto3 = ["boto3 (==1.38.36)"] -braket = ["mypy-boto3-braket (>=1.38.0,<1.39.0)"] -budgets = ["mypy-boto3-budgets (>=1.38.0,<1.39.0)"] -ce = ["mypy-boto3-ce (>=1.38.0,<1.39.0)"] -chatbot = ["mypy-boto3-chatbot (>=1.38.0,<1.39.0)"] -chime = ["mypy-boto3-chime (>=1.38.0,<1.39.0)"] -chime-sdk-identity = ["mypy-boto3-chime-sdk-identity (>=1.38.0,<1.39.0)"] -chime-sdk-media-pipelines = ["mypy-boto3-chime-sdk-media-pipelines (>=1.38.0,<1.39.0)"] -chime-sdk-meetings = ["mypy-boto3-chime-sdk-meetings (>=1.38.0,<1.39.0)"] -chime-sdk-messaging = ["mypy-boto3-chime-sdk-messaging (>=1.38.0,<1.39.0)"] -chime-sdk-voice = ["mypy-boto3-chime-sdk-voice (>=1.38.0,<1.39.0)"] -cleanrooms = ["mypy-boto3-cleanrooms (>=1.38.0,<1.39.0)"] -cleanroomsml = ["mypy-boto3-cleanroomsml (>=1.38.0,<1.39.0)"] -cloud9 = ["mypy-boto3-cloud9 (>=1.38.0,<1.39.0)"] -cloudcontrol = ["mypy-boto3-cloudcontrol (>=1.38.0,<1.39.0)"] -clouddirectory = ["mypy-boto3-clouddirectory (>=1.38.0,<1.39.0)"] -cloudformation = ["mypy-boto3-cloudformation (>=1.38.0,<1.39.0)"] -cloudfront = ["mypy-boto3-cloudfront (>=1.38.0,<1.39.0)"] -cloudfront-keyvaluestore = ["mypy-boto3-cloudfront-keyvaluestore (>=1.38.0,<1.39.0)"] -cloudhsm = ["mypy-boto3-cloudhsm (>=1.38.0,<1.39.0)"] -cloudhsmv2 = ["mypy-boto3-cloudhsmv2 (>=1.38.0,<1.39.0)"] -cloudsearch = ["mypy-boto3-cloudsearch (>=1.38.0,<1.39.0)"] -cloudsearchdomain = ["mypy-boto3-cloudsearchdomain (>=1.38.0,<1.39.0)"] -cloudtrail = ["mypy-boto3-cloudtrail (>=1.38.0,<1.39.0)"] -cloudtrail-data = ["mypy-boto3-cloudtrail-data (>=1.38.0,<1.39.0)"] -cloudwatch = ["mypy-boto3-cloudwatch (>=1.38.0,<1.39.0)"] -codeartifact = ["mypy-boto3-codeartifact (>=1.38.0,<1.39.0)"] -codebuild = ["mypy-boto3-codebuild (>=1.38.0,<1.39.0)"] -codecatalyst = ["mypy-boto3-codecatalyst (>=1.38.0,<1.39.0)"] -codecommit = ["mypy-boto3-codecommit (>=1.38.0,<1.39.0)"] -codeconnections = ["mypy-boto3-codeconnections (>=1.38.0,<1.39.0)"] -codedeploy = ["mypy-boto3-codedeploy (>=1.38.0,<1.39.0)"] -codeguru-reviewer = ["mypy-boto3-codeguru-reviewer (>=1.38.0,<1.39.0)"] -codeguru-security = ["mypy-boto3-codeguru-security (>=1.38.0,<1.39.0)"] -codeguruprofiler = ["mypy-boto3-codeguruprofiler (>=1.38.0,<1.39.0)"] -codepipeline = ["mypy-boto3-codepipeline (>=1.38.0,<1.39.0)"] -codestar-connections = ["mypy-boto3-codestar-connections (>=1.38.0,<1.39.0)"] -codestar-notifications = ["mypy-boto3-codestar-notifications (>=1.38.0,<1.39.0)"] -cognito-identity = ["mypy-boto3-cognito-identity (>=1.38.0,<1.39.0)"] -cognito-idp = ["mypy-boto3-cognito-idp (>=1.38.0,<1.39.0)"] -cognito-sync = ["mypy-boto3-cognito-sync (>=1.38.0,<1.39.0)"] -comprehend = ["mypy-boto3-comprehend (>=1.38.0,<1.39.0)"] -comprehendmedical = ["mypy-boto3-comprehendmedical (>=1.38.0,<1.39.0)"] -compute-optimizer = ["mypy-boto3-compute-optimizer (>=1.38.0,<1.39.0)"] -config = ["mypy-boto3-config (>=1.38.0,<1.39.0)"] -connect = ["mypy-boto3-connect (>=1.38.0,<1.39.0)"] -connect-contact-lens = ["mypy-boto3-connect-contact-lens (>=1.38.0,<1.39.0)"] -connectcampaigns = ["mypy-boto3-connectcampaigns (>=1.38.0,<1.39.0)"] -connectcampaignsv2 = ["mypy-boto3-connectcampaignsv2 (>=1.38.0,<1.39.0)"] -connectcases = ["mypy-boto3-connectcases (>=1.38.0,<1.39.0)"] -connectparticipant = ["mypy-boto3-connectparticipant (>=1.38.0,<1.39.0)"] -controlcatalog = ["mypy-boto3-controlcatalog (>=1.38.0,<1.39.0)"] -controltower = ["mypy-boto3-controltower (>=1.38.0,<1.39.0)"] -cost-optimization-hub = ["mypy-boto3-cost-optimization-hub (>=1.38.0,<1.39.0)"] -cur = ["mypy-boto3-cur (>=1.38.0,<1.39.0)"] -customer-profiles = ["mypy-boto3-customer-profiles (>=1.38.0,<1.39.0)"] -databrew = ["mypy-boto3-databrew (>=1.38.0,<1.39.0)"] -dataexchange = ["mypy-boto3-dataexchange (>=1.38.0,<1.39.0)"] -datapipeline = ["mypy-boto3-datapipeline (>=1.38.0,<1.39.0)"] -datasync = ["mypy-boto3-datasync (>=1.38.0,<1.39.0)"] -datazone = ["mypy-boto3-datazone (>=1.38.0,<1.39.0)"] -dax = ["mypy-boto3-dax (>=1.38.0,<1.39.0)"] -deadline = ["mypy-boto3-deadline (>=1.38.0,<1.39.0)"] -detective = ["mypy-boto3-detective (>=1.38.0,<1.39.0)"] -devicefarm = ["mypy-boto3-devicefarm (>=1.38.0,<1.39.0)"] -devops-guru = ["mypy-boto3-devops-guru (>=1.38.0,<1.39.0)"] -directconnect = ["mypy-boto3-directconnect (>=1.38.0,<1.39.0)"] -discovery = ["mypy-boto3-discovery (>=1.38.0,<1.39.0)"] -dlm = ["mypy-boto3-dlm (>=1.38.0,<1.39.0)"] -dms = ["mypy-boto3-dms (>=1.38.0,<1.39.0)"] -docdb = ["mypy-boto3-docdb (>=1.38.0,<1.39.0)"] -docdb-elastic = ["mypy-boto3-docdb-elastic (>=1.38.0,<1.39.0)"] -drs = ["mypy-boto3-drs (>=1.38.0,<1.39.0)"] -ds = ["mypy-boto3-ds (>=1.38.0,<1.39.0)"] -ds-data = ["mypy-boto3-ds-data (>=1.38.0,<1.39.0)"] -dsql = ["mypy-boto3-dsql (>=1.38.0,<1.39.0)"] -dynamodb = ["mypy-boto3-dynamodb (>=1.38.0,<1.39.0)"] -dynamodbstreams = ["mypy-boto3-dynamodbstreams (>=1.38.0,<1.39.0)"] -ebs = ["mypy-boto3-ebs (>=1.38.0,<1.39.0)"] -ec2 = ["mypy-boto3-ec2 (>=1.38.0,<1.39.0)"] -ec2-instance-connect = ["mypy-boto3-ec2-instance-connect (>=1.38.0,<1.39.0)"] -ecr = ["mypy-boto3-ecr (>=1.38.0,<1.39.0)"] -ecr-public = ["mypy-boto3-ecr-public (>=1.38.0,<1.39.0)"] -ecs = ["mypy-boto3-ecs (>=1.38.0,<1.39.0)"] -efs = ["mypy-boto3-efs (>=1.38.0,<1.39.0)"] -eks = ["mypy-boto3-eks (>=1.38.0,<1.39.0)"] -eks-auth = ["mypy-boto3-eks-auth (>=1.38.0,<1.39.0)"] -elasticache = ["mypy-boto3-elasticache (>=1.38.0,<1.39.0)"] -elasticbeanstalk = ["mypy-boto3-elasticbeanstalk (>=1.38.0,<1.39.0)"] -elastictranscoder = ["mypy-boto3-elastictranscoder (>=1.38.0,<1.39.0)"] -elb = ["mypy-boto3-elb (>=1.38.0,<1.39.0)"] -elbv2 = ["mypy-boto3-elbv2 (>=1.38.0,<1.39.0)"] -emr = ["mypy-boto3-emr (>=1.38.0,<1.39.0)"] -emr-containers = ["mypy-boto3-emr-containers (>=1.38.0,<1.39.0)"] -emr-serverless = ["mypy-boto3-emr-serverless (>=1.38.0,<1.39.0)"] -entityresolution = ["mypy-boto3-entityresolution (>=1.38.0,<1.39.0)"] -es = ["mypy-boto3-es (>=1.38.0,<1.39.0)"] -essential = ["mypy-boto3-cloudformation (>=1.38.0,<1.39.0)", "mypy-boto3-dynamodb (>=1.38.0,<1.39.0)", "mypy-boto3-ec2 (>=1.38.0,<1.39.0)", "mypy-boto3-lambda (>=1.38.0,<1.39.0)", "mypy-boto3-rds (>=1.38.0,<1.39.0)", "mypy-boto3-s3 (>=1.38.0,<1.39.0)", "mypy-boto3-sqs (>=1.38.0,<1.39.0)"] -events = ["mypy-boto3-events (>=1.38.0,<1.39.0)"] -evidently = ["mypy-boto3-evidently (>=1.38.0,<1.39.0)"] -evs = ["mypy-boto3-evs (>=1.38.0,<1.39.0)"] -finspace = ["mypy-boto3-finspace (>=1.38.0,<1.39.0)"] -finspace-data = ["mypy-boto3-finspace-data (>=1.38.0,<1.39.0)"] -firehose = ["mypy-boto3-firehose (>=1.38.0,<1.39.0)"] -fis = ["mypy-boto3-fis (>=1.38.0,<1.39.0)"] -fms = ["mypy-boto3-fms (>=1.38.0,<1.39.0)"] -forecast = ["mypy-boto3-forecast (>=1.38.0,<1.39.0)"] -forecastquery = ["mypy-boto3-forecastquery (>=1.38.0,<1.39.0)"] -frauddetector = ["mypy-boto3-frauddetector (>=1.38.0,<1.39.0)"] -freetier = ["mypy-boto3-freetier (>=1.38.0,<1.39.0)"] -fsx = ["mypy-boto3-fsx (>=1.38.0,<1.39.0)"] -full = ["boto3-stubs-full (>=1.38.0,<1.39.0)"] -gamelift = ["mypy-boto3-gamelift (>=1.38.0,<1.39.0)"] -gameliftstreams = ["mypy-boto3-gameliftstreams (>=1.38.0,<1.39.0)"] -geo-maps = ["mypy-boto3-geo-maps (>=1.38.0,<1.39.0)"] -geo-places = ["mypy-boto3-geo-places (>=1.38.0,<1.39.0)"] -geo-routes = ["mypy-boto3-geo-routes (>=1.38.0,<1.39.0)"] -glacier = ["mypy-boto3-glacier (>=1.38.0,<1.39.0)"] -globalaccelerator = ["mypy-boto3-globalaccelerator (>=1.38.0,<1.39.0)"] -glue = ["mypy-boto3-glue (>=1.38.0,<1.39.0)"] -grafana = ["mypy-boto3-grafana (>=1.38.0,<1.39.0)"] -greengrass = ["mypy-boto3-greengrass (>=1.38.0,<1.39.0)"] -greengrassv2 = ["mypy-boto3-greengrassv2 (>=1.38.0,<1.39.0)"] -groundstation = ["mypy-boto3-groundstation (>=1.38.0,<1.39.0)"] -guardduty = ["mypy-boto3-guardduty (>=1.38.0,<1.39.0)"] -health = ["mypy-boto3-health (>=1.38.0,<1.39.0)"] -healthlake = ["mypy-boto3-healthlake (>=1.38.0,<1.39.0)"] -iam = ["mypy-boto3-iam (>=1.38.0,<1.39.0)"] -identitystore = ["mypy-boto3-identitystore (>=1.38.0,<1.39.0)"] -imagebuilder = ["mypy-boto3-imagebuilder (>=1.38.0,<1.39.0)"] -importexport = ["mypy-boto3-importexport (>=1.38.0,<1.39.0)"] -inspector = ["mypy-boto3-inspector (>=1.38.0,<1.39.0)"] -inspector-scan = ["mypy-boto3-inspector-scan (>=1.38.0,<1.39.0)"] -inspector2 = ["mypy-boto3-inspector2 (>=1.38.0,<1.39.0)"] -internetmonitor = ["mypy-boto3-internetmonitor (>=1.38.0,<1.39.0)"] -invoicing = ["mypy-boto3-invoicing (>=1.38.0,<1.39.0)"] -iot = ["mypy-boto3-iot (>=1.38.0,<1.39.0)"] -iot-data = ["mypy-boto3-iot-data (>=1.38.0,<1.39.0)"] -iot-jobs-data = ["mypy-boto3-iot-jobs-data (>=1.38.0,<1.39.0)"] -iot-managed-integrations = ["mypy-boto3-iot-managed-integrations (>=1.38.0,<1.39.0)"] -iotanalytics = ["mypy-boto3-iotanalytics (>=1.38.0,<1.39.0)"] -iotdeviceadvisor = ["mypy-boto3-iotdeviceadvisor (>=1.38.0,<1.39.0)"] -iotevents = ["mypy-boto3-iotevents (>=1.38.0,<1.39.0)"] -iotevents-data = ["mypy-boto3-iotevents-data (>=1.38.0,<1.39.0)"] -iotfleethub = ["mypy-boto3-iotfleethub (>=1.38.0,<1.39.0)"] -iotfleetwise = ["mypy-boto3-iotfleetwise (>=1.38.0,<1.39.0)"] -iotsecuretunneling = ["mypy-boto3-iotsecuretunneling (>=1.38.0,<1.39.0)"] -iotsitewise = ["mypy-boto3-iotsitewise (>=1.38.0,<1.39.0)"] -iotthingsgraph = ["mypy-boto3-iotthingsgraph (>=1.38.0,<1.39.0)"] -iottwinmaker = ["mypy-boto3-iottwinmaker (>=1.38.0,<1.39.0)"] -iotwireless = ["mypy-boto3-iotwireless (>=1.38.0,<1.39.0)"] -ivs = ["mypy-boto3-ivs (>=1.38.0,<1.39.0)"] -ivs-realtime = ["mypy-boto3-ivs-realtime (>=1.38.0,<1.39.0)"] -ivschat = ["mypy-boto3-ivschat (>=1.38.0,<1.39.0)"] -kafka = ["mypy-boto3-kafka (>=1.38.0,<1.39.0)"] -kafkaconnect = ["mypy-boto3-kafkaconnect (>=1.38.0,<1.39.0)"] -kendra = ["mypy-boto3-kendra (>=1.38.0,<1.39.0)"] -kendra-ranking = ["mypy-boto3-kendra-ranking (>=1.38.0,<1.39.0)"] -keyspaces = ["mypy-boto3-keyspaces (>=1.38.0,<1.39.0)"] -kinesis = ["mypy-boto3-kinesis (>=1.38.0,<1.39.0)"] -kinesis-video-archived-media = ["mypy-boto3-kinesis-video-archived-media (>=1.38.0,<1.39.0)"] -kinesis-video-media = ["mypy-boto3-kinesis-video-media (>=1.38.0,<1.39.0)"] -kinesis-video-signaling = ["mypy-boto3-kinesis-video-signaling (>=1.38.0,<1.39.0)"] -kinesis-video-webrtc-storage = ["mypy-boto3-kinesis-video-webrtc-storage (>=1.38.0,<1.39.0)"] -kinesisanalytics = ["mypy-boto3-kinesisanalytics (>=1.38.0,<1.39.0)"] -kinesisanalyticsv2 = ["mypy-boto3-kinesisanalyticsv2 (>=1.38.0,<1.39.0)"] -kinesisvideo = ["mypy-boto3-kinesisvideo (>=1.38.0,<1.39.0)"] -kms = ["mypy-boto3-kms (>=1.38.0,<1.39.0)"] -lakeformation = ["mypy-boto3-lakeformation (>=1.38.0,<1.39.0)"] -lambda = ["mypy-boto3-lambda (>=1.38.0,<1.39.0)"] -launch-wizard = ["mypy-boto3-launch-wizard (>=1.38.0,<1.39.0)"] -lex-models = ["mypy-boto3-lex-models (>=1.38.0,<1.39.0)"] -lex-runtime = ["mypy-boto3-lex-runtime (>=1.38.0,<1.39.0)"] -lexv2-models = ["mypy-boto3-lexv2-models (>=1.38.0,<1.39.0)"] -lexv2-runtime = ["mypy-boto3-lexv2-runtime (>=1.38.0,<1.39.0)"] -license-manager = ["mypy-boto3-license-manager (>=1.38.0,<1.39.0)"] -license-manager-linux-subscriptions = ["mypy-boto3-license-manager-linux-subscriptions (>=1.38.0,<1.39.0)"] -license-manager-user-subscriptions = ["mypy-boto3-license-manager-user-subscriptions (>=1.38.0,<1.39.0)"] -lightsail = ["mypy-boto3-lightsail (>=1.38.0,<1.39.0)"] -location = ["mypy-boto3-location (>=1.38.0,<1.39.0)"] -logs = ["mypy-boto3-logs (>=1.38.0,<1.39.0)"] -lookoutequipment = ["mypy-boto3-lookoutequipment (>=1.38.0,<1.39.0)"] -lookoutmetrics = ["mypy-boto3-lookoutmetrics (>=1.38.0,<1.39.0)"] -lookoutvision = ["mypy-boto3-lookoutvision (>=1.38.0,<1.39.0)"] -m2 = ["mypy-boto3-m2 (>=1.38.0,<1.39.0)"] -machinelearning = ["mypy-boto3-machinelearning (>=1.38.0,<1.39.0)"] -macie2 = ["mypy-boto3-macie2 (>=1.38.0,<1.39.0)"] -mailmanager = ["mypy-boto3-mailmanager (>=1.38.0,<1.39.0)"] -managedblockchain = ["mypy-boto3-managedblockchain (>=1.38.0,<1.39.0)"] -managedblockchain-query = ["mypy-boto3-managedblockchain-query (>=1.38.0,<1.39.0)"] -marketplace-agreement = ["mypy-boto3-marketplace-agreement (>=1.38.0,<1.39.0)"] -marketplace-catalog = ["mypy-boto3-marketplace-catalog (>=1.38.0,<1.39.0)"] -marketplace-deployment = ["mypy-boto3-marketplace-deployment (>=1.38.0,<1.39.0)"] -marketplace-entitlement = ["mypy-boto3-marketplace-entitlement (>=1.38.0,<1.39.0)"] -marketplace-reporting = ["mypy-boto3-marketplace-reporting (>=1.38.0,<1.39.0)"] -marketplacecommerceanalytics = ["mypy-boto3-marketplacecommerceanalytics (>=1.38.0,<1.39.0)"] -mediaconnect = ["mypy-boto3-mediaconnect (>=1.38.0,<1.39.0)"] -mediaconvert = ["mypy-boto3-mediaconvert (>=1.38.0,<1.39.0)"] -medialive = ["mypy-boto3-medialive (>=1.38.0,<1.39.0)"] -mediapackage = ["mypy-boto3-mediapackage (>=1.38.0,<1.39.0)"] -mediapackage-vod = ["mypy-boto3-mediapackage-vod (>=1.38.0,<1.39.0)"] -mediapackagev2 = ["mypy-boto3-mediapackagev2 (>=1.38.0,<1.39.0)"] -mediastore = ["mypy-boto3-mediastore (>=1.38.0,<1.39.0)"] -mediastore-data = ["mypy-boto3-mediastore-data (>=1.38.0,<1.39.0)"] -mediatailor = ["mypy-boto3-mediatailor (>=1.38.0,<1.39.0)"] -medical-imaging = ["mypy-boto3-medical-imaging (>=1.38.0,<1.39.0)"] -memorydb = ["mypy-boto3-memorydb (>=1.38.0,<1.39.0)"] -meteringmarketplace = ["mypy-boto3-meteringmarketplace (>=1.38.0,<1.39.0)"] -mgh = ["mypy-boto3-mgh (>=1.38.0,<1.39.0)"] -mgn = ["mypy-boto3-mgn (>=1.38.0,<1.39.0)"] -migration-hub-refactor-spaces = ["mypy-boto3-migration-hub-refactor-spaces (>=1.38.0,<1.39.0)"] -migrationhub-config = ["mypy-boto3-migrationhub-config (>=1.38.0,<1.39.0)"] -migrationhuborchestrator = ["mypy-boto3-migrationhuborchestrator (>=1.38.0,<1.39.0)"] -migrationhubstrategy = ["mypy-boto3-migrationhubstrategy (>=1.38.0,<1.39.0)"] -mq = ["mypy-boto3-mq (>=1.38.0,<1.39.0)"] -mturk = ["mypy-boto3-mturk (>=1.38.0,<1.39.0)"] -mwaa = ["mypy-boto3-mwaa (>=1.38.0,<1.39.0)"] -neptune = ["mypy-boto3-neptune (>=1.38.0,<1.39.0)"] -neptune-graph = ["mypy-boto3-neptune-graph (>=1.38.0,<1.39.0)"] -neptunedata = ["mypy-boto3-neptunedata (>=1.38.0,<1.39.0)"] -network-firewall = ["mypy-boto3-network-firewall (>=1.38.0,<1.39.0)"] -networkflowmonitor = ["mypy-boto3-networkflowmonitor (>=1.38.0,<1.39.0)"] -networkmanager = ["mypy-boto3-networkmanager (>=1.38.0,<1.39.0)"] -networkmonitor = ["mypy-boto3-networkmonitor (>=1.38.0,<1.39.0)"] -notifications = ["mypy-boto3-notifications (>=1.38.0,<1.39.0)"] -notificationscontacts = ["mypy-boto3-notificationscontacts (>=1.38.0,<1.39.0)"] -oam = ["mypy-boto3-oam (>=1.38.0,<1.39.0)"] -observabilityadmin = ["mypy-boto3-observabilityadmin (>=1.38.0,<1.39.0)"] -omics = ["mypy-boto3-omics (>=1.38.0,<1.39.0)"] -opensearch = ["mypy-boto3-opensearch (>=1.38.0,<1.39.0)"] -opensearchserverless = ["mypy-boto3-opensearchserverless (>=1.38.0,<1.39.0)"] -opsworks = ["mypy-boto3-opsworks (>=1.38.0,<1.39.0)"] -opsworkscm = ["mypy-boto3-opsworkscm (>=1.38.0,<1.39.0)"] -organizations = ["mypy-boto3-organizations (>=1.38.0,<1.39.0)"] -osis = ["mypy-boto3-osis (>=1.38.0,<1.39.0)"] -outposts = ["mypy-boto3-outposts (>=1.38.0,<1.39.0)"] -panorama = ["mypy-boto3-panorama (>=1.38.0,<1.39.0)"] -partnercentral-selling = ["mypy-boto3-partnercentral-selling (>=1.38.0,<1.39.0)"] -payment-cryptography = ["mypy-boto3-payment-cryptography (>=1.38.0,<1.39.0)"] -payment-cryptography-data = ["mypy-boto3-payment-cryptography-data (>=1.38.0,<1.39.0)"] -pca-connector-ad = ["mypy-boto3-pca-connector-ad (>=1.38.0,<1.39.0)"] -pca-connector-scep = ["mypy-boto3-pca-connector-scep (>=1.38.0,<1.39.0)"] -pcs = ["mypy-boto3-pcs (>=1.38.0,<1.39.0)"] -personalize = ["mypy-boto3-personalize (>=1.38.0,<1.39.0)"] -personalize-events = ["mypy-boto3-personalize-events (>=1.38.0,<1.39.0)"] -personalize-runtime = ["mypy-boto3-personalize-runtime (>=1.38.0,<1.39.0)"] -pi = ["mypy-boto3-pi (>=1.38.0,<1.39.0)"] -pinpoint = ["mypy-boto3-pinpoint (>=1.38.0,<1.39.0)"] -pinpoint-email = ["mypy-boto3-pinpoint-email (>=1.38.0,<1.39.0)"] -pinpoint-sms-voice = ["mypy-boto3-pinpoint-sms-voice (>=1.38.0,<1.39.0)"] -pinpoint-sms-voice-v2 = ["mypy-boto3-pinpoint-sms-voice-v2 (>=1.38.0,<1.39.0)"] -pipes = ["mypy-boto3-pipes (>=1.38.0,<1.39.0)"] -polly = ["mypy-boto3-polly (>=1.38.0,<1.39.0)"] -pricing = ["mypy-boto3-pricing (>=1.38.0,<1.39.0)"] -proton = ["mypy-boto3-proton (>=1.38.0,<1.39.0)"] -qapps = ["mypy-boto3-qapps (>=1.38.0,<1.39.0)"] -qbusiness = ["mypy-boto3-qbusiness (>=1.38.0,<1.39.0)"] -qconnect = ["mypy-boto3-qconnect (>=1.38.0,<1.39.0)"] -qldb = ["mypy-boto3-qldb (>=1.38.0,<1.39.0)"] -qldb-session = ["mypy-boto3-qldb-session (>=1.38.0,<1.39.0)"] -quicksight = ["mypy-boto3-quicksight (>=1.38.0,<1.39.0)"] -ram = ["mypy-boto3-ram (>=1.38.0,<1.39.0)"] -rbin = ["mypy-boto3-rbin (>=1.38.0,<1.39.0)"] -rds = ["mypy-boto3-rds (>=1.38.0,<1.39.0)"] -rds-data = ["mypy-boto3-rds-data (>=1.38.0,<1.39.0)"] -redshift = ["mypy-boto3-redshift (>=1.38.0,<1.39.0)"] -redshift-data = ["mypy-boto3-redshift-data (>=1.38.0,<1.39.0)"] -redshift-serverless = ["mypy-boto3-redshift-serverless (>=1.38.0,<1.39.0)"] -rekognition = ["mypy-boto3-rekognition (>=1.38.0,<1.39.0)"] -repostspace = ["mypy-boto3-repostspace (>=1.38.0,<1.39.0)"] -resiliencehub = ["mypy-boto3-resiliencehub (>=1.38.0,<1.39.0)"] -resource-explorer-2 = ["mypy-boto3-resource-explorer-2 (>=1.38.0,<1.39.0)"] -resource-groups = ["mypy-boto3-resource-groups (>=1.38.0,<1.39.0)"] -resourcegroupstaggingapi = ["mypy-boto3-resourcegroupstaggingapi (>=1.38.0,<1.39.0)"] -robomaker = ["mypy-boto3-robomaker (>=1.38.0,<1.39.0)"] -rolesanywhere = ["mypy-boto3-rolesanywhere (>=1.38.0,<1.39.0)"] -route53 = ["mypy-boto3-route53 (>=1.38.0,<1.39.0)"] -route53-recovery-cluster = ["mypy-boto3-route53-recovery-cluster (>=1.38.0,<1.39.0)"] -route53-recovery-control-config = ["mypy-boto3-route53-recovery-control-config (>=1.38.0,<1.39.0)"] -route53-recovery-readiness = ["mypy-boto3-route53-recovery-readiness (>=1.38.0,<1.39.0)"] -route53domains = ["mypy-boto3-route53domains (>=1.38.0,<1.39.0)"] -route53profiles = ["mypy-boto3-route53profiles (>=1.38.0,<1.39.0)"] -route53resolver = ["mypy-boto3-route53resolver (>=1.38.0,<1.39.0)"] -rum = ["mypy-boto3-rum (>=1.38.0,<1.39.0)"] -s3 = ["mypy-boto3-s3 (>=1.38.0,<1.39.0)"] -s3control = ["mypy-boto3-s3control (>=1.38.0,<1.39.0)"] -s3outposts = ["mypy-boto3-s3outposts (>=1.38.0,<1.39.0)"] -s3tables = ["mypy-boto3-s3tables (>=1.38.0,<1.39.0)"] -sagemaker = ["mypy-boto3-sagemaker (>=1.38.0,<1.39.0)"] -sagemaker-a2i-runtime = ["mypy-boto3-sagemaker-a2i-runtime (>=1.38.0,<1.39.0)"] -sagemaker-edge = ["mypy-boto3-sagemaker-edge (>=1.38.0,<1.39.0)"] -sagemaker-featurestore-runtime = ["mypy-boto3-sagemaker-featurestore-runtime (>=1.38.0,<1.39.0)"] -sagemaker-geospatial = ["mypy-boto3-sagemaker-geospatial (>=1.38.0,<1.39.0)"] -sagemaker-metrics = ["mypy-boto3-sagemaker-metrics (>=1.38.0,<1.39.0)"] -sagemaker-runtime = ["mypy-boto3-sagemaker-runtime (>=1.38.0,<1.39.0)"] -savingsplans = ["mypy-boto3-savingsplans (>=1.38.0,<1.39.0)"] -scheduler = ["mypy-boto3-scheduler (>=1.38.0,<1.39.0)"] -schemas = ["mypy-boto3-schemas (>=1.38.0,<1.39.0)"] -sdb = ["mypy-boto3-sdb (>=1.38.0,<1.39.0)"] -secretsmanager = ["mypy-boto3-secretsmanager (>=1.38.0,<1.39.0)"] -security-ir = ["mypy-boto3-security-ir (>=1.38.0,<1.39.0)"] -securityhub = ["mypy-boto3-securityhub (>=1.38.0,<1.39.0)"] -securitylake = ["mypy-boto3-securitylake (>=1.38.0,<1.39.0)"] -serverlessrepo = ["mypy-boto3-serverlessrepo (>=1.38.0,<1.39.0)"] -service-quotas = ["mypy-boto3-service-quotas (>=1.38.0,<1.39.0)"] -servicecatalog = ["mypy-boto3-servicecatalog (>=1.38.0,<1.39.0)"] -servicecatalog-appregistry = ["mypy-boto3-servicecatalog-appregistry (>=1.38.0,<1.39.0)"] -servicediscovery = ["mypy-boto3-servicediscovery (>=1.38.0,<1.39.0)"] -ses = ["mypy-boto3-ses (>=1.38.0,<1.39.0)"] -sesv2 = ["mypy-boto3-sesv2 (>=1.38.0,<1.39.0)"] -shield = ["mypy-boto3-shield (>=1.38.0,<1.39.0)"] -signer = ["mypy-boto3-signer (>=1.38.0,<1.39.0)"] -simspaceweaver = ["mypy-boto3-simspaceweaver (>=1.38.0,<1.39.0)"] -sms = ["mypy-boto3-sms (>=1.38.0,<1.39.0)"] -snow-device-management = ["mypy-boto3-snow-device-management (>=1.38.0,<1.39.0)"] -snowball = ["mypy-boto3-snowball (>=1.38.0,<1.39.0)"] -sns = ["mypy-boto3-sns (>=1.38.0,<1.39.0)"] -socialmessaging = ["mypy-boto3-socialmessaging (>=1.38.0,<1.39.0)"] -sqs = ["mypy-boto3-sqs (>=1.38.0,<1.39.0)"] -ssm = ["mypy-boto3-ssm (>=1.38.0,<1.39.0)"] -ssm-contacts = ["mypy-boto3-ssm-contacts (>=1.38.0,<1.39.0)"] -ssm-guiconnect = ["mypy-boto3-ssm-guiconnect (>=1.38.0,<1.39.0)"] -ssm-incidents = ["mypy-boto3-ssm-incidents (>=1.38.0,<1.39.0)"] -ssm-quicksetup = ["mypy-boto3-ssm-quicksetup (>=1.38.0,<1.39.0)"] -ssm-sap = ["mypy-boto3-ssm-sap (>=1.38.0,<1.39.0)"] -sso = ["mypy-boto3-sso (>=1.38.0,<1.39.0)"] -sso-admin = ["mypy-boto3-sso-admin (>=1.38.0,<1.39.0)"] -sso-oidc = ["mypy-boto3-sso-oidc (>=1.38.0,<1.39.0)"] -stepfunctions = ["mypy-boto3-stepfunctions (>=1.38.0,<1.39.0)"] -storagegateway = ["mypy-boto3-storagegateway (>=1.38.0,<1.39.0)"] -sts = ["mypy-boto3-sts (>=1.38.0,<1.39.0)"] -supplychain = ["mypy-boto3-supplychain (>=1.38.0,<1.39.0)"] -support = ["mypy-boto3-support (>=1.38.0,<1.39.0)"] -support-app = ["mypy-boto3-support-app (>=1.38.0,<1.39.0)"] -swf = ["mypy-boto3-swf (>=1.38.0,<1.39.0)"] -synthetics = ["mypy-boto3-synthetics (>=1.38.0,<1.39.0)"] -taxsettings = ["mypy-boto3-taxsettings (>=1.38.0,<1.39.0)"] -textract = ["mypy-boto3-textract (>=1.38.0,<1.39.0)"] -timestream-influxdb = ["mypy-boto3-timestream-influxdb (>=1.38.0,<1.39.0)"] -timestream-query = ["mypy-boto3-timestream-query (>=1.38.0,<1.39.0)"] -timestream-write = ["mypy-boto3-timestream-write (>=1.38.0,<1.39.0)"] -tnb = ["mypy-boto3-tnb (>=1.38.0,<1.39.0)"] -transcribe = ["mypy-boto3-transcribe (>=1.38.0,<1.39.0)"] -transfer = ["mypy-boto3-transfer (>=1.38.0,<1.39.0)"] -translate = ["mypy-boto3-translate (>=1.38.0,<1.39.0)"] -trustedadvisor = ["mypy-boto3-trustedadvisor (>=1.38.0,<1.39.0)"] -verifiedpermissions = ["mypy-boto3-verifiedpermissions (>=1.38.0,<1.39.0)"] -voice-id = ["mypy-boto3-voice-id (>=1.38.0,<1.39.0)"] -vpc-lattice = ["mypy-boto3-vpc-lattice (>=1.38.0,<1.39.0)"] -waf = ["mypy-boto3-waf (>=1.38.0,<1.39.0)"] -waf-regional = ["mypy-boto3-waf-regional (>=1.38.0,<1.39.0)"] -wafv2 = ["mypy-boto3-wafv2 (>=1.38.0,<1.39.0)"] -wellarchitected = ["mypy-boto3-wellarchitected (>=1.38.0,<1.39.0)"] -wisdom = ["mypy-boto3-wisdom (>=1.38.0,<1.39.0)"] -workdocs = ["mypy-boto3-workdocs (>=1.38.0,<1.39.0)"] -workmail = ["mypy-boto3-workmail (>=1.38.0,<1.39.0)"] -workmailmessageflow = ["mypy-boto3-workmailmessageflow (>=1.38.0,<1.39.0)"] -workspaces = ["mypy-boto3-workspaces (>=1.38.0,<1.39.0)"] -workspaces-thin-client = ["mypy-boto3-workspaces-thin-client (>=1.38.0,<1.39.0)"] -workspaces-web = ["mypy-boto3-workspaces-web (>=1.38.0,<1.39.0)"] -xray = ["mypy-boto3-xray (>=1.38.0,<1.39.0)"] - -[[package]] -name = "botocore" -version = "1.38.36" -description = "Low-level, data-driven core of boto 3." -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "botocore-1.38.36-py3-none-any.whl", hash = "sha256:b6a50b853f6d23af9edfed89a59800c6bc1687a947cdd3492879f7d64e002d30"}, - {file = "botocore-1.38.36.tar.gz", hash = "sha256:4a1ced1a4218bdff0ed5b46abb54570d473154ddefafa5d121a8d96e4b76ebc1"}, -] - -[package.dependencies] -jmespath = ">=0.7.1,<2.0.0" -python-dateutil = ">=2.1,<3.0.0" -urllib3 = {version = ">=1.25.4,<2.2.0 || >2.2.0,<3", markers = "python_version >= \"3.10\""} - -[package.extras] -crt = ["awscrt (==0.23.8)"] - -[[package]] -name = "botocore-stubs" -version = "1.38.30" -description = "Type annotations and code completion for botocore" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "botocore_stubs-1.38.30-py3-none-any.whl", hash = "sha256:2efb8bdf36504aff596c670d875d8f7dd15205277c15c4cea54afdba8200c266"}, - {file = "botocore_stubs-1.38.30.tar.gz", hash = "sha256:291d7bf39a316c00a8a55b7255489b02c0cea1a343482e7784e8d1e235bae995"}, -] - -[package.dependencies] -types-awscrt = "*" - -[package.extras] -botocore = ["botocore"] - [[package]] name = "brotli" version = "1.1.0" @@ -956,6 +497,10 @@ files = [ {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a37b8f0391212d29b3a91a799c8e4a2855e0576911cdfb2515487e30e322253d"}, {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e84799f09591700a4154154cab9787452925578841a94321d5ee8fb9a9a328f0"}, {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f66b5337fa213f1da0d9000bc8dc0cb5b896b726eefd9c6046f699b169c41b9e"}, + {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5dab0844f2cf82be357a0eb11a9087f70c5430b2c241493fc122bb6f2bb0917c"}, + {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e4fe605b917c70283db7dfe5ada75e04561479075761a0b3866c081d035b01c1"}, + {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:1e9a65b5736232e7a7f91ff3d02277f11d339bf34099a56cdab6a8b3410a02b2"}, + {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:58d4b711689366d4a03ac7957ab8c28890415e267f9b6589969e74b6e42225ec"}, {file = "Brotli-1.1.0-cp310-cp310-win32.whl", hash = "sha256:be36e3d172dc816333f33520154d708a2657ea63762ec16b62ece02ab5e4daf2"}, {file = "Brotli-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:0c6244521dda65ea562d5a69b9a26120769b7a9fb3db2fe9545935ed6735b128"}, {file = "Brotli-1.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a3daabb76a78f829cafc365531c972016e4aa8d5b4bf60660ad8ecee19df7ccc"}, @@ -968,8 +513,14 @@ files = [ {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:19c116e796420b0cee3da1ccec3b764ed2952ccfcc298b55a10e5610ad7885f9"}, {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:510b5b1bfbe20e1a7b3baf5fed9e9451873559a976c1a78eebaa3b86c57b4265"}, {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a1fd8a29719ccce974d523580987b7f8229aeace506952fa9ce1d53a033873c8"}, + {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c247dd99d39e0338a604f8c2b3bc7061d5c2e9e2ac7ba9cc1be5a69cb6cd832f"}, + {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1b2c248cd517c222d89e74669a4adfa5577e06ab68771a529060cf5a156e9757"}, + {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:2a24c50840d89ded6c9a8fdc7b6ed3692ed4e86f1c4a4a938e1e92def92933e0"}, + {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f31859074d57b4639318523d6ffdca586ace54271a73ad23ad021acd807eb14b"}, {file = "Brotli-1.1.0-cp311-cp311-win32.whl", hash = "sha256:39da8adedf6942d76dc3e46653e52df937a3c4d6d18fdc94a7c29d263b1f5b50"}, {file = "Brotli-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:aac0411d20e345dc0920bdec5548e438e999ff68d77564d5e9463a7ca9d3e7b1"}, + {file = "Brotli-1.1.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:32d95b80260d79926f5fab3c41701dbb818fde1c9da590e77e571eefd14abe28"}, + {file = "Brotli-1.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b760c65308ff1e462f65d69c12e4ae085cff3b332d894637f6273a12a482d09f"}, {file = "Brotli-1.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:316cc9b17edf613ac76b1f1f305d2a748f1b976b033b049a6ecdfd5612c70409"}, {file = "Brotli-1.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:caf9ee9a5775f3111642d33b86237b05808dafcd6268faa492250e9b78046eb2"}, {file = "Brotli-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70051525001750221daa10907c77830bc889cb6d865cc0b813d9db7fefc21451"}, @@ -980,8 +531,24 @@ files = [ {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:4093c631e96fdd49e0377a9c167bfd75b6d0bad2ace734c6eb20b348bc3ea180"}, {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:7e4c4629ddad63006efa0ef968c8e4751c5868ff0b1c5c40f76524e894c50248"}, {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:861bf317735688269936f755fa136a99d1ed526883859f86e41a5d43c61d8966"}, + {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:87a3044c3a35055527ac75e419dfa9f4f3667a1e887ee80360589eb8c90aabb9"}, + {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c5529b34c1c9d937168297f2c1fde7ebe9ebdd5e121297ff9c043bdb2ae3d6fb"}, + {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:ca63e1890ede90b2e4454f9a65135a4d387a4585ff8282bb72964fab893f2111"}, + {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e79e6520141d792237c70bcd7a3b122d00f2613769ae0cb61c52e89fd3443839"}, {file = "Brotli-1.1.0-cp312-cp312-win32.whl", hash = "sha256:5f4d5ea15c9382135076d2fb28dde923352fe02951e66935a9efaac8f10e81b0"}, {file = "Brotli-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:906bc3a79de8c4ae5b86d3d75a8b77e44404b0f4261714306e3ad248d8ab0951"}, + {file = "Brotli-1.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8bf32b98b75c13ec7cf774164172683d6e7891088f6316e54425fde1efc276d5"}, + {file = "Brotli-1.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7bc37c4d6b87fb1017ea28c9508b36bbcb0c3d18b4260fcdf08b200c74a6aee8"}, + {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c0ef38c7a7014ffac184db9e04debe495d317cc9c6fb10071f7fefd93100a4f"}, + {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91d7cc2a76b5567591d12c01f019dd7afce6ba8cba6571187e21e2fc418ae648"}, + {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a93dde851926f4f2678e704fadeb39e16c35d8baebd5252c9fd94ce8ce68c4a0"}, + {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f0db75f47be8b8abc8d9e31bc7aad0547ca26f24a54e6fd10231d623f183d089"}, + {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6967ced6730aed543b8673008b5a391c3b1076d834ca438bbd70635c73775368"}, + {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:7eedaa5d036d9336c95915035fb57422054014ebdeb6f3b42eac809928e40d0c"}, + {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d487f5432bf35b60ed625d7e1b448e2dc855422e87469e3f450aa5552b0eb284"}, + {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:832436e59afb93e1836081a20f324cb185836c617659b07b129141a8426973c7"}, + {file = "Brotli-1.1.0-cp313-cp313-win32.whl", hash = "sha256:43395e90523f9c23a3d5bdf004733246fba087f2948f87ab28015f12359ca6a0"}, + {file = "Brotli-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:9011560a466d2eb3f5a6e4929cf4a09be405c64154e12df0dd72713f6500e32b"}, {file = "Brotli-1.1.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:a090ca607cbb6a34b0391776f0cb48062081f5f60ddcce5d11838e67a01928d1"}, {file = "Brotli-1.1.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2de9d02f5bda03d27ede52e8cfe7b865b066fa49258cbab568720aa5be80a47d"}, {file = "Brotli-1.1.0-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2333e30a5e00fe0fe55903c8832e08ee9c3b1382aacf4db26664a16528d51b4b"}, @@ -991,6 +558,10 @@ files = [ {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:fd5f17ff8f14003595ab414e45fce13d073e0762394f957182e69035c9f3d7c2"}, {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:069a121ac97412d1fe506da790b3e69f52254b9df4eb665cd42460c837193354"}, {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:e93dfc1a1165e385cc8239fab7c036fb2cd8093728cbd85097b284d7b99249a2"}, + {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_aarch64.whl", hash = "sha256:aea440a510e14e818e67bfc4027880e2fb500c2ccb20ab21c7a7c8b5b4703d75"}, + {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_i686.whl", hash = "sha256:6974f52a02321b36847cd19d1b8e381bf39939c21efd6ee2fc13a28b0d99348c"}, + {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_ppc64le.whl", hash = "sha256:a7e53012d2853a07a4a79c00643832161a910674a893d296c9f1259859a289d2"}, + {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_x86_64.whl", hash = "sha256:d7702622a8b40c49bffb46e1e3ba2e81268d5c04a34f460978c6b5517a34dd52"}, {file = "Brotli-1.1.0-cp36-cp36m-win32.whl", hash = "sha256:a599669fd7c47233438a56936988a2478685e74854088ef5293802123b5b2460"}, {file = "Brotli-1.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:d143fd47fad1db3d7c27a1b1d66162e855b5d50a89666af46e1679c496e8e579"}, {file = "Brotli-1.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:11d00ed0a83fa22d29bc6b64ef636c4552ebafcef57154b4ddd132f5638fbd1c"}, @@ -1002,6 +573,10 @@ files = [ {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:919e32f147ae93a09fe064d77d5ebf4e35502a8df75c29fb05788528e330fe74"}, {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:23032ae55523cc7bccb4f6a0bf368cd25ad9bcdcc1990b64a647e7bbcce9cb5b"}, {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:224e57f6eac61cc449f498cc5f0e1725ba2071a3d4f48d5d9dffba42db196438"}, + {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:cb1dac1770878ade83f2ccdf7d25e494f05c9165f5246b46a621cc849341dc01"}, + {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:3ee8a80d67a4334482d9712b8e83ca6b1d9bc7e351931252ebef5d8f7335a547"}, + {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:5e55da2c8724191e5b557f8e18943b1b4839b8efc3ef60d65985bcf6f587dd38"}, + {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:d342778ef319e1026af243ed0a07c97acf3bad33b9f29e7ae6a1f68fd083e90c"}, {file = "Brotli-1.1.0-cp37-cp37m-win32.whl", hash = "sha256:587ca6d3cef6e4e868102672d3bd9dc9698c309ba56d41c2b9c85bbb903cdb95"}, {file = "Brotli-1.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:2954c1c23f81c2eaf0b0717d9380bd348578a94161a65b3a2afc62c86467dd68"}, {file = "Brotli-1.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:efa8b278894b14d6da122a72fefcebc28445f2d3f880ac59d46c90f4c13be9a3"}, @@ -1014,6 +589,10 @@ files = [ {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ab4fbee0b2d9098c74f3057b2bc055a8bd92ccf02f65944a241b4349229185a"}, {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:141bd4d93984070e097521ed07e2575b46f817d08f9fa42b16b9b5f27b5ac088"}, {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fce1473f3ccc4187f75b4690cfc922628aed4d3dd013d047f95a9b3919a86596"}, + {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d2b35ca2c7f81d173d2fadc2f4f31e88cc5f7a39ae5b6db5513cf3383b0e0ec7"}, + {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:af6fa6817889314555aede9a919612b23739395ce767fe7fcbea9a80bf140fe5"}, + {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:2feb1d960f760a575dbc5ab3b1c00504b24caaf6986e2dc2b01c09c87866a943"}, + {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:4410f84b33374409552ac9b6903507cdb31cd30d2501fc5ca13d18f73548444a"}, {file = "Brotli-1.1.0-cp38-cp38-win32.whl", hash = "sha256:db85ecf4e609a48f4b29055f1e144231b90edc90af7481aa731ba2d059226b1b"}, {file = "Brotli-1.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:3d7954194c36e304e1523f55d7042c59dc53ec20dd4e9ea9d151f1b62b4415c0"}, {file = "Brotli-1.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5fb2ce4b8045c78ebbc7b8f3c15062e435d47e7393cc57c25115cfd49883747a"}, @@ -1026,6 +605,10 @@ files = [ {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:949f3b7c29912693cee0afcf09acd6ebc04c57af949d9bf77d6101ebb61e388c"}, {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:89f4988c7203739d48c6f806f1e87a1d96e0806d44f0fba61dba81392c9e474d"}, {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:de6551e370ef19f8de1807d0a9aa2cdfdce2e85ce88b122fe9f6b2b076837e59"}, + {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0737ddb3068957cf1b054899b0883830bb1fec522ec76b1098f9b6e0f02d9419"}, + {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:4f3607b129417e111e30637af1b56f24f7a49e64763253bbc275c75fa887d4b2"}, + {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:6c6e0c425f22c1c719c42670d561ad682f7bfeeef918edea971a79ac5252437f"}, + {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:494994f807ba0b92092a163a0a283961369a65f6cbe01e8891132b7a320e61eb"}, {file = "Brotli-1.1.0-cp39-cp39-win32.whl", hash = "sha256:f0d8a7a6b5983c2496e364b969f0e526647a06b075d034f3297dc66f3b360c64"}, {file = "Brotli-1.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:cdad5b9014d83ca68c25d2e9444e28e967ef16e80f6b436918c700c117a85467"}, {file = "Brotli-1.1.0.tar.gz", hash = "sha256:81de08ac11bcb85841e440c13611c00b67d3bf82698314928d0b676362546724"}, @@ -1074,95 +657,112 @@ cffi = ">=1.0.0" [[package]] name = "certifi" -version = "2025.4.26" +version = "2025.10.5" description = "Python package for providing Mozilla's CA Bundle." optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" groups = ["main"] files = [ - {file = "certifi-2025.4.26-py3-none-any.whl", hash = "sha256:30350364dfe371162649852c63336a15c70c6510c2ad5015b21c2345311805f3"}, - {file = "certifi-2025.4.26.tar.gz", hash = "sha256:0a816057ea3cdefcef70270d2c515e4506bbc954f417fa5ade2021213bb8f0c6"}, + {file = "certifi-2025.10.5-py3-none-any.whl", hash = "sha256:0f212c2744a9bb6de0c56639a6f68afe01ecd92d91f14ae897c4fe7bbeeef0de"}, + {file = "certifi-2025.10.5.tar.gz", hash = "sha256:47c09d31ccf2acf0be3f701ea53595ee7e0b8fa08801c6624be771df09ae7b43"}, ] [[package]] name = "cffi" -version = "1.17.1" +version = "2.0.0" description = "Foreign Function Interface for Python calling C code." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["main"] files = [ - {file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"}, - {file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"}, - {file = "cffi-1.17.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382"}, - {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702"}, - {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3"}, - {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6"}, - {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17"}, - {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8"}, - {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e"}, - {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be"}, - {file = "cffi-1.17.1-cp310-cp310-win32.whl", hash = "sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c"}, - {file = "cffi-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15"}, - {file = "cffi-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401"}, - {file = "cffi-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf"}, - {file = "cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4"}, - {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41"}, - {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1"}, - {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6"}, - {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d"}, - {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6"}, - {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f"}, - {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b"}, - {file = "cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655"}, - {file = "cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0"}, - {file = "cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4"}, - {file = "cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c"}, - {file = "cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36"}, - {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5"}, - {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff"}, - {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99"}, - {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93"}, - {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3"}, - {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8"}, - {file = "cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65"}, - {file = "cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903"}, - {file = "cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e"}, - {file = "cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2"}, - {file = "cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3"}, - {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683"}, - {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5"}, - {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4"}, - {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd"}, - {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed"}, - {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9"}, - {file = "cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d"}, - {file = "cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a"}, - {file = "cffi-1.17.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:636062ea65bd0195bc012fea9321aca499c0504409f413dc88af450b57ffd03b"}, - {file = "cffi-1.17.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7eac2ef9b63c79431bc4b25f1cd649d7f061a28808cbc6c47b534bd789ef964"}, - {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e221cf152cff04059d011ee126477f0d9588303eb57e88923578ace7baad17f9"}, - {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:31000ec67d4221a71bd3f67df918b1f88f676f1c3b535a7eb473255fdc0b83fc"}, - {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f17be4345073b0a7b8ea599688f692ac3ef23ce28e5df79c04de519dbc4912c"}, - {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2b1fac190ae3ebfe37b979cc1ce69c81f4e4fe5746bb401dca63a9062cdaf1"}, - {file = "cffi-1.17.1-cp38-cp38-win32.whl", hash = "sha256:7596d6620d3fa590f677e9ee430df2958d2d6d6de2feeae5b20e82c00b76fbf8"}, - {file = "cffi-1.17.1-cp38-cp38-win_amd64.whl", hash = "sha256:78122be759c3f8a014ce010908ae03364d00a1f81ab5c7f4a7a5120607ea56e1"}, - {file = "cffi-1.17.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b2ab587605f4ba0bf81dc0cb08a41bd1c0a5906bd59243d56bad7668a6fc6c16"}, - {file = "cffi-1.17.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:28b16024becceed8c6dfbc75629e27788d8a3f9030691a1dbf9821a128b22c36"}, - {file = "cffi-1.17.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d599671f396c4723d016dbddb72fe8e0397082b0a77a4fab8028923bec050e8"}, - {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca74b8dbe6e8e8263c0ffd60277de77dcee6c837a3d0881d8c1ead7268c9e576"}, - {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87"}, - {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98e3969bcff97cae1b2def8ba499ea3d6f31ddfdb7635374834cf89a1a08ecf0"}, - {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdf5ce3acdfd1661132f2a9c19cac174758dc2352bfe37d98aa7512c6b7178b3"}, - {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9755e4345d1ec879e3849e62222a18c7174d65a6a92d5b346b1863912168b595"}, - {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f1e22e8c4419538cb197e4dd60acc919d7696e5ef98ee4da4e01d3f8cfa4cc5a"}, - {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c03e868a0b3bc35839ba98e74211ed2b05d2119be4e8a0f224fba9384f1fe02e"}, - {file = "cffi-1.17.1-cp39-cp39-win32.whl", hash = "sha256:e31ae45bc2e29f6b2abd0de1cc3b9d5205aa847cafaecb8af1476a609a2f6eb7"}, - {file = "cffi-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662"}, - {file = "cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824"}, + {file = "cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44"}, + {file = "cffi-2.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49"}, + {file = "cffi-2.0.0-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c"}, + {file = "cffi-2.0.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb"}, + {file = "cffi-2.0.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0"}, + {file = "cffi-2.0.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4"}, + {file = "cffi-2.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453"}, + {file = "cffi-2.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495"}, + {file = "cffi-2.0.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5"}, + {file = "cffi-2.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb"}, + {file = "cffi-2.0.0-cp310-cp310-win32.whl", hash = "sha256:1f72fb8906754ac8a2cc3f9f5aaa298070652a0ffae577e0ea9bd480dc3c931a"}, + {file = "cffi-2.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:b18a3ed7d5b3bd8d9ef7a8cb226502c6bf8308df1525e1cc676c3680e7176739"}, + {file = "cffi-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe"}, + {file = "cffi-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c"}, + {file = "cffi-2.0.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92"}, + {file = "cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93"}, + {file = "cffi-2.0.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5"}, + {file = "cffi-2.0.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664"}, + {file = "cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26"}, + {file = "cffi-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9"}, + {file = "cffi-2.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414"}, + {file = "cffi-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743"}, + {file = "cffi-2.0.0-cp311-cp311-win32.whl", hash = "sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5"}, + {file = "cffi-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5"}, + {file = "cffi-2.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d"}, + {file = "cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d"}, + {file = "cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c"}, + {file = "cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe"}, + {file = "cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062"}, + {file = "cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e"}, + {file = "cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037"}, + {file = "cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba"}, + {file = "cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94"}, + {file = "cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187"}, + {file = "cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18"}, + {file = "cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5"}, + {file = "cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6"}, + {file = "cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb"}, + {file = "cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca"}, + {file = "cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b"}, + {file = "cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b"}, + {file = "cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2"}, + {file = "cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3"}, + {file = "cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26"}, + {file = "cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c"}, + {file = "cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b"}, + {file = "cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27"}, + {file = "cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75"}, + {file = "cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91"}, + {file = "cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5"}, + {file = "cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13"}, + {file = "cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b"}, + {file = "cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c"}, + {file = "cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef"}, + {file = "cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775"}, + {file = "cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205"}, + {file = "cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1"}, + {file = "cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f"}, + {file = "cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25"}, + {file = "cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad"}, + {file = "cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9"}, + {file = "cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d"}, + {file = "cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c"}, + {file = "cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8"}, + {file = "cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc"}, + {file = "cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592"}, + {file = "cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512"}, + {file = "cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4"}, + {file = "cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e"}, + {file = "cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6"}, + {file = "cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9"}, + {file = "cffi-2.0.0-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:fe562eb1a64e67dd297ccc4f5addea2501664954f2692b69a76449ec7913ecbf"}, + {file = "cffi-2.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:de8dad4425a6ca6e4e5e297b27b5c824ecc7581910bf9aee86cb6835e6812aa7"}, + {file = "cffi-2.0.0-cp39-cp39-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:4647afc2f90d1ddd33441e5b0e85b16b12ddec4fca55f0d9671fef036ecca27c"}, + {file = "cffi-2.0.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3f4d46d8b35698056ec29bca21546e1551a205058ae1a181d871e278b0b28165"}, + {file = "cffi-2.0.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:e6e73b9e02893c764e7e8d5bb5ce277f1a009cd5243f8228f75f842bf937c534"}, + {file = "cffi-2.0.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:cb527a79772e5ef98fb1d700678fe031e353e765d1ca2d409c92263c6d43e09f"}, + {file = "cffi-2.0.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:61d028e90346df14fedc3d1e5441df818d095f3b87d286825dfcbd6459b7ef63"}, + {file = "cffi-2.0.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0f6084a0ea23d05d20c3edcda20c3d006f9b6f3fefeac38f59262e10cef47ee2"}, + {file = "cffi-2.0.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:1cd13c99ce269b3ed80b417dcd591415d3372bcac067009b6e0f59c7d4015e65"}, + {file = "cffi-2.0.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:89472c9762729b5ae1ad974b777416bfda4ac5642423fa93bd57a09204712322"}, + {file = "cffi-2.0.0-cp39-cp39-win32.whl", hash = "sha256:2081580ebb843f759b9f617314a24ed5738c51d2aee65d31e02f6f7a2b97707a"}, + {file = "cffi-2.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:b882b3df248017dba09d6b16defe9b5c407fe32fc7c65a9c69798e6175601be9"}, + {file = "cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529"}, ] [package.dependencies] -pycparser = "*" +pycparser = {version = "*", markers = "implementation_name != \"PyPy\""} [[package]] name = "chardet" @@ -1178,116 +778,160 @@ files = [ [[package]] name = "charset-normalizer" -version = "3.4.2" +version = "3.4.4" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7" groups = ["main"] files = [ - {file = "charset_normalizer-3.4.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c48ed483eb946e6c04ccbe02c6b4d1d48e51944b6db70f697e089c193404941"}, - {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2d318c11350e10662026ad0eb71bb51c7812fc8590825304ae0bdd4ac283acd"}, - {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9cbfacf36cb0ec2897ce0ebc5d08ca44213af24265bd56eca54bee7923c48fd6"}, - {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18dd2e350387c87dabe711b86f83c9c78af772c748904d372ade190b5c7c9d4d"}, - {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8075c35cd58273fee266c58c0c9b670947c19df5fb98e7b66710e04ad4e9ff86"}, - {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5bf4545e3b962767e5c06fe1738f951f77d27967cb2caa64c28be7c4563e162c"}, - {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7a6ab32f7210554a96cd9e33abe3ddd86732beeafc7a28e9955cdf22ffadbab0"}, - {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b33de11b92e9f75a2b545d6e9b6f37e398d86c3e9e9653c4864eb7e89c5773ef"}, - {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8755483f3c00d6c9a77f490c17e6ab0c8729e39e6390328e42521ef175380ae6"}, - {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:68a328e5f55ec37c57f19ebb1fdc56a248db2e3e9ad769919a58672958e8f366"}, - {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:21b2899062867b0e1fde9b724f8aecb1af14f2778d69aacd1a5a1853a597a5db"}, - {file = "charset_normalizer-3.4.2-cp310-cp310-win32.whl", hash = "sha256:e8082b26888e2f8b36a042a58307d5b917ef2b1cacab921ad3323ef91901c71a"}, - {file = "charset_normalizer-3.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:f69a27e45c43520f5487f27627059b64aaf160415589230992cec34c5e18a509"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:be1e352acbe3c78727a16a455126d9ff83ea2dfdcbc83148d2982305a04714c2"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa88ca0b1932e93f2d961bf3addbb2db902198dca337d88c89e1559e066e7645"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d524ba3f1581b35c03cb42beebab4a13e6cdad7b36246bd22541fa585a56cccd"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28a1005facc94196e1fb3e82a3d442a9d9110b8434fc1ded7a24a2983c9888d8"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdb20a30fe1175ecabed17cbf7812f7b804b8a315a25f24678bcdf120a90077f"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0f5d9ed7f254402c9e7d35d2f5972c9bbea9040e99cd2861bd77dc68263277c7"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:efd387a49825780ff861998cd959767800d54f8308936b21025326de4b5a42b9"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f0aa37f3c979cf2546b73e8222bbfa3dc07a641585340179d768068e3455e544"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e70e990b2137b29dc5564715de1e12701815dacc1d056308e2b17e9095372a82"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0c8c57f84ccfc871a48a47321cfa49ae1df56cd1d965a09abe84066f6853b9c0"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6b66f92b17849b85cad91259efc341dce9c1af48e2173bf38a85c6329f1033e5"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-win32.whl", hash = "sha256:daac4765328a919a805fa5e2720f3e94767abd632ae410a9062dff5412bae65a"}, - {file = "charset_normalizer-3.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:e53efc7c7cee4c1e70661e2e112ca46a575f90ed9ae3fef200f2a25e954f4b28"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0c29de6a1a95f24b9a1aa7aefd27d2487263f00dfd55a77719b530788f75cff7"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cddf7bd982eaa998934a91f69d182aec997c6c468898efe6679af88283b498d3"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcbe676a55d7445b22c10967bceaaf0ee69407fbe0ece4d032b6eb8d4565982a"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d41c4d287cfc69060fa91cae9683eacffad989f1a10811995fa309df656ec214"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e594135de17ab3866138f496755f302b72157d115086d100c3f19370839dd3a"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf713fe9a71ef6fd5adf7a79670135081cd4431c2943864757f0fa3a65b1fafd"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a370b3e078e418187da8c3674eddb9d983ec09445c99a3a263c2011993522981"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a955b438e62efdf7e0b7b52a64dc5c3396e2634baa62471768a64bc2adb73d5c"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:7222ffd5e4de8e57e03ce2cef95a4c43c98fcb72ad86909abdfc2c17d227fc1b"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:bee093bf902e1d8fc0ac143c88902c3dfc8941f7ea1d6a8dd2bcb786d33db03d"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb8adb91d11846ee08bec4c8236c8549ac721c245678282dcb06b221aab59f"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-win32.whl", hash = "sha256:db4c7bf0e07fc3b7d89ac2a5880a6a8062056801b83ff56d8464b70f65482b6c"}, - {file = "charset_normalizer-3.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:5a9979887252a82fefd3d3ed2a8e3b937a7a809f65dcb1e068b090e165bbe99e"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:926ca93accd5d36ccdabd803392ddc3e03e6d4cd1cf17deff3b989ab8e9dbcf0"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eba9904b0f38a143592d9fc0e19e2df0fa2e41c3c3745554761c5f6447eedabf"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fddb7e2c84ac87ac3a947cb4e66d143ca5863ef48e4a5ecb83bd48619e4634e"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98f862da73774290f251b9df8d11161b6cf25b599a66baf087c1ffe340e9bfd1"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c9379d65defcab82d07b2a9dfbfc2e95bc8fe0ebb1b176a3190230a3ef0e07c"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e635b87f01ebc977342e2697d05b56632f5f879a4f15955dfe8cef2448b51691"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1c95a1e2902a8b722868587c0e1184ad5c55631de5afc0eb96bc4b0d738092c0"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ef8de666d6179b009dce7bcb2ad4c4a779f113f12caf8dc77f0162c29d20490b"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:32fc0341d72e0f73f80acb0a2c94216bd704f4f0bce10aedea38f30502b271ff"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:289200a18fa698949d2b39c671c2cc7a24d44096784e76614899a7ccf2574b7b"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4a476b06fbcf359ad25d34a057b7219281286ae2477cc5ff5e3f70a246971148"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-win32.whl", hash = "sha256:aaeeb6a479c7667fbe1099af9617c83aaca22182d6cf8c53966491a0f1b7ffb7"}, - {file = "charset_normalizer-3.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:aa6af9e7d59f9c12b33ae4e9450619cf2488e2bbe9b44030905877f0b2324980"}, - {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cad5f45b3146325bb38d6855642f6fd609c3f7cad4dbaf75549bf3b904d3184"}, - {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b2680962a4848b3c4f155dc2ee64505a9c57186d0d56b43123b17ca3de18f0fa"}, - {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:36b31da18b8890a76ec181c3cf44326bf2c48e36d393ca1b72b3f484113ea344"}, - {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f4074c5a429281bf056ddd4c5d3b740ebca4d43ffffe2ef4bf4d2d05114299da"}, - {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c9e36a97bee9b86ef9a1cf7bb96747eb7a15c2f22bdb5b516434b00f2a599f02"}, - {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:1b1bde144d98e446b056ef98e59c256e9294f6b74d7af6846bf5ffdafd687a7d"}, - {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:915f3849a011c1f593ab99092f3cecfcb4d65d8feb4a64cf1bf2d22074dc0ec4"}, - {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:fb707f3e15060adf5b7ada797624a6c6e0138e2a26baa089df64c68ee98e040f"}, - {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:25a23ea5c7edc53e0f29bae2c44fcb5a1aa10591aae107f2a2b2583a9c5cbc64"}, - {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:770cab594ecf99ae64c236bc9ee3439c3f46be49796e265ce0cc8bc17b10294f"}, - {file = "charset_normalizer-3.4.2-cp37-cp37m-win32.whl", hash = "sha256:6a0289e4589e8bdfef02a80478f1dfcb14f0ab696b5a00e1f4b8a14a307a3c58"}, - {file = "charset_normalizer-3.4.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6fc1f5b51fa4cecaa18f2bd7a003f3dd039dd615cd69a2afd6d3b19aed6775f2"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:76af085e67e56c8816c3ccf256ebd136def2ed9654525348cfa744b6802b69eb"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e45ba65510e2647721e35323d6ef54c7974959f6081b58d4ef5d87c60c84919a"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:046595208aae0120559a67693ecc65dd75d46f7bf687f159127046628178dc45"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75d10d37a47afee94919c4fab4c22b9bc2a8bf7d4f46f87363bcf0573f3ff4f5"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6333b3aa5a12c26b2a4d4e7335a28f1475e0e5e17d69d55141ee3cab736f66d1"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e8323a9b031aa0393768b87f04b4164a40037fb2a3c11ac06a03ffecd3618027"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:24498ba8ed6c2e0b56d4acbf83f2d989720a93b41d712ebd4f4979660db4417b"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:844da2b5728b5ce0e32d863af26f32b5ce61bc4273a9c720a9f3aa9df73b1455"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:65c981bdbd3f57670af8b59777cbfae75364b483fa8a9f420f08094531d54a01"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:3c21d4fca343c805a52c0c78edc01e3477f6dd1ad7c47653241cf2a206d4fc58"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:dc7039885fa1baf9be153a0626e337aa7ec8bf96b0128605fb0d77788ddc1681"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-win32.whl", hash = "sha256:8272b73e1c5603666618805fe821edba66892e2870058c94c53147602eab29c7"}, - {file = "charset_normalizer-3.4.2-cp38-cp38-win_amd64.whl", hash = "sha256:70f7172939fdf8790425ba31915bfbe8335030f05b9913d7ae00a87d4395620a"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:005fa3432484527f9732ebd315da8da8001593e2cf46a3d817669f062c3d9ed4"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e92fca20c46e9f5e1bb485887d074918b13543b1c2a1185e69bb8d17ab6236a7"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:50bf98d5e563b83cc29471fa114366e6806bc06bc7a25fd59641e41445327836"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:721c76e84fe669be19c5791da68232ca2e05ba5185575086e384352e2c309597"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82d8fd25b7f4675d0c47cf95b594d4e7b158aca33b76aa63d07186e13c0e0ab7"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3daeac64d5b371dea99714f08ffc2c208522ec6b06fbc7866a450dd446f5c0f"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:dccab8d5fa1ef9bfba0590ecf4d46df048d18ffe3eec01eeb73a42e0d9e7a8ba"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:aaf27faa992bfee0264dc1f03f4c75e9fcdda66a519db6b957a3f826e285cf12"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:eb30abc20df9ab0814b5a2524f23d75dcf83cde762c161917a2b4b7b55b1e518"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:c72fbbe68c6f32f251bdc08b8611c7b3060612236e960ef848e0a517ddbe76c5"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:982bb1e8b4ffda883b3d0a521e23abcd6fd17418f6d2c4118d257a10199c0ce3"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-win32.whl", hash = "sha256:43e0933a0eff183ee85833f341ec567c0980dae57c464d8a508e1b2ceb336471"}, - {file = "charset_normalizer-3.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:d11b54acf878eef558599658b0ffca78138c8c3655cf4f3a4a673c437e67732e"}, - {file = "charset_normalizer-3.4.2-py3-none-any.whl", hash = "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0"}, - {file = "charset_normalizer-3.4.2.tar.gz", hash = "sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e824f1492727fa856dd6eda4f7cee25f8518a12f3c4a56a74e8095695089cf6d"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4bd5d4137d500351a30687c2d3971758aac9a19208fc110ccb9d7188fbe709e8"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:027f6de494925c0ab2a55eab46ae5129951638a49a34d87f4c3eda90f696b4ad"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f820802628d2694cb7e56db99213f930856014862f3fd943d290ea8438d07ca8"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:798d75d81754988d2565bff1b97ba5a44411867c0cf32b77a7e8f8d84796b10d"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d1bb833febdff5c8927f922386db610b49db6e0d4f4ee29601d71e7c2694313"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9cd98cdc06614a2f768d2b7286d66805f94c48cde050acdbbb7db2600ab3197e"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:077fbb858e903c73f6c9db43374fd213b0b6a778106bc7032446a8e8b5b38b93"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:244bfb999c71b35de57821b8ea746b24e863398194a4014e4c76adc2bbdfeff0"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:64b55f9dce520635f018f907ff1b0df1fdc31f2795a922fb49dd14fbcdf48c84"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:faa3a41b2b66b6e50f84ae4a68c64fcd0c44355741c6374813a800cd6695db9e"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6515f3182dbe4ea06ced2d9e8666d97b46ef4c75e326b79bb624110f122551db"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cc00f04ed596e9dc0da42ed17ac5e596c6ccba999ba6bd92b0e0aef2f170f2d6"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-win32.whl", hash = "sha256:f34be2938726fc13801220747472850852fe6b1ea75869a048d6f896838c896f"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:a61900df84c667873b292c3de315a786dd8dac506704dea57bc957bd31e22c7d"}, + {file = "charset_normalizer-3.4.4-cp310-cp310-win_arm64.whl", hash = "sha256:cead0978fc57397645f12578bfd2d5ea9138ea0fac82b2f63f7f7c6877986a69"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6e1fcf0720908f200cd21aa4e6750a48ff6ce4afe7ff5a79a90d5ed8a08296f8"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f819d5fe9234f9f82d75bdfa9aef3a3d72c4d24a6e57aeaebba32a704553aa0"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a59cb51917aa591b1c4e6a43c132f0cdc3c76dbad6155df4e28ee626cc77a0a3"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8ef3c867360f88ac904fd3f5e1f902f13307af9052646963ee08ff4f131adafc"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d9e45d7faa48ee908174d8fe84854479ef838fc6a705c9315372eacbc2f02897"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:840c25fb618a231545cbab0564a799f101b63b9901f2569faecd6b222ac72381"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ca5862d5b3928c4940729dacc329aa9102900382fea192fc5e52eb69d6093815"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d9c7f57c3d666a53421049053eaacdd14bbd0a528e2186fcb2e672effd053bb0"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:277e970e750505ed74c832b4bf75dac7476262ee2a013f5574dd49075879e161"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:31fd66405eaf47bb62e8cd575dc621c56c668f27d46a61d975a249930dd5e2a4"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:0d3d8f15c07f86e9ff82319b3d9ef6f4bf907608f53fe9d92b28ea9ae3d1fd89"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:9f7fcd74d410a36883701fafa2482a6af2ff5ba96b9a620e9e0721e28ead5569"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ebf3e58c7ec8a8bed6d66a75d7fb37b55e5015b03ceae72a8e7c74495551e224"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-win32.whl", hash = "sha256:eecbc200c7fd5ddb9a7f16c7decb07b566c29fa2161a16cf67b8d068bd21690a"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:5ae497466c7901d54b639cf42d5b8c1b6a4fead55215500d2f486d34db48d016"}, + {file = "charset_normalizer-3.4.4-cp311-cp311-win_arm64.whl", hash = "sha256:65e2befcd84bc6f37095f5961e68a6f077bf44946771354a28ad434c2cce0ae1"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0a98e6759f854bd25a58a73fa88833fba3b7c491169f86ce1180c948ab3fd394"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b5b290ccc2a263e8d185130284f8501e3e36c5e02750fc6b6bdeb2e9e96f1e25"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74bb723680f9f7a6234dcf67aea57e708ec1fbdf5699fb91dfd6f511b0a320ef"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f1e34719c6ed0b92f418c7c780480b26b5d9c50349e9a9af7d76bf757530350d"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2437418e20515acec67d86e12bf70056a33abdacb5cb1655042f6538d6b085a8"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11d694519d7f29d6cd09f6ac70028dba10f92f6cdd059096db198c283794ac86"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ac1c4a689edcc530fc9d9aa11f5774b9e2f33f9a0c6a57864e90908f5208d30a"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:21d142cc6c0ec30d2efee5068ca36c128a30b0f2c53c1c07bd78cb6bc1d3be5f"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:5dbe56a36425d26d6cfb40ce79c314a2e4dd6211d51d6d2191c00bed34f354cc"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5bfbb1b9acf3334612667b61bd3002196fe2a1eb4dd74d247e0f2a4d50ec9bbf"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:d055ec1e26e441f6187acf818b73564e6e6282709e9bcb5b63f5b23068356a15"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:af2d8c67d8e573d6de5bc30cdb27e9b95e49115cd9baad5ddbd1a6207aaa82a9"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:780236ac706e66881f3b7f2f32dfe90507a09e67d1d454c762cf642e6e1586e0"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-win32.whl", hash = "sha256:5833d2c39d8896e4e19b689ffc198f08ea58116bee26dea51e362ecc7cd3ed26"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:a79cfe37875f822425b89a82333404539ae63dbdddf97f84dcbc3d339aae9525"}, + {file = "charset_normalizer-3.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:376bec83a63b8021bb5c8ea75e21c4ccb86e7e45ca4eb81146091b56599b80c3"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-win32.whl", hash = "sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14"}, + {file = "charset_normalizer-3.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-win32.whl", hash = "sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c"}, + {file = "charset_normalizer-3.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ce8a0633f41a967713a59c4139d29110c07e826d131a316b50ce11b1d79b4f84"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eaabd426fe94daf8fd157c32e571c85cb12e66692f15516a83a03264b08d06c3"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c4ef880e27901b6cc782f1b95f82da9313c0eb95c3af699103088fa0ac3ce9ac"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2aaba3b0819274cc41757a1da876f810a3e4d7b6eb25699253a4effef9e8e4af"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:778d2e08eda00f4256d7f672ca9fef386071c9202f5e4607920b86d7803387f2"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f155a433c2ec037d4e8df17d18922c3a0d9b3232a396690f17175d2946f0218d"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a8bf8d0f749c5757af2142fe7903a9df1d2e8aa3841559b2bad34b08d0e2bcf3"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:194f08cbb32dc406d6e1aea671a68be0823673db2832b38405deba2fb0d88f63"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_armv7l.whl", hash = "sha256:6aee717dcfead04c6eb1ce3bd29ac1e22663cdea57f943c87d1eab9a025438d7"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:cd4b7ca9984e5e7985c12bc60a6f173f3c958eae74f3ef6624bb6b26e2abbae4"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_riscv64.whl", hash = "sha256:b7cf1017d601aa35e6bb650b6ad28652c9cd78ee6caff19f3c28d03e1c80acbf"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:e912091979546adf63357d7e2ccff9b44f026c075aeaf25a52d0e95ad2281074"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:5cb4d72eea50c8868f5288b7f7f33ed276118325c1dfd3957089f6b519e1382a"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-win32.whl", hash = "sha256:837c2ce8c5a65a2035be9b3569c684358dfbf109fd3b6969630a87535495ceaa"}, + {file = "charset_normalizer-3.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:44c2a8734b333e0578090c4cd6b16f275e07aa6614ca8715e6c038e865e70576"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a9768c477b9d7bd54bc0c86dbaebdec6f03306675526c9927c0e8a04e8f94af9"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1bee1e43c28aa63cb16e5c14e582580546b08e535299b8b6158a7c9c768a1f3d"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:fd44c878ea55ba351104cb93cc85e74916eb8fa440ca7903e57575e97394f608"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0f04b14ffe5fdc8c4933862d8306109a2c51e0704acfa35d51598eb45a1e89fc"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:cd09d08005f958f370f539f186d10aec3377d55b9eeb0d796025d4886119d76e"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4fe7859a4e3e8457458e2ff592f15ccb02f3da787fcd31e0183879c3ad4692a1"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fa09f53c465e532f4d3db095e0c55b615f010ad81803d383195b6b5ca6cbf5f3"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:7fa17817dc5625de8a027cb8b26d9fefa3ea28c8253929b8d6649e705d2835b6"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:5947809c8a2417be3267efc979c47d76a079758166f7d43ef5ae8e9f92751f88"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:4902828217069c3c5c71094537a8e623f5d097858ac6ca8252f7b4d10b7560f1"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:7c308f7e26e4363d79df40ca5b2be1c6ba9f02bdbccfed5abddb7859a6ce72cf"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:2c9d3c380143a1fedbff95a312aa798578371eb29da42106a29019368a475318"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:cb01158d8b88ee68f15949894ccc6712278243d95f344770fa7593fa2d94410c"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-win32.whl", hash = "sha256:2677acec1a2f8ef614c6888b5b4ae4060cc184174a938ed4e8ef690e15d3e505"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:f8e160feb2aed042cd657a72acc0b481212ed28b1b9a95c0cee1621b524e1966"}, + {file = "charset_normalizer-3.4.4-cp39-cp39-win_arm64.whl", hash = "sha256:b5d84d37db046c5ca74ee7bb47dd6cbc13f80665fdde3e8040bdd3fb015ecb50"}, + {file = "charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f"}, + {file = "charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a"}, ] +[[package]] +name = "chromium" +version = "0.1.0" +description = "Modules Nemesis uses to handle Chromium files" +optional = false +python-versions = ">=3.12,<4.0" +groups = ["main"] +files = [] +develop = true + +[package.dependencies] +common = {path = "../../libs/common", develop = true} +dapr = "1.16.0" +file_linking = {path = "../file_linking", develop = true} +impacket = ">=0.12.0,<0.13.0" +nemesis_dpapi = {path = "../nemesis_dpapi", develop = true} +psycopg = {version = ">=3.0.0,<4.0.0", extras = ["binary"]} +structlog = ">=20.0.0,<30.0.0" + +[package.source] +type = "directory" +url = "../../libs/chromium" + [[package]] name = "click" -version = "8.2.1" +version = "8.3.0" description = "Composable command line interface toolkit" optional = false python-versions = ">=3.10" groups = ["main"] files = [ - {file = "click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b"}, - {file = "click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202"}, + {file = "click-8.3.0-py3-none-any.whl", hash = "sha256:9b9f285302c6e3064f4330c05f05b81945b2a39544279343e6e7c5f27a9baddc"}, + {file = "click-8.3.0.tar.gz", hash = "sha256:e7b8232224eba16f4ebe410c25ced9f7875cb5f3263ffc93cc3e8da705e229c4"}, ] [package.dependencies] @@ -1299,11 +943,12 @@ version = "0.4.6" description = "Cross-platform colored terminal text." optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -groups = ["main"] +groups = ["main", "dev"] files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] +markers = {dev = "sys_platform == \"win32\""} [[package]] name = "colorclass" @@ -1346,9 +991,12 @@ files = [] develop = true [package.dependencies] -dapr = "^1.14.0" +asyncpg = "^0.30.0" +dapr = "1.16.0" +dapr-ext-workflow = "^1.16.0" fastapi = "^0.115.6" minio = "^7.2.14" +psycopg = {version = "^3.2.9", extras = ["pool"]} pydantic = "^2.10.5" structlog = "^25.1.0" @@ -1356,6 +1004,21 @@ structlog = "^25.1.0" type = "directory" url = "../../libs/common" +[[package]] +name = "construct" +version = "2.10.70" +description = "A powerful declarative symmetric parser/builder for binary data" +optional = false +python-versions = ">=3.6" +groups = ["main"] +files = [ + {file = "construct-2.10.70-py3-none-any.whl", hash = "sha256:c80be81ef595a1a821ec69dc16099550ed22197615f4320b57cc9ce2a672cb30"}, + {file = "construct-2.10.70.tar.gz", hash = "sha256:4d2472f9684731e58cc9c56c463be63baa1447d674e0d66aeb5627b22f512c29"}, +] + +[package.extras] +extras = ["arrow", "cloudpickle", "cryptography", "lz4", "numpy", "ruamel.yaml"] + [[package]] name = "cryptography" version = "42.0.8" @@ -1413,14 +1076,14 @@ test-randomorder = ["pytest-randomly"] [[package]] name = "dapr" -version = "1.15.0" +version = "1.16.0" description = "The official release of Dapr Python SDK." optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "dapr-1.15.0-py3-none-any.whl", hash = "sha256:0093bf6df5eb9a14fbab60191a619438e0b6b336f60a7994e184276bcc35d5fb"}, - {file = "dapr-1.15.0.tar.gz", hash = "sha256:6b2373084143f164cb00702758b17a14fc4442314a1f3e2be36ee008d486c47a"}, + {file = "dapr-1.16.0-py3-none-any.whl", hash = "sha256:076dd559a0b450eae24b1c2ae779c9299ed3e06a05c1f72719a6613af8d19ced"}, + {file = "dapr-1.16.0.tar.gz", hash = "sha256:c7e3d005552a598d07608d0d502b2bc432e86678f94b2beccc13a096b9198684"}, ] [package.dependencies] @@ -1433,66 +1096,36 @@ typing-extensions = ">=4.4.0" [[package]] name = "dapr-ext-fastapi" -version = "1.15.0" +version = "1.16.0" description = "The official release of Dapr FastAPI extension." optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "dapr-ext-fastapi-1.15.0.tar.gz", hash = "sha256:d5411d24c6dcc256041b29383caa95d6024e58ab094ad24f7e72b9396bc245b0"}, - {file = "dapr_ext_fastapi-1.15.0-py3-none-any.whl", hash = "sha256:429f15345a4b2fb89586fe691d9c8addba0001246c54cf0fc7e5c7c3a6075c97"}, + {file = "dapr-ext-fastapi-1.16.0.tar.gz", hash = "sha256:10108c3831ae2164c1589c86e6b86fe8ee146650514961841d9ab5eb783f4a76"}, + {file = "dapr_ext_fastapi-1.16.0-py3-none-any.whl", hash = "sha256:9dcc0aaceb361c5132295450a71d4f9ddec09ab5848dbfe2a8b0cf9050fd903e"}, ] [package.dependencies] -dapr = ">=1.15.0" +dapr = ">=1.16.0" fastapi = ">=0.60.1" uvicorn = ">=0.11.6" [[package]] name = "dapr-ext-workflow" -version = "1.15.0" +version = "1.16.0" description = "The official release of Dapr Python SDK Workflow Authoring Extension." optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "dapr-ext-workflow-1.15.0.tar.gz", hash = "sha256:c05508005a7bbd54968a4b626a3e2034e5c841b85e7a67be7f2b80b3d006345b"}, - {file = "dapr_ext_workflow-1.15.0-py3-none-any.whl", hash = "sha256:2c2d2c63a8fed92e01417328a54e140775b170d3994b1f0fb54346bb50ad17dd"}, + {file = "dapr-ext-workflow-1.16.0.tar.gz", hash = "sha256:7487d174394d305e668784f4bac2dcecc757a1e0a8ddf6e5e1cb32c0a887be78"}, + {file = "dapr_ext_workflow-1.16.0-py3-none-any.whl", hash = "sha256:028f6b3a340a5a8f0b061eacdef60de1ce52de2340f9636f517f799f73437ee8"}, ] [package.dependencies] -dapr = ">=1.15.0" -durabletask-dapr = ">=0.2.0a7" - -[[package]] -name = "deprecated" -version = "1.2.18" -description = "Python @deprecated decorator to deprecate old python classes, functions or methods." -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" -groups = ["main"] -files = [ - {file = "Deprecated-1.2.18-py2.py3-none-any.whl", hash = "sha256:bd5011788200372a32418f888e326a09ff80d0214bd961147cfed01b5c018eec"}, - {file = "deprecated-1.2.18.tar.gz", hash = "sha256:422b6f6d859da6f2ef57857761bfb392480502a64c3028ca9bbe86085d72115d"}, -] - -[package.dependencies] -wrapt = ">=1.10,<2" - -[package.extras] -dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "setuptools", "tox"] - -[[package]] -name = "distro" -version = "1.9.0" -description = "Distro - an OS platform information API" -optional = false -python-versions = ">=3.6" -groups = ["main"] -files = [ - {file = "distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2"}, - {file = "distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed"}, -] +dapr = ">=1.16.0" +durabletask-dapr = ">=0.2.0a8" [[package]] name = "dnfile" @@ -1511,39 +1144,58 @@ pefile = ">=2019.4.18" [[package]] name = "dnspython" -version = "2.7.0" +version = "2.8.0" description = "DNS toolkit" optional = false -python-versions = ">=3.9" +python-versions = ">=3.10" groups = ["main"] files = [ - {file = "dnspython-2.7.0-py3-none-any.whl", hash = "sha256:b4c34b7d10b51bcc3a5071e7b8dee77939f1e878477eeecc965e9835f63c6c86"}, - {file = "dnspython-2.7.0.tar.gz", hash = "sha256:ce9c432eda0dc91cf618a5cedf1a4e142651196bbcd2c80e89ed5a907e5cfaf1"}, + {file = "dnspython-2.8.0-py3-none-any.whl", hash = "sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af"}, + {file = "dnspython-2.8.0.tar.gz", hash = "sha256:181d3c6996452cb1189c4046c61599b84a5a86e099562ffde77d26984ff26d0f"}, ] [package.extras] -dev = ["black (>=23.1.0)", "coverage (>=7.0)", "flake8 (>=7)", "hypercorn (>=0.16.0)", "mypy (>=1.8)", "pylint (>=3)", "pytest (>=7.4)", "pytest-cov (>=4.1.0)", "quart-trio (>=0.11.0)", "sphinx (>=7.2.0)", "sphinx-rtd-theme (>=2.0.0)", "twine (>=4.0.0)", "wheel (>=0.42.0)"] -dnssec = ["cryptography (>=43)"] -doh = ["h2 (>=4.1.0)", "httpcore (>=1.0.0)", "httpx (>=0.26.0)"] -doq = ["aioquic (>=1.0.0)"] -idna = ["idna (>=3.7)"] -trio = ["trio (>=0.23)"] +dev = ["black (>=25.1.0)", "coverage (>=7.0)", "flake8 (>=7)", "hypercorn (>=0.17.0)", "mypy (>=1.17)", "pylint (>=3)", "pytest (>=8.4)", "pytest-cov (>=6.2.0)", "quart-trio (>=0.12.0)", "sphinx (>=8.2.0)", "sphinx-rtd-theme (>=3.0.0)", "twine (>=6.1.0)", "wheel (>=0.45.0)"] +dnssec = ["cryptography (>=45)"] +doh = ["h2 (>=4.2.0)", "httpcore (>=1.0.0)", "httpx (>=0.28.0)"] +doq = ["aioquic (>=1.2.0)"] +idna = ["idna (>=3.10)"] +trio = ["trio (>=0.30)"] wmi = ["wmi (>=1.5.1)"] +[[package]] +name = "dpapick3" +version = "0.7.1" +description = "A native implementation of DPAPI" +optional = false +python-versions = ">=3.2" +groups = ["main"] +files = [ + {file = "dpapick3-0.7.1-py3-none-any.whl", hash = "sha256:61999f6d4d08231799d3d62e3a48502476fcc0c4d29f12bb8ed064e3352f5da9"}, + {file = "dpapick3-0.7.1.tar.gz", hash = "sha256:3449366800d5bb313dd6d8d9d259d1b94498881ac74ced163749617a431921cb"}, +] + +[package.dependencies] +pyasn1 = "*" +pycryptodome = "*" +python-registry = "*" + [[package]] name = "durabletask-dapr" -version = "0.2.0a7" +version = "0.2.0a8" description = "A Durable Task Client SDK for Python" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "durabletask_dapr-0.2.0a7-py3-none-any.whl", hash = "sha256:a13b410ee8404882984d8e442a27d06570face32ca2d3c60c73b7377013ea1e9"}, - {file = "durabletask_dapr-0.2.0a7.tar.gz", hash = "sha256:72fdb1055dbf47be3c9f24812fb41902e14ed27f19ccbd7af1372c4bad74963f"}, + {file = "durabletask_dapr-0.2.0a8-py3-none-any.whl", hash = "sha256:a5d13378f234d3c44c48e4f9df32b4a688b1a8dbe6f0a29daaa15eacd321c407"}, + {file = "durabletask_dapr-0.2.0a8.tar.gz", hash = "sha256:56d7508ce1b6fada3e344faeb836ef9fcc3460200f8257300782ddcd114194f0"}, ] [package.dependencies] +asyncio = "*" grpcio = "*" +protobuf = "*" [[package]] name = "easygui" @@ -1558,75 +1210,27 @@ files = [ ] [[package]] -name = "elastic-transport" -version = "8.17.1" -description = "Transport classes and utilities shared among Python Elastic client libraries" +name = "enum-compat" +version = "0.0.3" +description = "enum/enum34 compatibility package" optional = false -python-versions = ">=3.8" +python-versions = "*" groups = ["main"] files = [ - {file = "elastic_transport-8.17.1-py3-none-any.whl", hash = "sha256:192718f498f1d10c5e9aa8b9cf32aed405e469a7f0e9d6a8923431dbb2c59fb8"}, - {file = "elastic_transport-8.17.1.tar.gz", hash = "sha256:5edef32ac864dca8e2f0a613ef63491ee8d6b8cfb52881fa7313ba9290cac6d2"}, + {file = "enum-compat-0.0.3.tar.gz", hash = "sha256:3677daabed56a6f724451d585662253d8fb4e5569845aafa8bb0da36b1a8751e"}, + {file = "enum_compat-0.0.3-py3-none-any.whl", hash = "sha256:88091b617c7fc3bbbceae50db5958023c48dc40b50520005aa3bf27f8f7ea157"}, ] -[package.dependencies] -certifi = "*" -urllib3 = ">=1.26.2,<3" - -[package.extras] -develop = ["aiohttp", "furo", "httpx", "opentelemetry-api", "opentelemetry-sdk", "orjson", "pytest", "pytest-asyncio", "pytest-cov", "pytest-httpserver", "pytest-mock", "requests", "respx", "sphinx (>2)", "sphinx-autodoc-typehints", "trustme"] - -[[package]] -name = "elasticsearch" -version = "8.18.1" -description = "Python client for Elasticsearch" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "elasticsearch-8.18.1-py3-none-any.whl", hash = "sha256:1a8c8b5ec3ce5be88f96d2f898375671648e96272978bce0dee3137d9326aabb"}, - {file = "elasticsearch-8.18.1.tar.gz", hash = "sha256:998035f17a8c1fba7ae26b183dca797dcf95db86da6a7ecba56d31afc40f07c7"}, -] - -[package.dependencies] -elastic-transport = ">=8.15.1,<9" -python-dateutil = "*" -typing-extensions = "*" - -[package.extras] -async = ["aiohttp (>=3,<4)"] -dev = ["aiohttp", "black", "build", "coverage", "isort", "jinja2", "mapbox-vector-tile", "mypy", "nltk", "nox", "numpy", "orjson", "pandas", "pyarrow", "pyright", "pytest", "pytest-asyncio", "pytest-cov", "pytest-mock", "python-dateutil", "pyyaml (>=5.4)", "requests (>=2,<3)", "sentence-transformers", "simsimd", "tqdm", "twine", "types-python-dateutil", "types-tqdm", "unasync"] -docs = ["sphinx", "sphinx-autodoc-typehints", "sphinx-rtd-theme (>=2.0)"] -orjson = ["orjson (>=3)"] -pyarrow = ["pyarrow (>=1)"] -requests = ["requests (>=2.4.0,!=2.32.2,<3.0.0)"] -vectorstore-mmr = ["numpy (>=1)", "simsimd (>=3)"] - -[[package]] -name = "eval-type-backport" -version = "0.2.2" -description = "Like `typing._eval_type`, but lets older Python versions use newer typing features." -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "eval_type_backport-0.2.2-py3-none-any.whl", hash = "sha256:cb6ad7c393517f476f96d456d0412ea80f0a8cf96f6892834cd9340149111b0a"}, - {file = "eval_type_backport-0.2.2.tar.gz", hash = "sha256:f0576b4cf01ebb5bd358d02314d31846af5e07678387486e2c798af0e7d849c1"}, -] - -[package.extras] -tests = ["pytest"] - [[package]] name = "fastapi" -version = "0.115.12" +version = "0.115.14" description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" optional = false python-versions = ">=3.8" groups = ["main"] files = [ - {file = "fastapi-0.115.12-py3-none-any.whl", hash = "sha256:e94613d6c05e27be7ffebdd6ea5f388112e5e430c8f7d6494a9d1d88d43e814d"}, - {file = "fastapi-0.115.12.tar.gz", hash = "sha256:1e2c2a2646905f9e83d32f04a3f86aff4a286669c6c950ca95b5fd68c2602681"}, + {file = "fastapi-0.115.14-py3-none-any.whl", hash = "sha256:6c0c8bf9420bd58f565e585036d971872472b4f7d3f6c73b698e10cffdefb3ca"}, + {file = "fastapi-0.115.14.tar.gz", hash = "sha256:b1de15cdc1c499a4da47914db35d0e4ef8f1ce62b624e94e0e5824421df99739"}, ] [package.dependencies] @@ -1650,10 +1254,14 @@ develop = true [package.dependencies] asyncpg = "^0.30.0" +chromium = {path = "../chromium", develop = true} common = {path = "../common", develop = true} +dapr = "1.16.0" dnfile = "^0.15.1" +file_linking = {path = "../file_linking", develop = true} impacket = "^0.12.0" lnkparse3 = "^1.5.0" +nemesis_dpapi = {path = "../nemesis_dpapi", develop = true} plyara = "^2.2.7" psycopg = "^3.2.4" pydantic = "^2.10.5" @@ -1665,32 +1273,38 @@ type = "directory" url = "../../libs/file_enrichment_modules" [[package]] -name = "filelock" -version = "3.18.0" -description = "A platform independent file lock." +name = "file-linking" +version = "0.1.0" +description = "Modules Nemesis uses to handle file links and listings" optional = false -python-versions = ">=3.9" +python-versions = ">=3.12,<4.0" groups = ["main"] -files = [ - {file = "filelock-3.18.0-py3-none-any.whl", hash = "sha256:c401f4f8377c4464e6db25fff06205fd89bdd83b65eb0488ed1b160f780e21de"}, - {file = "filelock-3.18.0.tar.gz", hash = "sha256:adbc88eabb99d2fec8c9c1b229b171f18afa655400173ddc653d5d01501fb9f2"}, -] +files = [] +develop = true -[package.extras] -docs = ["furo (>=2024.8.6)", "sphinx (>=8.1.3)", "sphinx-autodoc-typehints (>=3)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.6.10)", "diff-cover (>=9.2.1)", "pytest (>=8.3.4)", "pytest-asyncio (>=0.25.2)", "pytest-cov (>=6)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.28.1)"] -typing = ["typing-extensions (>=4.12.2)"] +[package.dependencies] +asyncpg = "^0.30.0" +common = {path = "../common", develop = true} +dapr = "1.16.0" +psycopg = {version = ">=3.0.0,<4.0.0", extras = ["binary"]} +pytest-asyncio = "^1.2.0" +pyyaml = "^6.0.3" +structlog = ">=20.0.0,<30.0.0" + +[package.source] +type = "directory" +url = "../../libs/file_linking" [[package]] name = "flask" -version = "3.1.1" +version = "3.1.2" description = "A simple framework for building complex web applications." optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "flask-3.1.1-py3-none-any.whl", hash = "sha256:07aae2bb5eaf77993ef57e357491839f5fd9f4dc281593a81a9e4d79a24f295c"}, - {file = "flask-3.1.1.tar.gz", hash = "sha256:284c7b8f2f58cb737f0cf1c30fd7eaf0ccfcde196099d24ecede3fc2005aa59e"}, + {file = "flask-3.1.2-py3-none-any.whl", hash = "sha256:ca1d8112ec8a6158cc29ea4858963350011b5c846a414cdb7a954aa9e967d03c"}, + {file = "flask-3.1.2.tar.gz", hash = "sha256:bf656c15c80190ed628ad08cdfd3aaa35beb087855e2f494910aa3774cc4fd87"}, ] [package.dependencies] @@ -1707,158 +1321,144 @@ dotenv = ["python-dotenv"] [[package]] name = "frozenlist" -version = "1.7.0" +version = "1.8.0" description = "A list-like structure which implements collections.abc.MutableSequence" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "frozenlist-1.7.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cc4df77d638aa2ed703b878dd093725b72a824c3c546c076e8fdf276f78ee84a"}, - {file = "frozenlist-1.7.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:716a9973a2cc963160394f701964fe25012600f3d311f60c790400b00e568b61"}, - {file = "frozenlist-1.7.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a0fd1bad056a3600047fb9462cff4c5322cebc59ebf5d0a3725e0ee78955001d"}, - {file = "frozenlist-1.7.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3789ebc19cb811163e70fe2bd354cea097254ce6e707ae42e56f45e31e96cb8e"}, - {file = "frozenlist-1.7.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:af369aa35ee34f132fcfad5be45fbfcde0e3a5f6a1ec0712857f286b7d20cca9"}, - {file = "frozenlist-1.7.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac64b6478722eeb7a3313d494f8342ef3478dff539d17002f849101b212ef97c"}, - {file = "frozenlist-1.7.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f89f65d85774f1797239693cef07ad4c97fdd0639544bad9ac4b869782eb1981"}, - {file = "frozenlist-1.7.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1073557c941395fdfcfac13eb2456cb8aad89f9de27bae29fabca8e563b12615"}, - {file = "frozenlist-1.7.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ed8d2fa095aae4bdc7fdd80351009a48d286635edffee66bf865e37a9125c50"}, - {file = "frozenlist-1.7.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:24c34bea555fe42d9f928ba0a740c553088500377448febecaa82cc3e88aa1fa"}, - {file = "frozenlist-1.7.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:69cac419ac6a6baad202c85aaf467b65ac860ac2e7f2ac1686dc40dbb52f6577"}, - {file = "frozenlist-1.7.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:960d67d0611f4c87da7e2ae2eacf7ea81a5be967861e0c63cf205215afbfac59"}, - {file = "frozenlist-1.7.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:41be2964bd4b15bf575e5daee5a5ce7ed3115320fb3c2b71fca05582ffa4dc9e"}, - {file = "frozenlist-1.7.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:46d84d49e00c9429238a7ce02dc0be8f6d7cd0cd405abd1bebdc991bf27c15bd"}, - {file = "frozenlist-1.7.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:15900082e886edb37480335d9d518cec978afc69ccbc30bd18610b7c1b22a718"}, - {file = "frozenlist-1.7.0-cp310-cp310-win32.whl", hash = "sha256:400ddd24ab4e55014bba442d917203c73b2846391dd42ca5e38ff52bb18c3c5e"}, - {file = "frozenlist-1.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:6eb93efb8101ef39d32d50bce242c84bcbddb4f7e9febfa7b524532a239b4464"}, - {file = "frozenlist-1.7.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:aa51e147a66b2d74de1e6e2cf5921890de6b0f4820b257465101d7f37b49fb5a"}, - {file = "frozenlist-1.7.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9b35db7ce1cd71d36ba24f80f0c9e7cff73a28d7a74e91fe83e23d27c7828750"}, - {file = "frozenlist-1.7.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:34a69a85e34ff37791e94542065c8416c1afbf820b68f720452f636d5fb990cd"}, - {file = "frozenlist-1.7.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a646531fa8d82c87fe4bb2e596f23173caec9185bfbca5d583b4ccfb95183e2"}, - {file = "frozenlist-1.7.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:79b2ffbba483f4ed36a0f236ccb85fbb16e670c9238313709638167670ba235f"}, - {file = "frozenlist-1.7.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a26f205c9ca5829cbf82bb2a84b5c36f7184c4316617d7ef1b271a56720d6b30"}, - {file = "frozenlist-1.7.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bcacfad3185a623fa11ea0e0634aac7b691aa925d50a440f39b458e41c561d98"}, - {file = "frozenlist-1.7.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:72c1b0fe8fe451b34f12dce46445ddf14bd2a5bcad7e324987194dc8e3a74c86"}, - {file = "frozenlist-1.7.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61d1a5baeaac6c0798ff6edfaeaa00e0e412d49946c53fae8d4b8e8b3566c4ae"}, - {file = "frozenlist-1.7.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7edf5c043c062462f09b6820de9854bf28cc6cc5b6714b383149745e287181a8"}, - {file = "frozenlist-1.7.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:d50ac7627b3a1bd2dcef6f9da89a772694ec04d9a61b66cf87f7d9446b4a0c31"}, - {file = "frozenlist-1.7.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ce48b2fece5aeb45265bb7a58259f45027db0abff478e3077e12b05b17fb9da7"}, - {file = "frozenlist-1.7.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:fe2365ae915a1fafd982c146754e1de6ab3478def8a59c86e1f7242d794f97d5"}, - {file = "frozenlist-1.7.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:45a6f2fdbd10e074e8814eb98b05292f27bad7d1883afbe009d96abdcf3bc898"}, - {file = "frozenlist-1.7.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:21884e23cffabb157a9dd7e353779077bf5b8f9a58e9b262c6caad2ef5f80a56"}, - {file = "frozenlist-1.7.0-cp311-cp311-win32.whl", hash = "sha256:284d233a8953d7b24f9159b8a3496fc1ddc00f4db99c324bd5fb5f22d8698ea7"}, - {file = "frozenlist-1.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:387cbfdcde2f2353f19c2f66bbb52406d06ed77519ac7ee21be0232147c2592d"}, - {file = "frozenlist-1.7.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:3dbf9952c4bb0e90e98aec1bd992b3318685005702656bc6f67c1a32b76787f2"}, - {file = "frozenlist-1.7.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:1f5906d3359300b8a9bb194239491122e6cf1444c2efb88865426f170c262cdb"}, - {file = "frozenlist-1.7.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3dabd5a8f84573c8d10d8859a50ea2dec01eea372031929871368c09fa103478"}, - {file = "frozenlist-1.7.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa57daa5917f1738064f302bf2626281a1cb01920c32f711fbc7bc36111058a8"}, - {file = "frozenlist-1.7.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c193dda2b6d49f4c4398962810fa7d7c78f032bf45572b3e04dd5249dff27e08"}, - {file = "frozenlist-1.7.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bfe2b675cf0aaa6d61bf8fbffd3c274b3c9b7b1623beb3809df8a81399a4a9c4"}, - {file = "frozenlist-1.7.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8fc5d5cda37f62b262405cf9652cf0856839c4be8ee41be0afe8858f17f4c94b"}, - {file = "frozenlist-1.7.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b0d5ce521d1dd7d620198829b87ea002956e4319002ef0bc8d3e6d045cb4646e"}, - {file = "frozenlist-1.7.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:488d0a7d6a0008ca0db273c542098a0fa9e7dfaa7e57f70acef43f32b3f69dca"}, - {file = "frozenlist-1.7.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:15a7eaba63983d22c54d255b854e8108e7e5f3e89f647fc854bd77a237e767df"}, - {file = "frozenlist-1.7.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:1eaa7e9c6d15df825bf255649e05bd8a74b04a4d2baa1ae46d9c2d00b2ca2cb5"}, - {file = "frozenlist-1.7.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e4389e06714cfa9d47ab87f784a7c5be91d3934cd6e9a7b85beef808297cc025"}, - {file = "frozenlist-1.7.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:73bd45e1488c40b63fe5a7df892baf9e2a4d4bb6409a2b3b78ac1c6236178e01"}, - {file = "frozenlist-1.7.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:99886d98e1643269760e5fe0df31e5ae7050788dd288947f7f007209b8c33f08"}, - {file = "frozenlist-1.7.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:290a172aae5a4c278c6da8a96222e6337744cd9c77313efe33d5670b9f65fc43"}, - {file = "frozenlist-1.7.0-cp312-cp312-win32.whl", hash = "sha256:426c7bc70e07cfebc178bc4c2bf2d861d720c4fff172181eeb4a4c41d4ca2ad3"}, - {file = "frozenlist-1.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:563b72efe5da92e02eb68c59cb37205457c977aa7a449ed1b37e6939e5c47c6a"}, - {file = "frozenlist-1.7.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ee80eeda5e2a4e660651370ebffd1286542b67e268aa1ac8d6dbe973120ef7ee"}, - {file = "frozenlist-1.7.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d1a81c85417b914139e3a9b995d4a1c84559afc839a93cf2cb7f15e6e5f6ed2d"}, - {file = "frozenlist-1.7.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cbb65198a9132ebc334f237d7b0df163e4de83fb4f2bdfe46c1e654bdb0c5d43"}, - {file = "frozenlist-1.7.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dab46c723eeb2c255a64f9dc05b8dd601fde66d6b19cdb82b2e09cc6ff8d8b5d"}, - {file = "frozenlist-1.7.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6aeac207a759d0dedd2e40745575ae32ab30926ff4fa49b1635def65806fddee"}, - {file = "frozenlist-1.7.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bd8c4e58ad14b4fa7802b8be49d47993182fdd4023393899632c88fd8cd994eb"}, - {file = "frozenlist-1.7.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04fb24d104f425da3540ed83cbfc31388a586a7696142004c577fa61c6298c3f"}, - {file = "frozenlist-1.7.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6a5c505156368e4ea6b53b5ac23c92d7edc864537ff911d2fb24c140bb175e60"}, - {file = "frozenlist-1.7.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8bd7eb96a675f18aa5c553eb7ddc24a43c8c18f22e1f9925528128c052cdbe00"}, - {file = "frozenlist-1.7.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:05579bf020096fe05a764f1f84cd104a12f78eaab68842d036772dc6d4870b4b"}, - {file = "frozenlist-1.7.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:376b6222d114e97eeec13d46c486facd41d4f43bab626b7c3f6a8b4e81a5192c"}, - {file = "frozenlist-1.7.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:0aa7e176ebe115379b5b1c95b4096fb1c17cce0847402e227e712c27bdb5a949"}, - {file = "frozenlist-1.7.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3fbba20e662b9c2130dc771e332a99eff5da078b2b2648153a40669a6d0e36ca"}, - {file = "frozenlist-1.7.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:f3f4410a0a601d349dd406b5713fec59b4cee7e71678d5b17edda7f4655a940b"}, - {file = "frozenlist-1.7.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e2cdfaaec6a2f9327bf43c933c0319a7c429058e8537c508964a133dffee412e"}, - {file = "frozenlist-1.7.0-cp313-cp313-win32.whl", hash = "sha256:5fc4df05a6591c7768459caba1b342d9ec23fa16195e744939ba5914596ae3e1"}, - {file = "frozenlist-1.7.0-cp313-cp313-win_amd64.whl", hash = "sha256:52109052b9791a3e6b5d1b65f4b909703984b770694d3eb64fad124c835d7cba"}, - {file = "frozenlist-1.7.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:a6f86e4193bb0e235ef6ce3dde5cbabed887e0b11f516ce8a0f4d3b33078ec2d"}, - {file = "frozenlist-1.7.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:82d664628865abeb32d90ae497fb93df398a69bb3434463d172b80fc25b0dd7d"}, - {file = "frozenlist-1.7.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:912a7e8375a1c9a68325a902f3953191b7b292aa3c3fb0d71a216221deca460b"}, - {file = "frozenlist-1.7.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9537c2777167488d539bc5de2ad262efc44388230e5118868e172dd4a552b146"}, - {file = "frozenlist-1.7.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:f34560fb1b4c3e30ba35fa9a13894ba39e5acfc5f60f57d8accde65f46cc5e74"}, - {file = "frozenlist-1.7.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:acd03d224b0175f5a850edc104ac19040d35419eddad04e7cf2d5986d98427f1"}, - {file = "frozenlist-1.7.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2038310bc582f3d6a09b3816ab01737d60bf7b1ec70f5356b09e84fb7408ab1"}, - {file = "frozenlist-1.7.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b8c05e4c8e5f36e5e088caa1bf78a687528f83c043706640a92cb76cd6999384"}, - {file = "frozenlist-1.7.0-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:765bb588c86e47d0b68f23c1bee323d4b703218037765dcf3f25c838c6fecceb"}, - {file = "frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:32dc2e08c67d86d0969714dd484fd60ff08ff81d1a1e40a77dd34a387e6ebc0c"}, - {file = "frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:c0303e597eb5a5321b4de9c68e9845ac8f290d2ab3f3e2c864437d3c5a30cd65"}, - {file = "frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:a47f2abb4e29b3a8d0b530f7c3598badc6b134562b1a5caee867f7c62fee51e3"}, - {file = "frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:3d688126c242a6fabbd92e02633414d40f50bb6002fa4cf995a1d18051525657"}, - {file = "frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:4e7e9652b3d367c7bd449a727dc79d5043f48b88d0cbfd4f9f1060cf2b414104"}, - {file = "frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:1a85e345b4c43db8b842cab1feb41be5cc0b10a1830e6295b69d7310f99becaf"}, - {file = "frozenlist-1.7.0-cp313-cp313t-win32.whl", hash = "sha256:3a14027124ddb70dfcee5148979998066897e79f89f64b13328595c4bdf77c81"}, - {file = "frozenlist-1.7.0-cp313-cp313t-win_amd64.whl", hash = "sha256:3bf8010d71d4507775f658e9823210b7427be36625b387221642725b515dcf3e"}, - {file = "frozenlist-1.7.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:cea3dbd15aea1341ea2de490574a4a37ca080b2ae24e4b4f4b51b9057b4c3630"}, - {file = "frozenlist-1.7.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7d536ee086b23fecc36c2073c371572374ff50ef4db515e4e503925361c24f71"}, - {file = "frozenlist-1.7.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:dfcebf56f703cb2e346315431699f00db126d158455e513bd14089d992101e44"}, - {file = "frozenlist-1.7.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:974c5336e61d6e7eb1ea5b929cb645e882aadab0095c5a6974a111e6479f8878"}, - {file = "frozenlist-1.7.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c70db4a0ab5ab20878432c40563573229a7ed9241506181bba12f6b7d0dc41cb"}, - {file = "frozenlist-1.7.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1137b78384eebaf70560a36b7b229f752fb64d463d38d1304939984d5cb887b6"}, - {file = "frozenlist-1.7.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e793a9f01b3e8b5c0bc646fb59140ce0efcc580d22a3468d70766091beb81b35"}, - {file = "frozenlist-1.7.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:74739ba8e4e38221d2c5c03d90a7e542cb8ad681915f4ca8f68d04f810ee0a87"}, - {file = "frozenlist-1.7.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e63344c4e929b1a01e29bc184bbb5fd82954869033765bfe8d65d09e336a677"}, - {file = "frozenlist-1.7.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2ea2a7369eb76de2217a842f22087913cdf75f63cf1307b9024ab82dfb525938"}, - {file = "frozenlist-1.7.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:836b42f472a0e006e02499cef9352ce8097f33df43baaba3e0a28a964c26c7d2"}, - {file = "frozenlist-1.7.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e22b9a99741294b2571667c07d9f8cceec07cb92aae5ccda39ea1b6052ed4319"}, - {file = "frozenlist-1.7.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:9a19e85cc503d958abe5218953df722748d87172f71b73cf3c9257a91b999890"}, - {file = "frozenlist-1.7.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:f22dac33bb3ee8fe3e013aa7b91dc12f60d61d05b7fe32191ffa84c3aafe77bd"}, - {file = "frozenlist-1.7.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:9ccec739a99e4ccf664ea0775149f2749b8a6418eb5b8384b4dc0a7d15d304cb"}, - {file = "frozenlist-1.7.0-cp39-cp39-win32.whl", hash = "sha256:b3950f11058310008a87757f3eee16a8e1ca97979833239439586857bc25482e"}, - {file = "frozenlist-1.7.0-cp39-cp39-win_amd64.whl", hash = "sha256:43a82fce6769c70f2f5a06248b614a7d268080a9d20f7457ef10ecee5af82b63"}, - {file = "frozenlist-1.7.0-py3-none-any.whl", hash = "sha256:9a5af342e34f7e97caf8c995864c7a396418ae2859cc6fdf1b1073020d516a7e"}, - {file = "frozenlist-1.7.0.tar.gz", hash = "sha256:2e310d81923c2437ea8670467121cc3e9b0f76d3043cc1d2331d56c7fb7a3a8f"}, + {file = "frozenlist-1.8.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b37f6d31b3dcea7deb5e9696e529a6aa4a898adc33db82da12e4c60a7c4d2011"}, + {file = "frozenlist-1.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ef2b7b394f208233e471abc541cc6991f907ffd47dc72584acee3147899d6565"}, + {file = "frozenlist-1.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a88f062f072d1589b7b46e951698950e7da00442fc1cacbe17e19e025dc327ad"}, + {file = "frozenlist-1.8.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f57fb59d9f385710aa7060e89410aeb5058b99e62f4d16b08b91986b9a2140c2"}, + {file = "frozenlist-1.8.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:799345ab092bee59f01a915620b5d014698547afd011e691a208637312db9186"}, + {file = "frozenlist-1.8.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c23c3ff005322a6e16f71bf8692fcf4d5a304aaafe1e262c98c6d4adc7be863e"}, + {file = "frozenlist-1.8.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8a76ea0f0b9dfa06f254ee06053d93a600865b3274358ca48a352ce4f0798450"}, + {file = "frozenlist-1.8.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c7366fe1418a6133d5aa824ee53d406550110984de7637d65a178010f759c6ef"}, + {file = "frozenlist-1.8.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:13d23a45c4cebade99340c4165bd90eeb4a56c6d8a9d8aa49568cac19a6d0dc4"}, + {file = "frozenlist-1.8.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:e4a3408834f65da56c83528fb52ce7911484f0d1eaf7b761fc66001db1646eff"}, + {file = "frozenlist-1.8.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:42145cd2748ca39f32801dad54aeea10039da6f86e303659db90db1c4b614c8c"}, + {file = "frozenlist-1.8.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:e2de870d16a7a53901e41b64ffdf26f2fbb8917b3e6ebf398098d72c5b20bd7f"}, + {file = "frozenlist-1.8.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:20e63c9493d33ee48536600d1a5c95eefc870cd71e7ab037763d1fbb89cc51e7"}, + {file = "frozenlist-1.8.0-cp310-cp310-win32.whl", hash = "sha256:adbeebaebae3526afc3c96fad434367cafbfd1b25d72369a9e5858453b1bb71a"}, + {file = "frozenlist-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:667c3777ca571e5dbeb76f331562ff98b957431df140b54c85fd4d52eea8d8f6"}, + {file = "frozenlist-1.8.0-cp310-cp310-win_arm64.whl", hash = "sha256:80f85f0a7cc86e7a54c46d99c9e1318ff01f4687c172ede30fd52d19d1da1c8e"}, + {file = "frozenlist-1.8.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:09474e9831bc2b2199fad6da3c14c7b0fbdd377cce9d3d77131be28906cb7d84"}, + {file = "frozenlist-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:17c883ab0ab67200b5f964d2b9ed6b00971917d5d8a92df149dc2c9779208ee9"}, + {file = "frozenlist-1.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fa47e444b8ba08fffd1c18e8cdb9a75db1b6a27f17507522834ad13ed5922b93"}, + {file = "frozenlist-1.8.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2552f44204b744fba866e573be4c1f9048d6a324dfe14475103fd51613eb1d1f"}, + {file = "frozenlist-1.8.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:957e7c38f250991e48a9a73e6423db1bb9dd14e722a10f6b8bb8e16a0f55f695"}, + {file = "frozenlist-1.8.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8585e3bb2cdea02fc88ffa245069c36555557ad3609e83be0ec71f54fd4abb52"}, + {file = "frozenlist-1.8.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:edee74874ce20a373d62dc28b0b18b93f645633c2943fd90ee9d898550770581"}, + {file = "frozenlist-1.8.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c9a63152fe95756b85f31186bddf42e4c02c6321207fd6601a1c89ebac4fe567"}, + {file = "frozenlist-1.8.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b6db2185db9be0a04fecf2f241c70b63b1a242e2805be291855078f2b404dd6b"}, + {file = "frozenlist-1.8.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:f4be2e3d8bc8aabd566f8d5b8ba7ecc09249d74ba3c9ed52e54dc23a293f0b92"}, + {file = "frozenlist-1.8.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:c8d1634419f39ea6f5c427ea2f90ca85126b54b50837f31497f3bf38266e853d"}, + {file = "frozenlist-1.8.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:1a7fa382a4a223773ed64242dbe1c9c326ec09457e6b8428efb4118c685c3dfd"}, + {file = "frozenlist-1.8.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:11847b53d722050808926e785df837353bd4d75f1d494377e59b23594d834967"}, + {file = "frozenlist-1.8.0-cp311-cp311-win32.whl", hash = "sha256:27c6e8077956cf73eadd514be8fb04d77fc946a7fe9f7fe167648b0b9085cc25"}, + {file = "frozenlist-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:ac913f8403b36a2c8610bbfd25b8013488533e71e62b4b4adce9c86c8cea905b"}, + {file = "frozenlist-1.8.0-cp311-cp311-win_arm64.whl", hash = "sha256:d4d3214a0f8394edfa3e303136d0575eece0745ff2b47bd2cb2e66dd92d4351a"}, + {file = "frozenlist-1.8.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:78f7b9e5d6f2fdb88cdde9440dc147259b62b9d3b019924def9f6478be254ac1"}, + {file = "frozenlist-1.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:229bf37d2e4acdaf808fd3f06e854a4a7a3661e871b10dc1f8f1896a3b05f18b"}, + {file = "frozenlist-1.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f833670942247a14eafbb675458b4e61c82e002a148f49e68257b79296e865c4"}, + {file = "frozenlist-1.8.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:494a5952b1c597ba44e0e78113a7266e656b9794eec897b19ead706bd7074383"}, + {file = "frozenlist-1.8.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96f423a119f4777a4a056b66ce11527366a8bb92f54e541ade21f2374433f6d4"}, + {file = "frozenlist-1.8.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3462dd9475af2025c31cc61be6652dfa25cbfb56cbbf52f4ccfe029f38decaf8"}, + {file = "frozenlist-1.8.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4c800524c9cd9bac5166cd6f55285957fcfc907db323e193f2afcd4d9abd69b"}, + {file = "frozenlist-1.8.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d6a5df73acd3399d893dafc71663ad22534b5aa4f94e8a2fabfe856c3c1b6a52"}, + {file = "frozenlist-1.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:405e8fe955c2280ce66428b3ca55e12b3c4e9c336fb2103a4937e891c69a4a29"}, + {file = "frozenlist-1.8.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:908bd3f6439f2fef9e85031b59fd4f1297af54415fb60e4254a95f75b3cab3f3"}, + {file = "frozenlist-1.8.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:294e487f9ec720bd8ffcebc99d575f7eff3568a08a253d1ee1a0378754b74143"}, + {file = "frozenlist-1.8.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:74c51543498289c0c43656701be6b077f4b265868fa7f8a8859c197006efb608"}, + {file = "frozenlist-1.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:776f352e8329135506a1d6bf16ac3f87bc25b28e765949282dcc627af36123aa"}, + {file = "frozenlist-1.8.0-cp312-cp312-win32.whl", hash = "sha256:433403ae80709741ce34038da08511d4a77062aa924baf411ef73d1146e74faf"}, + {file = "frozenlist-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:34187385b08f866104f0c0617404c8eb08165ab1272e884abc89c112e9c00746"}, + {file = "frozenlist-1.8.0-cp312-cp312-win_arm64.whl", hash = "sha256:fe3c58d2f5db5fbd18c2987cba06d51b0529f52bc3a6cdc33d3f4eab725104bd"}, + {file = "frozenlist-1.8.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8d92f1a84bb12d9e56f818b3a746f3efba93c1b63c8387a73dde655e1e42282a"}, + {file = "frozenlist-1.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:96153e77a591c8adc2ee805756c61f59fef4cf4073a9275ee86fe8cba41241f7"}, + {file = "frozenlist-1.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f21f00a91358803399890ab167098c131ec2ddd5f8f5fd5fe9c9f2c6fcd91e40"}, + {file = "frozenlist-1.8.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fb30f9626572a76dfe4293c7194a09fb1fe93ba94c7d4f720dfae3b646b45027"}, + {file = "frozenlist-1.8.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eaa352d7047a31d87dafcacbabe89df0aa506abb5b1b85a2fb91bc3faa02d822"}, + {file = "frozenlist-1.8.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:03ae967b4e297f58f8c774c7eabcce57fe3c2434817d4385c50661845a058121"}, + {file = "frozenlist-1.8.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f6292f1de555ffcc675941d65fffffb0a5bcd992905015f85d0592201793e0e5"}, + {file = "frozenlist-1.8.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29548f9b5b5e3460ce7378144c3010363d8035cea44bc0bf02d57f5a685e084e"}, + {file = "frozenlist-1.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ec3cc8c5d4084591b4237c0a272cc4f50a5b03396a47d9caaf76f5d7b38a4f11"}, + {file = "frozenlist-1.8.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:517279f58009d0b1f2e7c1b130b377a349405da3f7621ed6bfae50b10adf20c1"}, + {file = "frozenlist-1.8.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:db1e72ede2d0d7ccb213f218df6a078a9c09a7de257c2fe8fcef16d5925230b1"}, + {file = "frozenlist-1.8.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b4dec9482a65c54a5044486847b8a66bf10c9cb4926d42927ec4e8fd5db7fed8"}, + {file = "frozenlist-1.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:21900c48ae04d13d416f0e1e0c4d81f7931f73a9dfa0b7a8746fb2fe7dd970ed"}, + {file = "frozenlist-1.8.0-cp313-cp313-win32.whl", hash = "sha256:8b7b94a067d1c504ee0b16def57ad5738701e4ba10cec90529f13fa03c833496"}, + {file = "frozenlist-1.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:878be833caa6a3821caf85eb39c5ba92d28e85df26d57afb06b35b2efd937231"}, + {file = "frozenlist-1.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:44389d135b3ff43ba8cc89ff7f51f5a0bb6b63d829c8300f79a2fe4fe61bcc62"}, + {file = "frozenlist-1.8.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:e25ac20a2ef37e91c1b39938b591457666a0fa835c7783c3a8f33ea42870db94"}, + {file = "frozenlist-1.8.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:07cdca25a91a4386d2e76ad992916a85038a9b97561bf7a3fd12d5d9ce31870c"}, + {file = "frozenlist-1.8.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4e0c11f2cc6717e0a741f84a527c52616140741cd812a50422f83dc31749fb52"}, + {file = "frozenlist-1.8.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b3210649ee28062ea6099cfda39e147fa1bc039583c8ee4481cb7811e2448c51"}, + {file = "frozenlist-1.8.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:581ef5194c48035a7de2aefc72ac6539823bb71508189e5de01d60c9dcd5fa65"}, + {file = "frozenlist-1.8.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3ef2d026f16a2b1866e1d86fc4e1291e1ed8a387b2c333809419a2f8b3a77b82"}, + {file = "frozenlist-1.8.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5500ef82073f599ac84d888e3a8c1f77ac831183244bfd7f11eaa0289fb30714"}, + {file = "frozenlist-1.8.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:50066c3997d0091c411a66e710f4e11752251e6d2d73d70d8d5d4c76442a199d"}, + {file = "frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:5c1c8e78426e59b3f8005e9b19f6ff46e5845895adbde20ece9218319eca6506"}, + {file = "frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:eefdba20de0d938cec6a89bd4d70f346a03108a19b9df4248d3cf0d88f1b0f51"}, + {file = "frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:cf253e0e1c3ceb4aaff6df637ce033ff6535fb8c70a764a8f46aafd3d6ab798e"}, + {file = "frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:032efa2674356903cd0261c4317a561a6850f3ac864a63fc1583147fb05a79b0"}, + {file = "frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6da155091429aeba16851ecb10a9104a108bcd32f6c1642867eadaee401c1c41"}, + {file = "frozenlist-1.8.0-cp313-cp313t-win32.whl", hash = "sha256:0f96534f8bfebc1a394209427d0f8a63d343c9779cda6fc25e8e121b5fd8555b"}, + {file = "frozenlist-1.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5d63a068f978fc69421fb0e6eb91a9603187527c86b7cd3f534a5b77a592b888"}, + {file = "frozenlist-1.8.0-cp313-cp313t-win_arm64.whl", hash = "sha256:bf0a7e10b077bf5fb9380ad3ae8ce20ef919a6ad93b4552896419ac7e1d8e042"}, + {file = "frozenlist-1.8.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cee686f1f4cadeb2136007ddedd0aaf928ab95216e7691c63e50a8ec066336d0"}, + {file = "frozenlist-1.8.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:119fb2a1bd47307e899c2fac7f28e85b9a543864df47aa7ec9d3c1b4545f096f"}, + {file = "frozenlist-1.8.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4970ece02dbc8c3a92fcc5228e36a3e933a01a999f7094ff7c23fbd2beeaa67c"}, + {file = "frozenlist-1.8.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:cba69cb73723c3f329622e34bdbf5ce1f80c21c290ff04256cff1cd3c2036ed2"}, + {file = "frozenlist-1.8.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:778a11b15673f6f1df23d9586f83c4846c471a8af693a22e066508b77d201ec8"}, + {file = "frozenlist-1.8.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0325024fe97f94c41c08872db482cf8ac4800d80e79222c6b0b7b162d5b13686"}, + {file = "frozenlist-1.8.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:97260ff46b207a82a7567b581ab4190bd4dfa09f4db8a8b49d1a958f6aa4940e"}, + {file = "frozenlist-1.8.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:54b2077180eb7f83dd52c40b2750d0a9f175e06a42e3213ce047219de902717a"}, + {file = "frozenlist-1.8.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2f05983daecab868a31e1da44462873306d3cbfd76d1f0b5b69c473d21dbb128"}, + {file = "frozenlist-1.8.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:33f48f51a446114bc5d251fb2954ab0164d5be02ad3382abcbfe07e2531d650f"}, + {file = "frozenlist-1.8.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:154e55ec0655291b5dd1b8731c637ecdb50975a2ae70c606d100750a540082f7"}, + {file = "frozenlist-1.8.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:4314debad13beb564b708b4a496020e5306c7333fa9a3ab90374169a20ffab30"}, + {file = "frozenlist-1.8.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:073f8bf8becba60aa931eb3bc420b217bb7d5b8f4750e6f8b3be7f3da85d38b7"}, + {file = "frozenlist-1.8.0-cp314-cp314-win32.whl", hash = "sha256:bac9c42ba2ac65ddc115d930c78d24ab8d4f465fd3fc473cdedfccadb9429806"}, + {file = "frozenlist-1.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:3e0761f4d1a44f1d1a47996511752cf3dcec5bbdd9cc2b4fe595caf97754b7a0"}, + {file = "frozenlist-1.8.0-cp314-cp314-win_arm64.whl", hash = "sha256:d1eaff1d00c7751b7c6662e9c5ba6eb2c17a2306ba5e2a37f24ddf3cc953402b"}, + {file = "frozenlist-1.8.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:d3bb933317c52d7ea5004a1c442eef86f426886fba134ef8cf4226ea6ee1821d"}, + {file = "frozenlist-1.8.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:8009897cdef112072f93a0efdce29cd819e717fd2f649ee3016efd3cd885a7ed"}, + {file = "frozenlist-1.8.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2c5dcbbc55383e5883246d11fd179782a9d07a986c40f49abe89ddf865913930"}, + {file = "frozenlist-1.8.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:39ecbc32f1390387d2aa4f5a995e465e9e2f79ba3adcac92d68e3e0afae6657c"}, + {file = "frozenlist-1.8.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92db2bf818d5cc8d9c1f1fc56b897662e24ea5adb36ad1f1d82875bd64e03c24"}, + {file = "frozenlist-1.8.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2dc43a022e555de94c3b68a4ef0b11c4f747d12c024a520c7101709a2144fb37"}, + {file = "frozenlist-1.8.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cb89a7f2de3602cfed448095bab3f178399646ab7c61454315089787df07733a"}, + {file = "frozenlist-1.8.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:33139dc858c580ea50e7e60a1b0ea003efa1fd42e6ec7fdbad78fff65fad2fd2"}, + {file = "frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:168c0969a329b416119507ba30b9ea13688fafffac1b7822802537569a1cb0ef"}, + {file = "frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:28bd570e8e189d7f7b001966435f9dac6718324b5be2990ac496cf1ea9ddb7fe"}, + {file = "frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b2a095d45c5d46e5e79ba1e5b9cb787f541a8dee0433836cea4b96a2c439dcd8"}, + {file = "frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:eab8145831a0d56ec9c4139b6c3e594c7a83c2c8be25d5bcf2d86136a532287a"}, + {file = "frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:974b28cf63cc99dfb2188d8d222bc6843656188164848c4f679e63dae4b0708e"}, + {file = "frozenlist-1.8.0-cp314-cp314t-win32.whl", hash = "sha256:342c97bf697ac5480c0a7ec73cd700ecfa5a8a40ac923bd035484616efecc2df"}, + {file = "frozenlist-1.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:06be8f67f39c8b1dc671f5d83aaefd3358ae5cdcf8314552c57e7ed3e6475bdd"}, + {file = "frozenlist-1.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:102e6314ca4da683dca92e3b1355490fed5f313b768500084fbe6371fddfdb79"}, + {file = "frozenlist-1.8.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:d8b7138e5cd0647e4523d6685b0eac5d4be9a184ae9634492f25c6eb38c12a47"}, + {file = "frozenlist-1.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a6483e309ca809f1efd154b4d37dc6d9f61037d6c6a81c2dc7a15cb22c8c5dca"}, + {file = "frozenlist-1.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1b9290cf81e95e93fdf90548ce9d3c1211cf574b8e3f4b3b7cb0537cf2227068"}, + {file = "frozenlist-1.8.0-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:59a6a5876ca59d1b63af8cd5e7ffffb024c3dc1e9cf9301b21a2e76286505c95"}, + {file = "frozenlist-1.8.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6dc4126390929823e2d2d9dc79ab4046ed74680360fc5f38b585c12c66cdf459"}, + {file = "frozenlist-1.8.0-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:332db6b2563333c5671fecacd085141b5800cb866be16d5e3eb15a2086476675"}, + {file = "frozenlist-1.8.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9ff15928d62a0b80bb875655c39bf517938c7d589554cbd2669be42d97c2cb61"}, + {file = "frozenlist-1.8.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7bf6cdf8e07c8151fba6fe85735441240ec7f619f935a5205953d58009aef8c6"}, + {file = "frozenlist-1.8.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:48e6d3f4ec5c7273dfe83ff27c91083c6c9065af655dc2684d2c200c94308bb5"}, + {file = "frozenlist-1.8.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:1a7607e17ad33361677adcd1443edf6f5da0ce5e5377b798fba20fae194825f3"}, + {file = "frozenlist-1.8.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:5a3a935c3a4e89c733303a2d5a7c257ea44af3a56c8202df486b7f5de40f37e1"}, + {file = "frozenlist-1.8.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:940d4a017dbfed9daf46a3b086e1d2167e7012ee297fef9e1c545c4d022f5178"}, + {file = "frozenlist-1.8.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:b9be22a69a014bc47e78072d0ecae716f5eb56c15238acca0f43d6eb8e4a5bda"}, + {file = "frozenlist-1.8.0-cp39-cp39-win32.whl", hash = "sha256:1aa77cb5697069af47472e39612976ed05343ff2e84a3dcf15437b232cbfd087"}, + {file = "frozenlist-1.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:7398c222d1d405e796970320036b1b563892b65809d9e5261487bb2c7f7b5c6a"}, + {file = "frozenlist-1.8.0-cp39-cp39-win_arm64.whl", hash = "sha256:b4f3b365f31c6cd4af24545ca0a244a53688cad8834e32f56831c4923b50a103"}, + {file = "frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d"}, + {file = "frozenlist-1.8.0.tar.gz", hash = "sha256:3ede829ed8d842f6cd48fc7081d7a41001a56f1f38603f9d49bf3020d59a31ad"}, ] -[[package]] -name = "fsspec" -version = "2025.5.1" -description = "File-system specification" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "fsspec-2025.5.1-py3-none-any.whl", hash = "sha256:24d3a2e663d5fc735ab256263c4075f374a174c3410c0b25e5bd1970bceaa462"}, - {file = "fsspec-2025.5.1.tar.gz", hash = "sha256:2e55e47a540b91843b755e83ded97c6e897fa0942b11490113f09e9c443c2475"}, -] - -[package.extras] -abfs = ["adlfs"] -adl = ["adlfs"] -arrow = ["pyarrow (>=1)"] -dask = ["dask", "distributed"] -dev = ["pre-commit", "ruff"] -doc = ["numpydoc", "sphinx", "sphinx-design", "sphinx-rtd-theme", "yarl"] -dropbox = ["dropbox", "dropboxdrivefs", "requests"] -full = ["adlfs", "aiohttp (!=4.0.0a0,!=4.0.0a1)", "dask", "distributed", "dropbox", "dropboxdrivefs", "fusepy", "gcsfs", "libarchive-c", "ocifs", "panel", "paramiko", "pyarrow (>=1)", "pygit2", "requests", "s3fs", "smbprotocol", "tqdm"] -fuse = ["fusepy"] -gcs = ["gcsfs"] -git = ["pygit2"] -github = ["requests"] -gs = ["gcsfs"] -gui = ["panel"] -hdfs = ["pyarrow (>=1)"] -http = ["aiohttp (!=4.0.0a0,!=4.0.0a1)"] -libarchive = ["libarchive-c"] -oci = ["ocifs"] -s3 = ["s3fs"] -sftp = ["paramiko"] -smb = ["smbprotocol"] -ssh = ["paramiko"] -test = ["aiohttp (!=4.0.0a0,!=4.0.0a1)", "numpy", "pytest", "pytest-asyncio (!=0.22.0)", "pytest-benchmark", "pytest-cov", "pytest-mock", "pytest-recording", "pytest-rerunfailures", "requests"] -test-downstream = ["aiobotocore (>=2.5.4,<3.0.0)", "dask[dataframe,test]", "moto[server] (>4,<5)", "pytest-timeout", "xarray"] -test-full = ["adlfs", "aiohttp (!=4.0.0a0,!=4.0.0a1)", "cloudpickle", "dask", "distributed", "dropbox", "dropboxdrivefs", "fastparquet", "fusepy", "gcsfs", "jinja2", "kerchunk", "libarchive-c", "lz4", "notebook", "numpy", "ocifs", "pandas", "panel", "paramiko", "pyarrow", "pyarrow (>=1)", "pyftpdlib", "pygit2", "pytest", "pytest-asyncio (!=0.22.0)", "pytest-benchmark", "pytest-cov", "pytest-mock", "pytest-recording", "pytest-rerunfailures", "python-snappy", "requests", "smbprotocol", "tqdm", "urllib3", "zarr", "zstandard"] -tqdm = ["tqdm"] - [[package]] name = "googleapis-common-protos" version = "1.70.0" @@ -1879,84 +1479,97 @@ grpc = ["grpcio (>=1.44.0,<2.0.0)"] [[package]] name = "grpcio" -version = "1.73.0" +version = "1.75.1" description = "HTTP/2-based RPC framework" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "grpcio-1.73.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:d050197eeed50f858ef6c51ab09514856f957dba7b1f7812698260fc9cc417f6"}, - {file = "grpcio-1.73.0-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:ebb8d5f4b0200916fb292a964a4d41210de92aba9007e33d8551d85800ea16cb"}, - {file = "grpcio-1.73.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:c0811331b469e3f15dda5f90ab71bcd9681189a83944fd6dc908e2c9249041ef"}, - {file = "grpcio-1.73.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:12787c791c3993d0ea1cc8bf90393647e9a586066b3b322949365d2772ba965b"}, - {file = "grpcio-1.73.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c17771e884fddf152f2a0df12478e8d02853e5b602a10a9a9f1f52fa02b1d32"}, - {file = "grpcio-1.73.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:275e23d4c428c26b51857bbd95fcb8e528783597207ec592571e4372b300a29f"}, - {file = "grpcio-1.73.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:9ffc972b530bf73ef0f948f799482a1bf12d9b6f33406a8e6387c0ca2098a833"}, - {file = "grpcio-1.73.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ebd8d269df64aff092b2cec5e015d8ae09c7e90888b5c35c24fdca719a2c9f35"}, - {file = "grpcio-1.73.0-cp310-cp310-win32.whl", hash = "sha256:072d8154b8f74300ed362c01d54af8b93200c1a9077aeaea79828d48598514f1"}, - {file = "grpcio-1.73.0-cp310-cp310-win_amd64.whl", hash = "sha256:ce953d9d2100e1078a76a9dc2b7338d5415924dc59c69a15bf6e734db8a0f1ca"}, - {file = "grpcio-1.73.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:51036f641f171eebe5fa7aaca5abbd6150f0c338dab3a58f9111354240fe36ec"}, - {file = "grpcio-1.73.0-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:d12bbb88381ea00bdd92c55aff3da3391fd85bc902c41275c8447b86f036ce0f"}, - {file = "grpcio-1.73.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:483c507c2328ed0e01bc1adb13d1eada05cc737ec301d8e5a8f4a90f387f1790"}, - {file = "grpcio-1.73.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c201a34aa960c962d0ce23fe5f423f97e9d4b518ad605eae6d0a82171809caaa"}, - {file = "grpcio-1.73.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:859f70c8e435e8e1fa060e04297c6818ffc81ca9ebd4940e180490958229a45a"}, - {file = "grpcio-1.73.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e2459a27c6886e7e687e4e407778425f3c6a971fa17a16420227bda39574d64b"}, - {file = "grpcio-1.73.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:e0084d4559ee3dbdcce9395e1bc90fdd0262529b32c417a39ecbc18da8074ac7"}, - {file = "grpcio-1.73.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ef5fff73d5f724755693a464d444ee0a448c6cdfd3c1616a9223f736c622617d"}, - {file = "grpcio-1.73.0-cp311-cp311-win32.whl", hash = "sha256:965a16b71a8eeef91fc4df1dc40dc39c344887249174053814f8a8e18449c4c3"}, - {file = "grpcio-1.73.0-cp311-cp311-win_amd64.whl", hash = "sha256:b71a7b4483d1f753bbc11089ff0f6fa63b49c97a9cc20552cded3fcad466d23b"}, - {file = "grpcio-1.73.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:fb9d7c27089d9ba3746f18d2109eb530ef2a37452d2ff50f5a6696cd39167d3b"}, - {file = "grpcio-1.73.0-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:128ba2ebdac41e41554d492b82c34586a90ebd0766f8ebd72160c0e3a57b9155"}, - {file = "grpcio-1.73.0-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:068ecc415f79408d57a7f146f54cdf9f0acb4b301a52a9e563973dc981e82f3d"}, - {file = "grpcio-1.73.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ddc1cfb2240f84d35d559ade18f69dcd4257dbaa5ba0de1a565d903aaab2968"}, - {file = "grpcio-1.73.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e53007f70d9783f53b41b4cf38ed39a8e348011437e4c287eee7dd1d39d54b2f"}, - {file = "grpcio-1.73.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4dd8d8d092efede7d6f48d695ba2592046acd04ccf421436dd7ed52677a9ad29"}, - {file = "grpcio-1.73.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:70176093d0a95b44d24baa9c034bb67bfe2b6b5f7ebc2836f4093c97010e17fd"}, - {file = "grpcio-1.73.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:085ebe876373ca095e24ced95c8f440495ed0b574c491f7f4f714ff794bbcd10"}, - {file = "grpcio-1.73.0-cp312-cp312-win32.whl", hash = "sha256:cfc556c1d6aef02c727ec7d0016827a73bfe67193e47c546f7cadd3ee6bf1a60"}, - {file = "grpcio-1.73.0-cp312-cp312-win_amd64.whl", hash = "sha256:bbf45d59d090bf69f1e4e1594832aaf40aa84b31659af3c5e2c3f6a35202791a"}, - {file = "grpcio-1.73.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:da1d677018ef423202aca6d73a8d3b2cb245699eb7f50eb5f74cae15a8e1f724"}, - {file = "grpcio-1.73.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:36bf93f6a657f37c131d9dd2c391b867abf1426a86727c3575393e9e11dadb0d"}, - {file = "grpcio-1.73.0-cp313-cp313-manylinux_2_17_aarch64.whl", hash = "sha256:d84000367508ade791d90c2bafbd905574b5ced8056397027a77a215d601ba15"}, - {file = "grpcio-1.73.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c98ba1d928a178ce33f3425ff823318040a2b7ef875d30a0073565e5ceb058d9"}, - {file = "grpcio-1.73.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a73c72922dfd30b396a5f25bb3a4590195ee45ecde7ee068acb0892d2900cf07"}, - {file = "grpcio-1.73.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:10e8edc035724aba0346a432060fd192b42bd03675d083c01553cab071a28da5"}, - {file = "grpcio-1.73.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:f5cdc332b503c33b1643b12ea933582c7b081957c8bc2ea4cc4bc58054a09288"}, - {file = "grpcio-1.73.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:07ad7c57233c2109e4ac999cb9c2710c3b8e3f491a73b058b0ce431f31ed8145"}, - {file = "grpcio-1.73.0-cp313-cp313-win32.whl", hash = "sha256:0eb5df4f41ea10bda99a802b2a292d85be28958ede2a50f2beb8c7fc9a738419"}, - {file = "grpcio-1.73.0-cp313-cp313-win_amd64.whl", hash = "sha256:38cf518cc54cd0c47c9539cefa8888549fcc067db0b0c66a46535ca8032020c4"}, - {file = "grpcio-1.73.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:1284850607901cfe1475852d808e5a102133461ec9380bc3fc9ebc0686ee8e32"}, - {file = "grpcio-1.73.0-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:0e092a4b28eefb63eec00d09ef33291cd4c3a0875cde29aec4d11d74434d222c"}, - {file = "grpcio-1.73.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:33577fe7febffe8ebad458744cfee8914e0c10b09f0ff073a6b149a84df8ab8f"}, - {file = "grpcio-1.73.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:60813d8a16420d01fa0da1fc7ebfaaa49a7e5051b0337cd48f4f950eb249a08e"}, - {file = "grpcio-1.73.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2a9c957dc65e5d474378d7bcc557e9184576605d4b4539e8ead6e351d7ccce20"}, - {file = "grpcio-1.73.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:3902b71407d021163ea93c70c8531551f71ae742db15b66826cf8825707d2908"}, - {file = "grpcio-1.73.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:1dd7fa7276dcf061e2d5f9316604499eea06b1b23e34a9380572d74fe59915a8"}, - {file = "grpcio-1.73.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2d1510c4ea473110cb46a010555f2c1a279d1c256edb276e17fa571ba1e8927c"}, - {file = "grpcio-1.73.0-cp39-cp39-win32.whl", hash = "sha256:d0a1517b2005ba1235a1190b98509264bf72e231215dfeef8db9a5a92868789e"}, - {file = "grpcio-1.73.0-cp39-cp39-win_amd64.whl", hash = "sha256:6228f7eb6d9f785f38b589d49957fca5df3d5b5349e77d2d89b14e390165344c"}, - {file = "grpcio-1.73.0.tar.gz", hash = "sha256:3af4c30918a7f0d39de500d11255f8d9da4f30e94a2033e70fe2a720e184bd8e"}, + {file = "grpcio-1.75.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:1712b5890b22547dd29f3215c5788d8fc759ce6dd0b85a6ba6e2731f2d04c088"}, + {file = "grpcio-1.75.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:8d04e101bba4b55cea9954e4aa71c24153ba6182481b487ff376da28d4ba46cf"}, + {file = "grpcio-1.75.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:683cfc70be0c1383449097cba637317e4737a357cfc185d887fd984206380403"}, + {file = "grpcio-1.75.1-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:491444c081a54dcd5e6ada57314321ae526377f498d4aa09d975c3241c5b9e1c"}, + {file = "grpcio-1.75.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ce08d4e112d0d38487c2b631ec8723deac9bc404e9c7b1011426af50a79999e4"}, + {file = "grpcio-1.75.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5a2acda37fc926ccc4547977ac3e56b1df48fe200de968e8c8421f6e3093df6c"}, + {file = "grpcio-1.75.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:745c5fe6bf05df6a04bf2d11552c7d867a2690759e7ab6b05c318a772739bd75"}, + {file = "grpcio-1.75.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:259526a7159d39e2db40d566fe3e8f8e034d0fb2db5bf9c00e09aace655a4c2b"}, + {file = "grpcio-1.75.1-cp310-cp310-win32.whl", hash = "sha256:f4b29b9aabe33fed5df0a85e5f13b09ff25e2c05bd5946d25270a8bd5682dac9"}, + {file = "grpcio-1.75.1-cp310-cp310-win_amd64.whl", hash = "sha256:cf2e760978dcce7ff7d465cbc7e276c3157eedc4c27aa6de7b594c7a295d3d61"}, + {file = "grpcio-1.75.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:573855ca2e58e35032aff30bfbd1ee103fbcf4472e4b28d4010757700918e326"}, + {file = "grpcio-1.75.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:6a4996a2c8accc37976dc142d5991adf60733e223e5c9a2219e157dc6a8fd3a2"}, + {file = "grpcio-1.75.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b1ea1bbe77ecbc1be00af2769f4ae4a88ce93be57a4f3eebd91087898ed749f9"}, + {file = "grpcio-1.75.1-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:e5b425aee54cc5e3e3c58f00731e8a33f5567965d478d516d35ef99fd648ab68"}, + {file = "grpcio-1.75.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0049a7bf547dafaeeb1db17079ce79596c298bfe308fc084d023c8907a845b9a"}, + {file = "grpcio-1.75.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5b8ea230c7f77c0a1a3208a04a1eda164633fb0767b4cefd65a01079b65e5b1f"}, + {file = "grpcio-1.75.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:36990d629c3c9fb41e546414e5af52d0a7af37ce7113d9682c46d7e2919e4cca"}, + {file = "grpcio-1.75.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b10ad908118d38c2453ade7ff790e5bce36580c3742919007a2a78e3a1e521ca"}, + {file = "grpcio-1.75.1-cp311-cp311-win32.whl", hash = "sha256:d6be2b5ee7bea656c954dcf6aa8093c6f0e6a3ef9945c99d99fcbfc88c5c0bfe"}, + {file = "grpcio-1.75.1-cp311-cp311-win_amd64.whl", hash = "sha256:61c692fb05956b17dd6d1ab480f7f10ad0536dba3bc8fd4e3c7263dc244ed772"}, + {file = "grpcio-1.75.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:7b888b33cd14085d86176b1628ad2fcbff94cfbbe7809465097aa0132e58b018"}, + {file = "grpcio-1.75.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:8775036efe4ad2085975531d221535329f5dac99b6c2a854a995456098f99546"}, + {file = "grpcio-1.75.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:bb658f703468d7fbb5dcc4037c65391b7dc34f808ac46ed9136c24fc5eeb041d"}, + {file = "grpcio-1.75.1-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:4b7177a1cdb3c51b02b0c0a256b0a72fdab719600a693e0e9037949efffb200b"}, + {file = "grpcio-1.75.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7d4fa6ccc3ec2e68a04f7b883d354d7fea22a34c44ce535a2f0c0049cf626ddf"}, + {file = "grpcio-1.75.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3d86880ecaeb5b2f0a8afa63824de93adb8ebe4e49d0e51442532f4e08add7d6"}, + {file = "grpcio-1.75.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a8041d2f9e8a742aeae96f4b047ee44e73619f4f9d24565e84d5446c623673b6"}, + {file = "grpcio-1.75.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3652516048bf4c314ce12be37423c79829f46efffb390ad64149a10c6071e8de"}, + {file = "grpcio-1.75.1-cp312-cp312-win32.whl", hash = "sha256:44b62345d8403975513af88da2f3d5cc76f73ca538ba46596f92a127c2aea945"}, + {file = "grpcio-1.75.1-cp312-cp312-win_amd64.whl", hash = "sha256:b1e191c5c465fa777d4cafbaacf0c01e0d5278022082c0abbd2ee1d6454ed94d"}, + {file = "grpcio-1.75.1-cp313-cp313-linux_armv7l.whl", hash = "sha256:3bed22e750d91d53d9e31e0af35a7b0b51367e974e14a4ff229db5b207647884"}, + {file = "grpcio-1.75.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:5b8f381eadcd6ecaa143a21e9e80a26424c76a0a9b3d546febe6648f3a36a5ac"}, + {file = "grpcio-1.75.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5bf4001d3293e3414d0cf99ff9b1139106e57c3a66dfff0c5f60b2a6286ec133"}, + {file = "grpcio-1.75.1-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:9f82ff474103e26351dacfe8d50214e7c9322960d8d07ba7fa1d05ff981c8b2d"}, + {file = "grpcio-1.75.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0ee119f4f88d9f75414217823d21d75bfe0e6ed40135b0cbbfc6376bc9f7757d"}, + {file = "grpcio-1.75.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:664eecc3abe6d916fa6cf8dd6b778e62fb264a70f3430a3180995bf2da935446"}, + {file = "grpcio-1.75.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:c32193fa08b2fbebf08fe08e84f8a0aad32d87c3ad42999c65e9449871b1c66e"}, + {file = "grpcio-1.75.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5cebe13088b9254f6e615bcf1da9131d46cfa4e88039454aca9cb65f639bd3bc"}, + {file = "grpcio-1.75.1-cp313-cp313-win32.whl", hash = "sha256:4b4c678e7ed50f8ae8b8dbad15a865ee73ce12668b6aaf411bf3258b5bc3f970"}, + {file = "grpcio-1.75.1-cp313-cp313-win_amd64.whl", hash = "sha256:5573f51e3f296a1bcf71e7a690c092845fb223072120f4bdb7a5b48e111def66"}, + {file = "grpcio-1.75.1-cp314-cp314-linux_armv7l.whl", hash = "sha256:c05da79068dd96723793bffc8d0e64c45f316248417515f28d22204d9dae51c7"}, + {file = "grpcio-1.75.1-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:06373a94fd16ec287116a825161dca179a0402d0c60674ceeec8c9fba344fe66"}, + {file = "grpcio-1.75.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4484f4b7287bdaa7a5b3980f3c7224c3c622669405d20f69549f5fb956ad0421"}, + {file = "grpcio-1.75.1-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:2720c239c1180eee69f7883c1d4c83fc1a495a2535b5fa322887c70bf02b16e8"}, + {file = "grpcio-1.75.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:07a554fa31c668cf0e7a188678ceeca3cb8fead29bbe455352e712ec33ca701c"}, + {file = "grpcio-1.75.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:3e71a2105210366bfc398eef7f57a664df99194f3520edb88b9c3a7e46ee0d64"}, + {file = "grpcio-1.75.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:8679aa8a5b67976776d3c6b0521e99d1c34db8a312a12bcfd78a7085cb9b604e"}, + {file = "grpcio-1.75.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:aad1c774f4ebf0696a7f148a56d39a3432550612597331792528895258966dc0"}, + {file = "grpcio-1.75.1-cp314-cp314-win32.whl", hash = "sha256:62ce42d9994446b307649cb2a23335fa8e927f7ab2cbf5fcb844d6acb4d85f9c"}, + {file = "grpcio-1.75.1-cp314-cp314-win_amd64.whl", hash = "sha256:f86e92275710bea3000cb79feca1762dc0ad3b27830dd1a74e82ab321d4ee464"}, + {file = "grpcio-1.75.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:c09fba33327c3ac11b5c33dbdd8218eef8990d78f83b1656d628831812a8c0fb"}, + {file = "grpcio-1.75.1-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:7e21400b037be29545704889e72e586c238e346dcb2d08d8a7288d16c883a9ec"}, + {file = "grpcio-1.75.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c12121e509b9f8b0914d10054d24120237d19e870b1cd82acbb8a9b9ddd198a3"}, + {file = "grpcio-1.75.1-cp39-cp39-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:73577a93e692b3474b1bfe84285d098de36705dbd838bb4d6a056d326e4dc880"}, + {file = "grpcio-1.75.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e19e7dfa0d7ca7dea22be464339e18ac608fd75d88c56770c646cdabe54bc724"}, + {file = "grpcio-1.75.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:4e1c28f51c1cf67eccdfc1065e8e866c9ed622f09773ca60947089c117f848a1"}, + {file = "grpcio-1.75.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:030a6164bc2ca726052778c0cf8e3249617a34e368354f9e6107c27ad4af8c28"}, + {file = "grpcio-1.75.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:67697efef5a98d46d5db7b1720fa4043536f8b8e5072a5d61cfca762f287e939"}, + {file = "grpcio-1.75.1-cp39-cp39-win32.whl", hash = "sha256:52015cf73eb5d76f6404e0ce0505a69b51fd1f35810b3a01233b34b10baafb41"}, + {file = "grpcio-1.75.1-cp39-cp39-win_amd64.whl", hash = "sha256:9fe51e4a1f896ea84ac750900eae34d9e9b896b5b1e4a30b02dc31ad29f36383"}, + {file = "grpcio-1.75.1.tar.gz", hash = "sha256:3e81d89ece99b9ace23a6916880baca613c03a799925afb2857887efa8b1b3d2"}, ] +[package.dependencies] +typing-extensions = ">=4.12,<5.0" + [package.extras] -protobuf = ["grpcio-tools (>=1.73.0)"] +protobuf = ["grpcio-tools (>=1.75.1)"] [[package]] name = "grpcio-status" -version = "1.71.0" +version = "1.75.1" description = "Status proto mapping for gRPC" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "grpcio_status-1.71.0-py3-none-any.whl", hash = "sha256:843934ef8c09e3e858952887467f8256aac3910c55f077a359a65b2b3cde3e68"}, - {file = "grpcio_status-1.71.0.tar.gz", hash = "sha256:11405fed67b68f406b3f3c7c5ae5104a79d2d309666d10d61b152e91d28fb968"}, + {file = "grpcio_status-1.75.1-py3-none-any.whl", hash = "sha256:f681b301be26dcf7abf5c765d4a22e4098765e1a65cbdfa3efca384edf8e4e3c"}, + {file = "grpcio_status-1.75.1.tar.gz", hash = "sha256:8162afa21833a2085c91089cc395ad880fac1378a1d60233d976649ed724cbf8"}, ] [package.dependencies] googleapis-common-protos = ">=1.5.5" -grpcio = ">=1.71.0" -protobuf = ">=5.26.1,<6.0dev" +grpcio = ">=1.75.1" +protobuf = ">=6.31.1,<7.0.0" [[package]] name = "h11" @@ -1970,124 +1583,16 @@ files = [ {file = "h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1"}, ] -[[package]] -name = "hf-xet" -version = "1.1.3" -description = "Fast transfer of large files with the Hugging Face Hub." -optional = false -python-versions = ">=3.8" -groups = ["main"] -markers = "platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"arm64\" or platform_machine == \"aarch64\"" -files = [ - {file = "hf_xet-1.1.3-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:c3b508b5f583a75641aebf732853deb058953370ce8184f5dabc49f803b0819b"}, - {file = "hf_xet-1.1.3-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:b788a61977fbe6b5186e66239e2a329a3f0b7e7ff50dad38984c0c74f44aeca1"}, - {file = "hf_xet-1.1.3-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd2da210856444a34aad8ada2fc12f70dabed7cc20f37e90754d1d9b43bc0534"}, - {file = "hf_xet-1.1.3-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:8203f52827e3df65981984936654a5b390566336956f65765a8aa58c362bb841"}, - {file = "hf_xet-1.1.3-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:30c575a5306f8e6fda37edb866762140a435037365eba7a17ce7bd0bc0216a8b"}, - {file = "hf_xet-1.1.3-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:7c1a6aa6abed1f696f8099aa9796ca04c9ee778a58728a115607de9cc4638ff1"}, - {file = "hf_xet-1.1.3-cp37-abi3-win_amd64.whl", hash = "sha256:b578ae5ac9c056296bb0df9d018e597c8dc6390c5266f35b5c44696003cde9f3"}, - {file = "hf_xet-1.1.3.tar.gz", hash = "sha256:a5f09b1dd24e6ff6bcedb4b0ddab2d81824098bb002cf8b4ffa780545fa348c3"}, -] - -[package.extras] -tests = ["pytest"] - -[[package]] -name = "httpcore" -version = "1.0.9" -description = "A minimal low-level HTTP client." -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55"}, - {file = "httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8"}, -] - -[package.dependencies] -certifi = "*" -h11 = ">=0.16" - -[package.extras] -asyncio = ["anyio (>=4.0,<5.0)"] -http2 = ["h2 (>=3,<5)"] -socks = ["socksio (==1.*)"] -trio = ["trio (>=0.22.0,<1.0)"] - -[[package]] -name = "httpx" -version = "0.28.1" -description = "The next generation HTTP client." -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad"}, - {file = "httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc"}, -] - -[package.dependencies] -anyio = "*" -certifi = "*" -httpcore = "==1.*" -idna = "*" - -[package.extras] -brotli = ["brotli", "brotlicffi"] -cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"] -http2 = ["h2 (>=3,<5)"] -socks = ["socksio (==1.*)"] -zstd = ["zstandard (>=0.18.0)"] - -[[package]] -name = "huggingface-hub" -version = "0.33.0" -description = "Client library to download and publish models, datasets and other repos on the huggingface.co hub" -optional = false -python-versions = ">=3.8.0" -groups = ["main"] -files = [ - {file = "huggingface_hub-0.33.0-py3-none-any.whl", hash = "sha256:e8668875b40c68f9929150d99727d39e5ebb8a05a98e4191b908dc7ded9074b3"}, - {file = "huggingface_hub-0.33.0.tar.gz", hash = "sha256:aa31f70d29439d00ff7a33837c03f1f9dd83971ce4e29ad664d63ffb17d3bb97"}, -] - -[package.dependencies] -filelock = "*" -fsspec = ">=2023.5.0" -hf-xet = {version = ">=1.1.2,<2.0.0", markers = "platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"arm64\" or platform_machine == \"aarch64\""} -packaging = ">=20.9" -pyyaml = ">=5.1" -requests = "*" -tqdm = ">=4.42.1" -typing-extensions = ">=3.7.4.3" - -[package.extras] -all = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "authlib (>=1.3.2)", "fastapi", "gradio (>=4.0.0)", "httpx", "itsdangerous", "jedi", "libcst (==1.4.0)", "mypy (==1.15.0)", "mypy (>=1.14.1,<1.15.0)", "numpy", "pytest (>=8.1.1,<8.2.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "ruff (>=0.9.0)", "soundfile", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)", "urllib3 (<2.0)"] -cli = ["InquirerPy (==0.3.4)"] -dev = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "authlib (>=1.3.2)", "fastapi", "gradio (>=4.0.0)", "httpx", "itsdangerous", "jedi", "libcst (==1.4.0)", "mypy (==1.15.0)", "mypy (>=1.14.1,<1.15.0)", "numpy", "pytest (>=8.1.1,<8.2.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "ruff (>=0.9.0)", "soundfile", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)", "urllib3 (<2.0)"] -fastai = ["fastai (>=2.4)", "fastcore (>=1.3.27)", "toml"] -hf-transfer = ["hf-transfer (>=0.1.4)"] -hf-xet = ["hf-xet (>=1.1.2,<2.0.0)"] -inference = ["aiohttp"] -mcp = ["aiohttp", "mcp (>=1.8.0)", "typer"] -oauth = ["authlib (>=1.3.2)", "fastapi", "httpx", "itsdangerous"] -quality = ["libcst (==1.4.0)", "mypy (==1.15.0)", "mypy (>=1.14.1,<1.15.0)", "ruff (>=0.9.0)"] -tensorflow = ["graphviz", "pydot", "tensorflow"] -tensorflow-testing = ["keras (<3.0)", "tensorflow"] -testing = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "authlib (>=1.3.2)", "fastapi", "gradio (>=4.0.0)", "httpx", "itsdangerous", "jedi", "numpy", "pytest (>=8.1.1,<8.2.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "soundfile", "urllib3 (<2.0)"] -torch = ["safetensors[torch]", "torch"] -typing = ["types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)"] - [[package]] name = "idna" -version = "3.10" +version = "3.11" description = "Internationalized Domain Names in Applications (IDNA)" optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" groups = ["main"] files = [ - {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, - {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, + {file = "idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea"}, + {file = "idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902"}, ] [package.extras] @@ -2119,14 +1624,14 @@ six = "*" [[package]] name = "importlib-metadata" -version = "8.5.0" +version = "8.7.0" description = "Read metadata from Python packages" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["main"] files = [ - {file = "importlib_metadata-8.5.0-py3-none-any.whl", hash = "sha256:45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b"}, - {file = "importlib_metadata-8.5.0.tar.gz", hash = "sha256:71522656f0abace1d072b9e5481a48f07c138e00f079c38c8f883823f9c26bd7"}, + {file = "importlib_metadata-8.7.0-py3-none-any.whl", hash = "sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd"}, + {file = "importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000"}, ] [package.dependencies] @@ -2138,7 +1643,7 @@ cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] enabler = ["pytest-enabler (>=2.2)"] perf = ["ipython"] -test = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] +test = ["flufl.flake8", "importlib_resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] type = ["pytest-mypy"] [[package]] @@ -2197,6 +1702,30 @@ check = ["check-manifest", "flake8", "flake8-black", "flake8-deprecated", "flake docs = ["docutils", "sphinx (>=5.0)"] test = ["pytest"] +[[package]] +name = "inflection" +version = "0.5.1" +description = "A port of Ruby on Rails inflector to Python" +optional = false +python-versions = ">=3.5" +groups = ["main"] +files = [ + {file = "inflection-0.5.1-py2.py3-none-any.whl", hash = "sha256:f38b2b640938a4f35ade69ac3d053042959b62a0f1076a5bbaa1b9526605a8a2"}, + {file = "inflection-0.5.1.tar.gz", hash = "sha256:1a29730d366e996aaacffb2f1f1cb9593dc38e2ddd30c91250c6dde09ea9b417"}, +] + +[[package]] +name = "iniconfig" +version = "2.1.0" +description = "brain-dead simple config-ini parsing" +optional = false +python-versions = ">=3.8" +groups = ["main", "dev"] +files = [ + {file = "iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760"}, + {file = "iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"}, +] + [[package]] name = "itsdangerous" version = "2.2.0" @@ -2227,170 +1756,6 @@ MarkupSafe = ">=2.0" [package.extras] i18n = ["Babel (>=2.7)"] -[[package]] -name = "jiter" -version = "0.10.0" -description = "Fast iterable JSON parser." -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "jiter-0.10.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:cd2fb72b02478f06a900a5782de2ef47e0396b3e1f7d5aba30daeb1fce66f303"}, - {file = "jiter-0.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:32bb468e3af278f095d3fa5b90314728a6916d89ba3d0ffb726dd9bf7367285e"}, - {file = "jiter-0.10.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa8b3e0068c26ddedc7abc6fac37da2d0af16b921e288a5a613f4b86f050354f"}, - {file = "jiter-0.10.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:286299b74cc49e25cd42eea19b72aa82c515d2f2ee12d11392c56d8701f52224"}, - {file = "jiter-0.10.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6ed5649ceeaeffc28d87fb012d25a4cd356dcd53eff5acff1f0466b831dda2a7"}, - {file = "jiter-0.10.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2ab0051160cb758a70716448908ef14ad476c3774bd03ddce075f3c1f90a3d6"}, - {file = "jiter-0.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:03997d2f37f6b67d2f5c475da4412be584e1cec273c1cfc03d642c46db43f8cf"}, - {file = "jiter-0.10.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c404a99352d839fed80d6afd6c1d66071f3bacaaa5c4268983fc10f769112e90"}, - {file = "jiter-0.10.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:66e989410b6666d3ddb27a74c7e50d0829704ede652fd4c858e91f8d64b403d0"}, - {file = "jiter-0.10.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b532d3af9ef4f6374609a3bcb5e05a1951d3bf6190dc6b176fdb277c9bbf15ee"}, - {file = "jiter-0.10.0-cp310-cp310-win32.whl", hash = "sha256:da9be20b333970e28b72edc4dff63d4fec3398e05770fb3205f7fb460eb48dd4"}, - {file = "jiter-0.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:f59e533afed0c5b0ac3eba20d2548c4a550336d8282ee69eb07b37ea526ee4e5"}, - {file = "jiter-0.10.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:3bebe0c558e19902c96e99217e0b8e8b17d570906e72ed8a87170bc290b1e978"}, - {file = "jiter-0.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:558cc7e44fd8e507a236bee6a02fa17199ba752874400a0ca6cd6e2196cdb7dc"}, - {file = "jiter-0.10.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d613e4b379a07d7c8453c5712ce7014e86c6ac93d990a0b8e7377e18505e98d"}, - {file = "jiter-0.10.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f62cf8ba0618eda841b9bf61797f21c5ebd15a7a1e19daab76e4e4b498d515b2"}, - {file = "jiter-0.10.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:919d139cdfa8ae8945112398511cb7fca58a77382617d279556b344867a37e61"}, - {file = "jiter-0.10.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:13ddbc6ae311175a3b03bd8994881bc4635c923754932918e18da841632349db"}, - {file = "jiter-0.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c440ea003ad10927a30521a9062ce10b5479592e8a70da27f21eeb457b4a9c5"}, - {file = "jiter-0.10.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:dc347c87944983481e138dea467c0551080c86b9d21de6ea9306efb12ca8f606"}, - {file = "jiter-0.10.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:13252b58c1f4d8c5b63ab103c03d909e8e1e7842d302473f482915d95fefd605"}, - {file = "jiter-0.10.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7d1bbf3c465de4a24ab12fb7766a0003f6f9bce48b8b6a886158c4d569452dc5"}, - {file = "jiter-0.10.0-cp311-cp311-win32.whl", hash = "sha256:db16e4848b7e826edca4ccdd5b145939758dadf0dc06e7007ad0e9cfb5928ae7"}, - {file = "jiter-0.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:9c9c1d5f10e18909e993f9641f12fe1c77b3e9b533ee94ffa970acc14ded3812"}, - {file = "jiter-0.10.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:1e274728e4a5345a6dde2d343c8da018b9d4bd4350f5a472fa91f66fda44911b"}, - {file = "jiter-0.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7202ae396446c988cb2a5feb33a543ab2165b786ac97f53b59aafb803fef0744"}, - {file = "jiter-0.10.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23ba7722d6748b6920ed02a8f1726fb4b33e0fd2f3f621816a8b486c66410ab2"}, - {file = "jiter-0.10.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:371eab43c0a288537d30e1f0b193bc4eca90439fc08a022dd83e5e07500ed026"}, - {file = "jiter-0.10.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6c675736059020365cebc845a820214765162728b51ab1e03a1b7b3abb70f74c"}, - {file = "jiter-0.10.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0c5867d40ab716e4684858e4887489685968a47e3ba222e44cde6e4a2154f959"}, - {file = "jiter-0.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:395bb9a26111b60141757d874d27fdea01b17e8fac958b91c20128ba8f4acc8a"}, - {file = "jiter-0.10.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6842184aed5cdb07e0c7e20e5bdcfafe33515ee1741a6835353bb45fe5d1bd95"}, - {file = "jiter-0.10.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:62755d1bcea9876770d4df713d82606c8c1a3dca88ff39046b85a048566d56ea"}, - {file = "jiter-0.10.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:533efbce2cacec78d5ba73a41756beff8431dfa1694b6346ce7af3a12c42202b"}, - {file = "jiter-0.10.0-cp312-cp312-win32.whl", hash = "sha256:8be921f0cadd245e981b964dfbcd6fd4bc4e254cdc069490416dd7a2632ecc01"}, - {file = "jiter-0.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:a7c7d785ae9dda68c2678532a5a1581347e9c15362ae9f6e68f3fdbfb64f2e49"}, - {file = "jiter-0.10.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:e0588107ec8e11b6f5ef0e0d656fb2803ac6cf94a96b2b9fc675c0e3ab5e8644"}, - {file = "jiter-0.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cafc4628b616dc32530c20ee53d71589816cf385dd9449633e910d596b1f5c8a"}, - {file = "jiter-0.10.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:520ef6d981172693786a49ff5b09eda72a42e539f14788124a07530f785c3ad6"}, - {file = "jiter-0.10.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:554dedfd05937f8fc45d17ebdf298fe7e0c77458232bcb73d9fbbf4c6455f5b3"}, - {file = "jiter-0.10.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5bc299da7789deacf95f64052d97f75c16d4fc8c4c214a22bf8d859a4288a1c2"}, - {file = "jiter-0.10.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5161e201172de298a8a1baad95eb85db4fb90e902353b1f6a41d64ea64644e25"}, - {file = "jiter-0.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e2227db6ba93cb3e2bf67c87e594adde0609f146344e8207e8730364db27041"}, - {file = "jiter-0.10.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:15acb267ea5e2c64515574b06a8bf393fbfee6a50eb1673614aa45f4613c0cca"}, - {file = "jiter-0.10.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:901b92f2e2947dc6dfcb52fd624453862e16665ea909a08398dde19c0731b7f4"}, - {file = "jiter-0.10.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:d0cb9a125d5a3ec971a094a845eadde2db0de85b33c9f13eb94a0c63d463879e"}, - {file = "jiter-0.10.0-cp313-cp313-win32.whl", hash = "sha256:48a403277ad1ee208fb930bdf91745e4d2d6e47253eedc96e2559d1e6527006d"}, - {file = "jiter-0.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:75f9eb72ecb640619c29bf714e78c9c46c9c4eaafd644bf78577ede459f330d4"}, - {file = "jiter-0.10.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:28ed2a4c05a1f32ef0e1d24c2611330219fed727dae01789f4a335617634b1ca"}, - {file = "jiter-0.10.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14a4c418b1ec86a195f1ca69da8b23e8926c752b685af665ce30777233dfe070"}, - {file = "jiter-0.10.0-cp313-cp313t-win_amd64.whl", hash = "sha256:d7bfed2fe1fe0e4dda6ef682cee888ba444b21e7a6553e03252e4feb6cf0adca"}, - {file = "jiter-0.10.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:5e9251a5e83fab8d87799d3e1a46cb4b7f2919b895c6f4483629ed2446f66522"}, - {file = "jiter-0.10.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:023aa0204126fe5b87ccbcd75c8a0d0261b9abdbbf46d55e7ae9f8e22424eeb8"}, - {file = "jiter-0.10.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c189c4f1779c05f75fc17c0c1267594ed918996a231593a21a5ca5438445216"}, - {file = "jiter-0.10.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:15720084d90d1098ca0229352607cd68256c76991f6b374af96f36920eae13c4"}, - {file = "jiter-0.10.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e4f2fb68e5f1cfee30e2b2a09549a00683e0fde4c6a2ab88c94072fc33cb7426"}, - {file = "jiter-0.10.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ce541693355fc6da424c08b7edf39a2895f58d6ea17d92cc2b168d20907dee12"}, - {file = "jiter-0.10.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31c50c40272e189d50006ad5c73883caabb73d4e9748a688b216e85a9a9ca3b9"}, - {file = "jiter-0.10.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fa3402a2ff9815960e0372a47b75c76979d74402448509ccd49a275fa983ef8a"}, - {file = "jiter-0.10.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:1956f934dca32d7bb647ea21d06d93ca40868b505c228556d3373cbd255ce853"}, - {file = "jiter-0.10.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:fcedb049bdfc555e261d6f65a6abe1d5ad68825b7202ccb9692636c70fcced86"}, - {file = "jiter-0.10.0-cp314-cp314-win32.whl", hash = "sha256:ac509f7eccca54b2a29daeb516fb95b6f0bd0d0d8084efaf8ed5dfc7b9f0b357"}, - {file = "jiter-0.10.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5ed975b83a2b8639356151cef5c0d597c68376fc4922b45d0eb384ac058cfa00"}, - {file = "jiter-0.10.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3aa96f2abba33dc77f79b4cf791840230375f9534e5fac927ccceb58c5e604a5"}, - {file = "jiter-0.10.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:bd6292a43c0fc09ce7c154ec0fa646a536b877d1e8f2f96c19707f65355b5a4d"}, - {file = "jiter-0.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:39de429dcaeb6808d75ffe9effefe96a4903c6a4b376b2f6d08d77c1aaee2f18"}, - {file = "jiter-0.10.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52ce124f13a7a616fad3bb723f2bfb537d78239d1f7f219566dc52b6f2a9e48d"}, - {file = "jiter-0.10.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:166f3606f11920f9a1746b2eea84fa2c0a5d50fd313c38bdea4edc072000b0af"}, - {file = "jiter-0.10.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:28dcecbb4ba402916034fc14eba7709f250c4d24b0c43fc94d187ee0580af181"}, - {file = "jiter-0.10.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:86c5aa6910f9bebcc7bc4f8bc461aff68504388b43bfe5e5c0bd21efa33b52f4"}, - {file = "jiter-0.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ceeb52d242b315d7f1f74b441b6a167f78cea801ad7c11c36da77ff2d42e8a28"}, - {file = "jiter-0.10.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ff76d8887c8c8ee1e772274fcf8cc1071c2c58590d13e33bd12d02dc9a560397"}, - {file = "jiter-0.10.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a9be4d0fa2b79f7222a88aa488bd89e2ae0a0a5b189462a12def6ece2faa45f1"}, - {file = "jiter-0.10.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9ab7fd8738094139b6c1ab1822d6f2000ebe41515c537235fd45dabe13ec9324"}, - {file = "jiter-0.10.0-cp39-cp39-win32.whl", hash = "sha256:5f51e048540dd27f204ff4a87f5d79294ea0aa3aa552aca34934588cf27023cf"}, - {file = "jiter-0.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:1b28302349dc65703a9e4ead16f163b1c339efffbe1049c30a44b001a2a4fff9"}, - {file = "jiter-0.10.0.tar.gz", hash = "sha256:07a7142c38aacc85194391108dc91b5b57093c978a9932bd86a36862759d9500"}, -] - -[[package]] -name = "jmespath" -version = "1.0.1" -description = "JSON Matching Expressions" -optional = false -python-versions = ">=3.7" -groups = ["main"] -files = [ - {file = "jmespath-1.0.1-py3-none-any.whl", hash = "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980"}, - {file = "jmespath-1.0.1.tar.gz", hash = "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe"}, -] - -[[package]] -name = "jsonpath-ng" -version = "1.7.0" -description = "A final implementation of JSONPath for Python that aims to be standard compliant, including arithmetic and binary comparison operators and providing clear AST for metaprogramming." -optional = false -python-versions = "*" -groups = ["main"] -files = [ - {file = "jsonpath-ng-1.7.0.tar.gz", hash = "sha256:f6f5f7fd4e5ff79c785f1573b394043b39849fb2bb47bcead935d12b00beab3c"}, - {file = "jsonpath_ng-1.7.0-py2-none-any.whl", hash = "sha256:898c93fc173f0c336784a3fa63d7434297544b7198124a68f9a3ef9597b0ae6e"}, - {file = "jsonpath_ng-1.7.0-py3-none-any.whl", hash = "sha256:f3d7f9e848cba1b6da28c55b1c26ff915dc9e0b1ba7e752a53d6da8d5cbd00b6"}, -] - -[package.dependencies] -ply = "*" - -[[package]] -name = "jsonref" -version = "1.1.0" -description = "jsonref is a library for automatic dereferencing of JSON Reference objects for Python." -optional = false -python-versions = ">=3.7" -groups = ["main"] -files = [ - {file = "jsonref-1.1.0-py3-none-any.whl", hash = "sha256:590dc7773df6c21cbf948b5dac07a72a251db28b0238ceecce0a2abfa8ec30a9"}, - {file = "jsonref-1.1.0.tar.gz", hash = "sha256:32fe8e1d85af0fdefbebce950af85590b22b60f9e95443176adbde4e1ecea552"}, -] - -[[package]] -name = "jsonschema" -version = "4.24.0" -description = "An implementation of JSON Schema validation for Python" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "jsonschema-4.24.0-py3-none-any.whl", hash = "sha256:a462455f19f5faf404a7902952b6f0e3ce868f3ee09a359b05eca6673bd8412d"}, - {file = "jsonschema-4.24.0.tar.gz", hash = "sha256:0b4e8069eb12aedfa881333004bccaec24ecef5a8a6a4b6df142b2cc9599d196"}, -] - -[package.dependencies] -attrs = ">=22.2.0" -jsonschema-specifications = ">=2023.03.6" -referencing = ">=0.28.4" -rpds-py = ">=0.7.1" - -[package.extras] -format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] -format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=24.6.0)"] - -[[package]] -name = "jsonschema-specifications" -version = "2025.4.1" -description = "The JSON Schema meta-schemas and vocabularies, exposed as a Registry" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "jsonschema_specifications-2025.4.1-py3-none-any.whl", hash = "sha256:4653bffbd6584f7de83a67e0d620ef16900b390ddc7939d56684d6c81e33f1af"}, - {file = "jsonschema_specifications-2025.4.1.tar.gz", hash = "sha256:630159c9f4dbea161a6a2205c3011cc4f18ff381b189fff48bb39b9bf26ae608"}, -] - -[package.dependencies] -referencing = ">=0.31.0" - [[package]] name = "ldap3" version = "2.9.1" @@ -2424,102 +1789,87 @@ ldap3 = ">2.5.0,<2.5.2 || >2.5.2,<2.6 || >2.6" [[package]] name = "lief" -version = "0.16.6" +version = "0.16.7" description = "Library to instrument executable formats" optional = false python-versions = ">=3.8" groups = ["main"] files = [ - {file = "lief-0.16.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ddaea8ea7606ce6be937b44788b845a7da6f2ef034fb05d1cf6ef4556942a26d"}, - {file = "lief-0.16.6-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:1884201b56ea7a97deae6b98af990ac30e14927e5e147d455df25a5c3bd60472"}, - {file = "lief-0.16.6-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:4fa34cbac6c2ffd62c7d71a3a94f50df171595ebeea8d07753164f200b971ae0"}, - {file = "lief-0.16.6-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:89b6adf6fbb774bb1ce82ca299a00ff9fe5842696f0417d2ce28ff554c9b577a"}, - {file = "lief-0.16.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:6c2751bdb6d8c0b2dcf0f368b8d675196a7635db6e16aa3ceb7d8ded1bc22ddb"}, - {file = "lief-0.16.6-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:7263f73708b6c49d69f3c7ea42d15d53a5064af524efdb0b2134f2c63f9b77db"}, - {file = "lief-0.16.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:afe2fc86c5b0152baf29d259439117dd4c56bf8ce05c4fd36631d41a0b1e8bc2"}, - {file = "lief-0.16.6-cp310-cp310-win32.whl", hash = "sha256:becabb86bf9ca10d2b272fa84a0ce0526fa93fdd4a8be6b39871cf0ab9cf4bc4"}, - {file = "lief-0.16.6-cp310-cp310-win_amd64.whl", hash = "sha256:c561feeeed8dba457a168d8c283ba44551bd7363a0f12555fd5025aa8b75ac2d"}, - {file = "lief-0.16.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:89093638ee720677e7302850c3c33f42aeb9f173f1c738c918d63d7545886c72"}, - {file = "lief-0.16.6-cp311-cp311-macosx_11_0_x86_64.whl", hash = "sha256:474e80c3eb735d59428cf53e6537528a0a9fd9e177f9dc415f55f87d37785fde"}, - {file = "lief-0.16.6-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:905614f58ed24254ddb1fe1de566cfea01a73e17e5489cf753a7d2afaf3df7ce"}, - {file = "lief-0.16.6-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:5ccbc90ebdda7e417ccac268eb3976bfb0078786fa63a634e57e8c3b3efca179"}, - {file = "lief-0.16.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ce4cd431ec386f23650ed227b6960ff08801fea10aa3eb451a60724c7b4c0015"}, - {file = "lief-0.16.6-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:a597c6f11f668f691bc5bca52c5b9c7511b36b7623d55fac70e0e1bf09a4585b"}, - {file = "lief-0.16.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f659d7c5e2a14d4c86acea1301ec9c88b28fcc63c0444a39bd2b46c8c54760e8"}, - {file = "lief-0.16.6-cp311-cp311-win32.whl", hash = "sha256:65f9768708f208cac67217d640c757cd6627a54df640909121668e5a001b1584"}, - {file = "lief-0.16.6-cp311-cp311-win_amd64.whl", hash = "sha256:ebeba2502fde32ede420deb1641535ed25f10616f293522ad68b57d8e66b4820"}, - {file = "lief-0.16.6-cp311-cp311-win_arm64.whl", hash = "sha256:02c77cfb1b428c4494b3bb8a1614b5fa587d7af928e2acf43d550a09809c8030"}, - {file = "lief-0.16.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c61dab95d7afed02b839ee1718700d4fab4634043c56b4f28d0557d0f7d4849f"}, - {file = "lief-0.16.6-cp312-cp312-macosx_11_0_x86_64.whl", hash = "sha256:38ccfc0e35c1683f8b8d0487ecf1b01c05cd2d0e9d42fced4a767f2065bcf7e0"}, - {file = "lief-0.16.6-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:647f0038a2edd34b956684f2dcaf8b6551757c3158f3bd8fdffe73a491a69c95"}, - {file = "lief-0.16.6-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:ca56c1f8933d5c9fdf6fc98d6f5caf684e5aa369457b30df8c235ff0dc5e7da5"}, - {file = "lief-0.16.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:57b53923cbc57e2eaaed8f8bc8a0490d8fdbbac6f2218905ceb2ff867a864015"}, - {file = "lief-0.16.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:ca9c5a85a26daa4008aec42858fc763f630fb117fa77959912725c48015730e9"}, - {file = "lief-0.16.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:88ab1dc087367d3e49fd7594bce4e381307b510b2aa6a7d8b9e011be1ea29260"}, - {file = "lief-0.16.6-cp312-cp312-win32.whl", hash = "sha256:7cd0921289d756005b1930f95c066c7eee7cdcac97aa4c8172e3ae0f83d80707"}, - {file = "lief-0.16.6-cp312-cp312-win_amd64.whl", hash = "sha256:08bfb33a07c7ad162a4a75524e034ff2faf893e83a648fa39c5a787ea07d761d"}, - {file = "lief-0.16.6-cp312-cp312-win_arm64.whl", hash = "sha256:0a64c08f0fc2b2f05c66111e47130b115db88136b46a3577ab515148a5a31caf"}, - {file = "lief-0.16.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4f280296429164710c8c7293a6db92362f0e9ff8e9fa43da995d93ecbe64ec8d"}, - {file = "lief-0.16.6-cp313-cp313-macosx_11_0_x86_64.whl", hash = "sha256:1c603164f48f53948c6562d5f6e3bf937f481759ec657b07df96370fd8b46db5"}, - {file = "lief-0.16.6-cp313-cp313-manylinux2014_aarch64.whl", hash = "sha256:bcbf9ac2aa831c076252892985f65f682855564759e29e005bdd5720ea60f3da"}, - {file = "lief-0.16.6-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:217c7c70eb444d9e40a66a7445cc5285fdae7f70ccc20fa342bec13857c224b9"}, - {file = "lief-0.16.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:dbb4afede2d641dff4fe1d88ad62d0bcb38c1a27d5c150afcc725d5e894dae6c"}, - {file = "lief-0.16.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2aa18e9a23826b8b02eb58a3cab2a410a3d6a8cd81b6afafc21cd949d025426f"}, - {file = "lief-0.16.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:bdc5acd9a8baab3cb2146bcd0f53080ff0701f684b7269cd0014daad04f50a31"}, - {file = "lief-0.16.6-cp313-cp313-win32.whl", hash = "sha256:e60da9a09f599bbde4abf88723bb1c900ba7ef9f5e6922cb365451c0bed74712"}, - {file = "lief-0.16.6-cp313-cp313-win_amd64.whl", hash = "sha256:31553d7926533b1ac9487b135d1e9e0e5a603477eba5eaf980a70b86065ee981"}, - {file = "lief-0.16.6-cp313-cp313-win_arm64.whl", hash = "sha256:3ab3d11879a8684632700aab39eedcd0a4293d3596d266bf0d93b3a1bf9b51ca"}, - {file = "lief-0.16.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:023444d48af24d7af9744786e6fcee406a60774c91d73cca2ae4cd4bc59138df"}, - {file = "lief-0.16.6-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:c27162e80af1577e8612245554c6031e0c426b5277279931278946bab2e06278"}, - {file = "lief-0.16.6-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:218acabcdb4a4c82ddf5606fae2bb5aad97388db61053429e32c2f16e84c09a5"}, - {file = "lief-0.16.6-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:1ace7b111034f5ecaff5ced71acf0da1a418bade5c9e2fe8387970ecc95e8808"}, - {file = "lief-0.16.6-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:bc66472b447961f7ca588108b52f56b4038b6b609765ef3a12aca36864807aaf"}, - {file = "lief-0.16.6-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:d7346e2e01db6ce631b36e5260b1c2e71cc0132418c4f7b95d5aa7151d74e84a"}, - {file = "lief-0.16.6-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:196f944983b980f564fa553fa1be8517c3a7b731756b7b6415d258aae2a23d32"}, - {file = "lief-0.16.6-cp38-cp38-win32.whl", hash = "sha256:2af02fe2a01d34332c2225f8cadada5570c1ad7e124ab58ab3e3365d1548ae08"}, - {file = "lief-0.16.6-cp38-cp38-win_amd64.whl", hash = "sha256:dd1ce8bd2e129e07ca28ef7637e3daf156654fc9c2383364ad814e6b6c7ee1c1"}, - {file = "lief-0.16.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2b5d2fbc81d268dda83e04c6a1bfc2dc58d1bcc06506de9a27a84d08952a30bb"}, - {file = "lief-0.16.6-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:dddad9f5dd4fe6176e340e440d8cd49bc9bbdbae5c91bf22fe5b52a08efcce9d"}, - {file = "lief-0.16.6-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:a062395b77927d1c011ff18d52277229b3f81c0a2c43650004e79ffc518c23c2"}, - {file = "lief-0.16.6-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:4b0b7cf01dc72563f25fffea739f0b0c50139618c2ae30976acc847c66853c5a"}, - {file = "lief-0.16.6-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:8cbc9e6198fb6a7441063a5ab2afd1eaf04d45c88301384ab55fbd088cf58c94"}, - {file = "lief-0.16.6-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:2bc32c3970a1392fd31cd00a5de9fb8ad786cfab2506e34f010c1eabb30c18e3"}, - {file = "lief-0.16.6-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:942ee7f178b2160d118d25241d1a31119490d0d1ee64fb96e453f8a76997e4cc"}, - {file = "lief-0.16.6-cp39-cp39-win32.whl", hash = "sha256:ae7474af6a2090c807b3e25b07a23724071eb1ed986f92f9c512cfe4c3fd4c5b"}, - {file = "lief-0.16.6-cp39-cp39-win_amd64.whl", hash = "sha256:508fa7ce8415f40e518757aa55bfb4b3492cbb0cc1cd1bb69fce1bc53e9d5bfe"}, + {file = "lief-0.16.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:22b6b8a7fba2c6474900f84bbe581971b66edc295ccbe36c7dc8d3733b224bd9"}, + {file = "lief-0.16.7-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:09859f1ccfe25de456bcb523ca4ed0e23becbf2fedd750653fa843107800ee71"}, + {file = "lief-0.16.7-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:f0165d78450e4930e29cc0d2c27bb863274464302c095a5a73b5b4ab0c173eca"}, + {file = "lief-0.16.7-cp310-cp310-manylinux_2_28_i686.whl", hash = "sha256:7464a7065ac4d0722d36dfad32ba5e9a066c9dcde29ededa2f4693e150019c68"}, + {file = "lief-0.16.7-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:3816f681b68e98a1a198c1ba92a0fb7d53b53760c8643cf10b0c626807d32c7e"}, + {file = "lief-0.16.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:2b89d988f5307d3302f46a620101c067814c665807ba042462722b00062da46e"}, + {file = "lief-0.16.7-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:9dcdd2f17b17cf21dda37d63a6035d1cdb648b2c4cc7b14cc473257c0a9dbaff"}, + {file = "lief-0.16.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8704e46086d6c4effb211b4d58f01a5a1a2574c5d8813037d7480975eed5ad23"}, + {file = "lief-0.16.7-cp310-cp310-win32.whl", hash = "sha256:f41e451aca07d613257a34a5b4f0b5de1a33a3e6098f3e25f99a81f49b905de3"}, + {file = "lief-0.16.7-cp310-cp310-win_amd64.whl", hash = "sha256:2047066ac3722a14b8b6a4d81d581c25096259e95c23458b07f521b3daac4111"}, + {file = "lief-0.16.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f3261a6dc9a1337c572bb3bec7e122d728a760f46a9c405d175962a100b90989"}, + {file = "lief-0.16.7-cp311-cp311-macosx_11_0_x86_64.whl", hash = "sha256:c5e423bacd382ffd265ef2075e749317d7f315f4f0b1ab5dbbfe23335af63ec4"}, + {file = "lief-0.16.7-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:ffe270a072652853da29f0486f658f49eadecc085162bdfff5bb54ba7b875e4b"}, + {file = "lief-0.16.7-cp311-cp311-manylinux_2_28_i686.whl", hash = "sha256:b227e4026f9ac4bbd31020812da4efdc5176cd6c6cd8c27a857c336cd9f8eb64"}, + {file = "lief-0.16.7-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:05865543d48bfced68b85eb939ac776aae7fa00eb1ec13abc0f74663904b110b"}, + {file = "lief-0.16.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2c854991b3c160fb817dd8d4dc1815ff62116177aadd8c35d87f10694dcf8bf7"}, + {file = "lief-0.16.7-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8bc838f87984bb224548f82b78c1f686509b87a32d334b14522da341f86d0139"}, + {file = "lief-0.16.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:2054e3f3bb5e9ae10b7b527a148b41448fdaa9e8eecf60e073e2552447feaa70"}, + {file = "lief-0.16.7-cp311-cp311-win32.whl", hash = "sha256:a93b843cca0b08990e5f7843df6957ab5c99959143322fa6d648267c2bf01b34"}, + {file = "lief-0.16.7-cp311-cp311-win_amd64.whl", hash = "sha256:afcd0d589d463e608add43fb520a2b17e8e82b9565f90856bc89d0e1523a0359"}, + {file = "lief-0.16.7-cp311-cp311-win_arm64.whl", hash = "sha256:c4d13dd846fab09f7dc3ebb830bf3e87f0715a576ca00bfd4dd0cdce49987ead"}, + {file = "lief-0.16.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0d4a210cd4a5f5df3d8a9017c2bdf516d5bcdd2c79f831c82b61fb241e804e91"}, + {file = "lief-0.16.7-cp312-cp312-macosx_11_0_x86_64.whl", hash = "sha256:75078e35324e8028410a75b42632f90c9c179e2af6850f51740bbc9ac87dd305"}, + {file = "lief-0.16.7-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:0424a0ba8c8990b340ea42c84821963d4e7c581fe422a6845999e965728bc94a"}, + {file = "lief-0.16.7-cp312-cp312-manylinux_2_28_i686.whl", hash = "sha256:84ecf5b8ba30c6690c7c423ff6d9e233c4df837a80b467037fcbb088a2df8ef5"}, + {file = "lief-0.16.7-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:642b5aa629206f846760c7688c053cb8b005e5b49bec9f41adb566a04f4e3637"}, + {file = "lief-0.16.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b42cd2cdd5f436f9907af9c1deb99d37c3ab6bd3e9159c4ca78d37b2d693bd4b"}, + {file = "lief-0.16.7-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:98ace12f35cd0e6cc26b346bb85053a0c7ea5923fed103c2d10d405919189b57"}, + {file = "lief-0.16.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d3ad4a12a3f0bef96363a39d6435f259f6f98f6c55da0f97e652acb75bd53fe6"}, + {file = "lief-0.16.7-cp312-cp312-win32.whl", hash = "sha256:296996474d0a37ecaebe550fd4b4f3467dd046343ccaefa598a68d743d190756"}, + {file = "lief-0.16.7-cp312-cp312-win_amd64.whl", hash = "sha256:e857f3bfaaebca402c73ec380357264a207000f5bebd799309d2685ac692422e"}, + {file = "lief-0.16.7-cp312-cp312-win_arm64.whl", hash = "sha256:d10b2c29f184075d723549f42ded8c324906e871f69327226dda5cb89acca745"}, + {file = "lief-0.16.7-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:18fa1203b5ead1fce6d350ffbdf53733c6c5da78f3e64428e63bdba4579208c9"}, + {file = "lief-0.16.7-cp313-cp313-macosx_11_0_x86_64.whl", hash = "sha256:a932d1a2db513aec4e6e432c69386d264cd80e47aacec7e22b328da7c7e3ced3"}, + {file = "lief-0.16.7-cp313-cp313-manylinux2014_aarch64.whl", hash = "sha256:c2d3038c36cb5866c5d2af80084aab5d504b3ff52aabd9cea6ffcae52608181f"}, + {file = "lief-0.16.7-cp313-cp313-manylinux_2_28_i686.whl", hash = "sha256:9de29cd2da97a419cd87fe8b91eb436c2c01a4c12f204f34738d0437987d7af5"}, + {file = "lief-0.16.7-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:0e1ec0601636c45e4cbb92346ba884e21461c16049c9b240c60811ab0d9675dc"}, + {file = "lief-0.16.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:61fa4ccd8fcbfe51983631652e379fdf63b5d15dd411911e40911cb185c046d9"}, + {file = "lief-0.16.7-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:9a223f3f93a96a245b01e08c660a2cc33f68b0d01dfb7c0fd1ffc1bb1626ad7d"}, + {file = "lief-0.16.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e61bde4de30bfac86143834e10c6cd38c5b4d2670f4706ad0f6f56132f61b85d"}, + {file = "lief-0.16.7-cp313-cp313-win32.whl", hash = "sha256:78590ccc155d13c5653dc14b9773a905bcf2affb46e660b8a60c92b587fb9c0d"}, + {file = "lief-0.16.7-cp313-cp313-win_amd64.whl", hash = "sha256:adc0e5e8f2d4e4ead048d52e6886a89f6a4b694b1f4aeff1ad9e5a08c4bb1c2b"}, + {file = "lief-0.16.7-cp313-cp313-win_arm64.whl", hash = "sha256:f9e620187ab7ee45384a271c6773dd12fdc47738cf83e969ef972306b9dab7ab"}, + {file = "lief-0.16.7-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:134c672f62248a18a394d8e1e83ecea3d810188e252e617f8befad0d4daa2c63"}, + {file = "lief-0.16.7-cp314-cp314-macosx_11_0_x86_64.whl", hash = "sha256:fb3f20672887556491e6c0c669b17c813a5c890a9d7e1727695ca7abff63fe62"}, + {file = "lief-0.16.7-cp314-cp314-manylinux2014_aarch64.whl", hash = "sha256:7e337db70e9bd0cd263efd7f9f993eb21181c2bcaf32c509747285963230bce2"}, + {file = "lief-0.16.7-cp314-cp314-manylinux_2_28_i686.whl", hash = "sha256:80a6e15a6da334f8a2d8ca90bfa79ee502e99ec98776140eaef478cac35f2e8b"}, + {file = "lief-0.16.7-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:b84e266c7bc5130aec44d67e1f27ada0da5be29d6a516b5e7279d7664eb31cfe"}, + {file = "lief-0.16.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d01bb63ecac85c221ca889da41f2a76a3d2e6eadf934dd184ffe2e8dcc853ab8"}, + {file = "lief-0.16.7-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:d041fbff28420cda3a419ffb8f80160b4f8cbf3ba60ce494c4ce9085630715e6"}, + {file = "lief-0.16.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:86ff075577e9951b8f928cbe861ec48e66d7d954bc02e89cb2fdfda50e3b940a"}, + {file = "lief-0.16.7-cp314-cp314-win32.whl", hash = "sha256:f8b0df937db6870a58ce8340580cd7c154a54178819fc1cd15a8a1619460ad53"}, + {file = "lief-0.16.7-cp314-cp314-win_amd64.whl", hash = "sha256:c808e73d8ece0dc7055f6e555e627fecd272cb245fb112a8a3e6c91339d88bc7"}, + {file = "lief-0.16.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:10332a99b4c8c68decd77ce1b4f8424ffe628c67ff4e8901991d1abc1f3f5e35"}, + {file = "lief-0.16.7-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:7d3b831f7c7f7cdedfd6a549062b0f0cd2e2f58ed4a0986899343de0b6e64f71"}, + {file = "lief-0.16.7-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:11e8293dd6d878672073e11519ef4c387cb2dea82dd0345de1a356e3c94b6f48"}, + {file = "lief-0.16.7-cp38-cp38-manylinux_2_28_i686.whl", hash = "sha256:85ab9785a76100bfe39780ee53693c898ea148b2becc12858e3a085ae6c9ead4"}, + {file = "lief-0.16.7-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:cc61dd4eb9b4c19526176847c6066e90a220942d40a47c4d62d430e8e6b6a323"}, + {file = "lief-0.16.7-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:bb6a5469fe004a431ed97129cf2ab6d6cf52cd35fcad8d7e7fbdb5f438d81cc8"}, + {file = "lief-0.16.7-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:529127e0081feb61eb09df3ad8881ab0d48d2e9c38ef10a99f482dd9f5851383"}, + {file = "lief-0.16.7-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:baa3dc5ce5f7fb97f0a3e559c6500f738f315b9859de33c58ce410b67a888ba8"}, + {file = "lief-0.16.7-cp38-cp38-win32.whl", hash = "sha256:fce54e60505f63134adfe39c36c8aa9f317ff39757dcb011c394b3c2086a325d"}, + {file = "lief-0.16.7-cp38-cp38-win_amd64.whl", hash = "sha256:9d8892ff6f16fd8ed5bda968b90999f750c3d9216da1bedf29cfe0a8bf2edb44"}, + {file = "lief-0.16.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:39519b755d0b9de6540b9cf4f6b25c89d85ca9ab4320802b2ff48aaa0a4b46b6"}, + {file = "lief-0.16.7-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:73483c7382c9f4c84385b60edcc724fe99aa5b06a253c7ac583c9d335d25407b"}, + {file = "lief-0.16.7-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:bbbe19f4fce0e617971aa6c0ac9545d92c5caedd570c652f5cae3bff55b2928c"}, + {file = "lief-0.16.7-cp39-cp39-manylinux_2_28_i686.whl", hash = "sha256:7e7092b89be7be66bdec64e8bd2bbd47e5d9ad038edd3c072003de1ef1088f3a"}, + {file = "lief-0.16.7-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:03dbe989ce35e1d541f09433671cc35fff6e24583df1c6bdfdfcb8e8ac094812"}, + {file = "lief-0.16.7-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:52c905c23ecd33fd51d88984e718a43b010a6ca8e2fbf87b407d1896d09240ca"}, + {file = "lief-0.16.7-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:2a4559bcde3b753d4c184074895cc143d8271b7247745600c998e08da377d8bb"}, + {file = "lief-0.16.7-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7b0562ac85b39bc6070eaec206dd9c5908843739f6787acbc00509b17945c542"}, + {file = "lief-0.16.7-cp39-cp39-win32.whl", hash = "sha256:fe59fdbb98898126d7bfe25a0d13444c09a9e5f9079ccad02f8efc750188fac7"}, + {file = "lief-0.16.7-cp39-cp39-win_amd64.whl", hash = "sha256:c254d5b5f154f0173113676beee78866c03bb02f96abe77165e48f16bd3bab30"}, ] -[[package]] -name = "litellm" -version = "1.72.4" -description = "Library to easily interface with LLM API providers" -optional = false -python-versions = "!=2.7.*,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,!=3.7.*,>=3.8" -groups = ["main"] -files = [ - {file = "litellm-1.72.4-py3-none-any.whl", hash = "sha256:f98ca994420ed649c466d423655a6e0f2aeecab4564ed372b3378a949e491dc2"}, - {file = "litellm-1.72.4.tar.gz", hash = "sha256:8855de30f78bcb1f37af244519b37a37faaaf579401b1414400b5b5e5b616d57"}, -] - -[package.dependencies] -aiohttp = "*" -click = "*" -httpx = ">=0.23.0" -importlib-metadata = ">=6.8.0" -jinja2 = ">=3.1.2,<4.0.0" -jsonschema = ">=4.22.0,<5.0.0" -openai = ">=1.68.2" -pydantic = ">=2.0.0,<3.0.0" -python-dotenv = ">=0.2.0" -tiktoken = ">=0.7.0" -tokenizers = "*" - -[package.extras] -caching = ["diskcache (>=5.6.1,<6.0.0)"] -extra-proxy = ["azure-identity (>=1.15.0,<2.0.0)", "azure-keyvault-secrets (>=4.8.0,<5.0.0)", "google-cloud-kms (>=2.21.3,<3.0.0)", "prisma (==0.11.0)", "redisvl (>=0.4.1,<0.5.0)", "resend (>=0.8.0,<0.9.0)"] -proxy = ["PyJWT (>=2.8.0,<3.0.0)", "apscheduler (>=3.10.4,<4.0.0)", "backoff", "boto3 (==1.34.34)", "cryptography (>=43.0.1,<44.0.0)", "fastapi (>=0.115.5,<0.116.0)", "fastapi-sso (>=0.16.0,<0.17.0)", "gunicorn (>=23.0.0,<24.0.0)", "litellm-enterprise (==0.1.7)", "litellm-proxy-extras (==0.2.3)", "mcp (==1.5.0)", "orjson (>=3.9.7,<4.0.0)", "pynacl (>=1.5.0,<2.0.0)", "python-multipart (>=0.0.18,<0.0.19)", "pyyaml (>=6.0.1,<7.0.0)", "rich (==13.7.1)", "rq", "uvicorn (>=0.29.0,<0.30.0)", "uvloop (>=0.21.0,<0.22.0)", "websockets (>=13.1.0,<14.0.0)"] -utils = ["numpydoc"] - [[package]] name = "lnkparse3" version = "1.5.2" @@ -2535,106 +1885,103 @@ files = [ [package.dependencies] pyyaml = "*" -[[package]] -name = "logfire-api" -version = "3.19.0" -description = "Shim for the Logfire SDK which does nothing unless Logfire is installed" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "logfire_api-3.19.0-py3-none-any.whl", hash = "sha256:8728403642133c46fe4719eccbae12441c305c86d996384742029b4cd4ceec7b"}, - {file = "logfire_api-3.19.0.tar.gz", hash = "sha256:fe85e56267dd12ae546179b0364fce93779fd33746516c36ea43db9bf3918be3"}, -] - -[[package]] -name = "loguru" -version = "0.7.3" -description = "Python logging made (stupidly) simple" -optional = false -python-versions = "<4.0,>=3.5" -groups = ["main"] -files = [ - {file = "loguru-0.7.3-py3-none-any.whl", hash = "sha256:31a33c10c8e1e10422bfd431aeb5d351c7cf7fa671e3c4df004162264b28220c"}, - {file = "loguru-0.7.3.tar.gz", hash = "sha256:19480589e77d47b8d85b2c827ad95d49bf31b0dcde16593892eb51dd18706eb6"}, -] - -[package.dependencies] -colorama = {version = ">=0.3.4", markers = "sys_platform == \"win32\""} -win32-setctime = {version = ">=1.0.0", markers = "sys_platform == \"win32\""} - -[package.extras] -dev = ["Sphinx (==8.1.3)", "build (==1.2.2)", "colorama (==0.4.5)", "colorama (==0.4.6)", "exceptiongroup (==1.1.3)", "freezegun (==1.1.0)", "freezegun (==1.5.0)", "mypy (==v0.910)", "mypy (==v0.971)", "mypy (==v1.13.0)", "mypy (==v1.4.1)", "myst-parser (==4.0.0)", "pre-commit (==4.0.1)", "pytest (==6.1.2)", "pytest (==8.3.2)", "pytest-cov (==2.12.1)", "pytest-cov (==5.0.0)", "pytest-cov (==6.0.0)", "pytest-mypy-plugins (==1.9.3)", "pytest-mypy-plugins (==3.1.0)", "sphinx-rtd-theme (==3.0.2)", "tox (==3.27.1)", "tox (==4.23.2)", "twine (==6.0.1)"] - [[package]] name = "markupsafe" -version = "3.0.2" +version = "3.0.3" description = "Safely add untrusted strings to HTML/XML markup." optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50"}, - {file = "MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d"}, - {file = "MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30"}, - {file = "MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1"}, - {file = "MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6"}, - {file = "MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-win32.whl", hash = "sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f"}, - {file = "MarkupSafe-3.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a"}, - {file = "markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0"}, + {file = "markupsafe-3.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559"}, + {file = "markupsafe-3.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419"}, + {file = "markupsafe-3.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ba88449deb3de88bd40044603fafffb7bc2b055d626a330323a9ed736661695"}, + {file = "markupsafe-3.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f42d0984e947b8adf7dd6dde396e720934d12c506ce84eea8476409563607591"}, + {file = "markupsafe-3.0.3-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c0c0b3ade1c0b13b936d7970b1d37a57acde9199dc2aecc4c336773e1d86049c"}, + {file = "markupsafe-3.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0303439a41979d9e74d18ff5e2dd8c43ed6c6001fd40e5bf2e43f7bd9bbc523f"}, + {file = "markupsafe-3.0.3-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:d2ee202e79d8ed691ceebae8e0486bd9a2cd4794cec4824e1c99b6f5009502f6"}, + {file = "markupsafe-3.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:177b5253b2834fe3678cb4a5f0059808258584c559193998be2601324fdeafb1"}, + {file = "markupsafe-3.0.3-cp310-cp310-win32.whl", hash = "sha256:2a15a08b17dd94c53a1da0438822d70ebcd13f8c3a95abe3a9ef9f11a94830aa"}, + {file = "markupsafe-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:c4ffb7ebf07cfe8931028e3e4c85f0357459a3f9f9490886198848f4fa002ec8"}, + {file = "markupsafe-3.0.3-cp310-cp310-win_arm64.whl", hash = "sha256:e2103a929dfa2fcaf9bb4e7c091983a49c9ac3b19c9061b6d5427dd7d14d81a1"}, + {file = "markupsafe-3.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1cc7ea17a6824959616c525620e387f6dd30fec8cb44f649e31712db02123dad"}, + {file = "markupsafe-3.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4bd4cd07944443f5a265608cc6aab442e4f74dff8088b0dfc8238647b8f6ae9a"}, + {file = "markupsafe-3.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b5420a1d9450023228968e7e6a9ce57f65d148ab56d2313fcd589eee96a7a50"}, + {file = "markupsafe-3.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0bf2a864d67e76e5c9a34dc26ec616a66b9888e25e7b9460e1c76d3293bd9dbf"}, + {file = "markupsafe-3.0.3-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc51efed119bc9cfdf792cdeaa4d67e8f6fcccab66ed4bfdd6bde3e59bfcbb2f"}, + {file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:068f375c472b3e7acbe2d5318dea141359e6900156b5b2ba06a30b169086b91a"}, + {file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:7be7b61bb172e1ed687f1754f8e7484f1c8019780f6f6b0786e76bb01c2ae115"}, + {file = "markupsafe-3.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f9e130248f4462aaa8e2552d547f36ddadbeaa573879158d721bbd33dfe4743a"}, + {file = "markupsafe-3.0.3-cp311-cp311-win32.whl", hash = "sha256:0db14f5dafddbb6d9208827849fad01f1a2609380add406671a26386cdf15a19"}, + {file = "markupsafe-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:de8a88e63464af587c950061a5e6a67d3632e36df62b986892331d4620a35c01"}, + {file = "markupsafe-3.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:3b562dd9e9ea93f13d53989d23a7e775fdfd1066c33494ff43f5418bc8c58a5c"}, + {file = "markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e"}, + {file = "markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce"}, + {file = "markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d"}, + {file = "markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d"}, + {file = "markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a"}, + {file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b"}, + {file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f"}, + {file = "markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b"}, + {file = "markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d"}, + {file = "markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c"}, + {file = "markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f"}, + {file = "markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795"}, + {file = "markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219"}, + {file = "markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6"}, + {file = "markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676"}, + {file = "markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9"}, + {file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1"}, + {file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc"}, + {file = "markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12"}, + {file = "markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed"}, + {file = "markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5"}, + {file = "markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485"}, + {file = "markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73"}, + {file = "markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37"}, + {file = "markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19"}, + {file = "markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025"}, + {file = "markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6"}, + {file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f"}, + {file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb"}, + {file = "markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009"}, + {file = "markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354"}, + {file = "markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218"}, + {file = "markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287"}, + {file = "markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe"}, + {file = "markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026"}, + {file = "markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737"}, + {file = "markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97"}, + {file = "markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d"}, + {file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda"}, + {file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf"}, + {file = "markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe"}, + {file = "markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9"}, + {file = "markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581"}, + {file = "markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4"}, + {file = "markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab"}, + {file = "markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175"}, + {file = "markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634"}, + {file = "markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50"}, + {file = "markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e"}, + {file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5"}, + {file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523"}, + {file = "markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc"}, + {file = "markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d"}, + {file = "markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9"}, + {file = "markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa"}, + {file = "markupsafe-3.0.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:15d939a21d546304880945ca1ecb8a039db6b4dc49b2c5a400387cdae6a62e26"}, + {file = "markupsafe-3.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f71a396b3bf33ecaa1626c255855702aca4d3d9fea5e051b41ac59a9c1c41edc"}, + {file = "markupsafe-3.0.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f4b68347f8c5eab4a13419215bdfd7f8c9b19f2b25520968adfad23eb0ce60c"}, + {file = "markupsafe-3.0.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e8fc20152abba6b83724d7ff268c249fa196d8259ff481f3b1476383f8f24e42"}, + {file = "markupsafe-3.0.3-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:949b8d66bc381ee8b007cd945914c721d9aba8e27f71959d750a46f7c282b20b"}, + {file = "markupsafe-3.0.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:3537e01efc9d4dccdf77221fb1cb3b8e1a38d5428920e0657ce299b20324d758"}, + {file = "markupsafe-3.0.3-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:591ae9f2a647529ca990bc681daebdd52c8791ff06c2bfa05b65163e28102ef2"}, + {file = "markupsafe-3.0.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a320721ab5a1aba0a233739394eb907f8c8da5c98c9181d1161e77a0c8e36f2d"}, + {file = "markupsafe-3.0.3-cp39-cp39-win32.whl", hash = "sha256:df2449253ef108a379b8b5d6b43f4b1a8e81a061d6537becd5582fba5f9196d7"}, + {file = "markupsafe-3.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:7c3fb7d25180895632e5d3148dbdc29ea38ccb7fd210aa27acbd1201a1902c6e"}, + {file = "markupsafe-3.0.3-cp39-cp39-win_arm64.whl", hash = "sha256:38664109c14ffc9e7437e86b4dceb442b0096dfe3541d7864d9cbe1da4cf36c8"}, + {file = "markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698"}, ] [[package]] @@ -2651,14 +1998,14 @@ files = [ [[package]] name = "minikerberos" -version = "0.4.6" +version = "0.4.7" description = "Kerberos manipulation library in pure Python" optional = false python-versions = ">=3.6" groups = ["main"] files = [ - {file = "minikerberos-0.4.6-py3-none-any.whl", hash = "sha256:9bb7160e0ccbf742746f9777c54311187113ce813d2cc75d470e37602a908a12"}, - {file = "minikerberos-0.4.6.tar.gz", hash = "sha256:56fd389e06197043b7d89eee713e9a5f2bb546020db6a064e1921d740f957290"}, + {file = "minikerberos-0.4.7-py3-none-any.whl", hash = "sha256:f526cbe6d506b577805caacc1ff1bec99dffd0c9f197e519213cea11d9f71641"}, + {file = "minikerberos-0.4.7.tar.gz", hash = "sha256:97303d1afddea9e3b18eafd8c48be932cd98e506228891f2682981687d76cc09"}, ] [package.dependencies] @@ -2671,14 +2018,14 @@ unicrypto = ">=0.0.10" [[package]] name = "minio" -version = "7.2.15" +version = "7.2.18" description = "MinIO Python SDK for Amazon S3 Compatible Cloud Storage" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "minio-7.2.15-py3-none-any.whl", hash = "sha256:c06ef7a43e5d67107067f77b6c07ebdd68733e5aa7eed03076472410ca19d876"}, - {file = "minio-7.2.15.tar.gz", hash = "sha256:5247df5d4dca7bfa4c9b20093acd5ad43e82d8710ceb059d79c6eea970f49f79"}, + {file = "minio-7.2.18-py3-none-any.whl", hash = "sha256:f23a6edbff8d0bc4b5c1a61b2628a01c5a3342aefc613ff9c276012e6321108f"}, + {file = "minio-7.2.18.tar.gz", hash = "sha256:173402a5716099159c5659f9de75be204ebe248557b9f1cc9cf45aa70e9d3024"}, ] [package.dependencies] @@ -2729,116 +2076,158 @@ olefile = ">=0.46" [[package]] name = "multidict" -version = "6.4.4" +version = "6.7.0" description = "multidict implementation" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "multidict-6.4.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8adee3ac041145ffe4488ea73fa0a622b464cc25340d98be76924d0cda8545ff"}, - {file = "multidict-6.4.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b61e98c3e2a861035aaccd207da585bdcacef65fe01d7a0d07478efac005e028"}, - {file = "multidict-6.4.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:75493f28dbadecdbb59130e74fe935288813301a8554dc32f0c631b6bdcdf8b0"}, - {file = "multidict-6.4.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ffc3c6a37e048b5395ee235e4a2a0d639c2349dffa32d9367a42fc20d399772"}, - {file = "multidict-6.4.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:87cb72263946b301570b0f63855569a24ee8758aaae2cd182aae7d95fbc92ca7"}, - {file = "multidict-6.4.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9bbf7bd39822fd07e3609b6b4467af4c404dd2b88ee314837ad1830a7f4a8299"}, - {file = "multidict-6.4.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d1f7cbd4f1f44ddf5fd86a8675b7679176eae770f2fc88115d6dddb6cefb59bc"}, - {file = "multidict-6.4.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb5ac9e5bfce0e6282e7f59ff7b7b9a74aa8e5c60d38186a4637f5aa764046ad"}, - {file = "multidict-6.4.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4efc31dfef8c4eeb95b6b17d799eedad88c4902daba39ce637e23a17ea078915"}, - {file = "multidict-6.4.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:9fcad2945b1b91c29ef2b4050f590bfcb68d8ac8e0995a74e659aa57e8d78e01"}, - {file = "multidict-6.4.4-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:d877447e7368c7320832acb7159557e49b21ea10ffeb135c1077dbbc0816b598"}, - {file = "multidict-6.4.4-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:33a12ebac9f380714c298cbfd3e5b9c0c4e89c75fe612ae496512ee51028915f"}, - {file = "multidict-6.4.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:0f14ea68d29b43a9bf37953881b1e3eb75b2739e896ba4a6aa4ad4c5b9ffa145"}, - {file = "multidict-6.4.4-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:0327ad2c747a6600e4797d115d3c38a220fdb28e54983abe8964fd17e95ae83c"}, - {file = "multidict-6.4.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d1a20707492db9719a05fc62ee215fd2c29b22b47c1b1ba347f9abc831e26683"}, - {file = "multidict-6.4.4-cp310-cp310-win32.whl", hash = "sha256:d83f18315b9fca5db2452d1881ef20f79593c4aa824095b62cb280019ef7aa3d"}, - {file = "multidict-6.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:9c17341ee04545fd962ae07330cb5a39977294c883485c8d74634669b1f7fe04"}, - {file = "multidict-6.4.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4f5f29794ac0e73d2a06ac03fd18870adc0135a9d384f4a306a951188ed02f95"}, - {file = "multidict-6.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c04157266344158ebd57b7120d9b0b35812285d26d0e78193e17ef57bfe2979a"}, - {file = "multidict-6.4.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bb61ffd3ab8310d93427e460f565322c44ef12769f51f77277b4abad7b6f7223"}, - {file = "multidict-6.4.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e0ba18a9afd495f17c351d08ebbc4284e9c9f7971d715f196b79636a4d0de44"}, - {file = "multidict-6.4.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:9faf1b1dcaadf9f900d23a0e6d6c8eadd6a95795a0e57fcca73acce0eb912065"}, - {file = "multidict-6.4.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a4d1cb1327c6082c4fce4e2a438483390964c02213bc6b8d782cf782c9b1471f"}, - {file = "multidict-6.4.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:941f1bec2f5dbd51feeb40aea654c2747f811ab01bdd3422a48a4e4576b7d76a"}, - {file = "multidict-6.4.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e5f8a146184da7ea12910a4cec51ef85e44f6268467fb489c3caf0cd512f29c2"}, - {file = "multidict-6.4.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:232b7237e57ec3c09be97206bfb83a0aa1c5d7d377faa019c68a210fa35831f1"}, - {file = "multidict-6.4.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:55ae0721c1513e5e3210bca4fc98456b980b0c2c016679d3d723119b6b202c42"}, - {file = "multidict-6.4.4-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:51d662c072579f63137919d7bb8fc250655ce79f00c82ecf11cab678f335062e"}, - {file = "multidict-6.4.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:0e05c39962baa0bb19a6b210e9b1422c35c093b651d64246b6c2e1a7e242d9fd"}, - {file = "multidict-6.4.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:d5b1cc3ab8c31d9ebf0faa6e3540fb91257590da330ffe6d2393d4208e638925"}, - {file = "multidict-6.4.4-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:93ec84488a384cd7b8a29c2c7f467137d8a73f6fe38bb810ecf29d1ade011a7c"}, - {file = "multidict-6.4.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b308402608493638763abc95f9dc0030bbd6ac6aff784512e8ac3da73a88af08"}, - {file = "multidict-6.4.4-cp311-cp311-win32.whl", hash = "sha256:343892a27d1a04d6ae455ecece12904d242d299ada01633d94c4f431d68a8c49"}, - {file = "multidict-6.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:73484a94f55359780c0f458bbd3c39cb9cf9c182552177d2136e828269dee529"}, - {file = "multidict-6.4.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:dc388f75a1c00000824bf28b7633e40854f4127ede80512b44c3cfeeea1839a2"}, - {file = "multidict-6.4.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:98af87593a666f739d9dba5d0ae86e01b0e1a9cfcd2e30d2d361fbbbd1a9162d"}, - {file = "multidict-6.4.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:aff4cafea2d120327d55eadd6b7f1136a8e5a0ecf6fb3b6863e8aca32cd8e50a"}, - {file = "multidict-6.4.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:169c4ba7858176b797fe551d6e99040c531c775d2d57b31bcf4de6d7a669847f"}, - {file = "multidict-6.4.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b9eb4c59c54421a32b3273d4239865cb14ead53a606db066d7130ac80cc8ec93"}, - {file = "multidict-6.4.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7cf3bd54c56aa16fdb40028d545eaa8d051402b61533c21e84046e05513d5780"}, - {file = "multidict-6.4.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f682c42003c7264134bfe886376299db4cc0c6cd06a3295b41b347044bcb5482"}, - {file = "multidict-6.4.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a920f9cf2abdf6e493c519492d892c362007f113c94da4c239ae88429835bad1"}, - {file = "multidict-6.4.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:530d86827a2df6504526106b4c104ba19044594f8722d3e87714e847c74a0275"}, - {file = "multidict-6.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ecde56ea2439b96ed8a8d826b50c57364612ddac0438c39e473fafad7ae1c23b"}, - {file = "multidict-6.4.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:dc8c9736d8574b560634775ac0def6bdc1661fc63fa27ffdfc7264c565bcb4f2"}, - {file = "multidict-6.4.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:7f3d3b3c34867579ea47cbd6c1f2ce23fbfd20a273b6f9e3177e256584f1eacc"}, - {file = "multidict-6.4.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:87a728af265e08f96b6318ebe3c0f68b9335131f461efab2fc64cc84a44aa6ed"}, - {file = "multidict-6.4.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9f193eeda1857f8e8d3079a4abd258f42ef4a4bc87388452ed1e1c4d2b0c8740"}, - {file = "multidict-6.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:be06e73c06415199200e9a2324a11252a3d62030319919cde5e6950ffeccf72e"}, - {file = "multidict-6.4.4-cp312-cp312-win32.whl", hash = "sha256:622f26ea6a7e19b7c48dd9228071f571b2fbbd57a8cd71c061e848f281550e6b"}, - {file = "multidict-6.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:5e2bcda30d5009996ff439e02a9f2b5c3d64a20151d34898c000a6281faa3781"}, - {file = "multidict-6.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:82ffabefc8d84c2742ad19c37f02cde5ec2a1ee172d19944d380f920a340e4b9"}, - {file = "multidict-6.4.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:6a2f58a66fe2c22615ad26156354005391e26a2f3721c3621504cd87c1ea87bf"}, - {file = "multidict-6.4.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5883d6ee0fd9d8a48e9174df47540b7545909841ac82354c7ae4cbe9952603bd"}, - {file = "multidict-6.4.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9abcf56a9511653fa1d052bfc55fbe53dbee8f34e68bd6a5a038731b0ca42d15"}, - {file = "multidict-6.4.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6ed5ae5605d4ad5a049fad2a28bb7193400700ce2f4ae484ab702d1e3749c3f9"}, - {file = "multidict-6.4.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bbfcb60396f9bcfa63e017a180c3105b8c123a63e9d1428a36544e7d37ca9e20"}, - {file = "multidict-6.4.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b0f1987787f5f1e2076b59692352ab29a955b09ccc433c1f6b8e8e18666f608b"}, - {file = "multidict-6.4.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1d0121ccce8c812047d8d43d691a1ad7641f72c4f730474878a5aeae1b8ead8c"}, - {file = "multidict-6.4.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:83ec4967114295b8afd120a8eec579920c882831a3e4c3331d591a8e5bfbbc0f"}, - {file = "multidict-6.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:995f985e2e268deaf17867801b859a282e0448633f1310e3704b30616d269d69"}, - {file = "multidict-6.4.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:d832c608f94b9f92a0ec8b7e949be7792a642b6e535fcf32f3e28fab69eeb046"}, - {file = "multidict-6.4.4-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d21c1212171cf7da703c5b0b7a0e85be23b720818aef502ad187d627316d5645"}, - {file = "multidict-6.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:cbebaa076aaecad3d4bb4c008ecc73b09274c952cf6a1b78ccfd689e51f5a5b0"}, - {file = "multidict-6.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:c93a6fb06cc8e5d3628b2b5fda215a5db01e8f08fc15fadd65662d9b857acbe4"}, - {file = "multidict-6.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8cd8f81f1310182362fb0c7898145ea9c9b08a71081c5963b40ee3e3cac589b1"}, - {file = "multidict-6.4.4-cp313-cp313-win32.whl", hash = "sha256:3e9f1cd61a0ab857154205fb0b1f3d3ace88d27ebd1409ab7af5096e409614cd"}, - {file = "multidict-6.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:8ffb40b74400e4455785c2fa37eba434269149ec525fc8329858c862e4b35373"}, - {file = "multidict-6.4.4-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:6a602151dbf177be2450ef38966f4be3467d41a86c6a845070d12e17c858a156"}, - {file = "multidict-6.4.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0d2b9712211b860d123815a80b859075d86a4d54787e247d7fbee9db6832cf1c"}, - {file = "multidict-6.4.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:d2fa86af59f8fc1972e121ade052145f6da22758f6996a197d69bb52f8204e7e"}, - {file = "multidict-6.4.4-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50855d03e9e4d66eab6947ba688ffb714616f985838077bc4b490e769e48da51"}, - {file = "multidict-6.4.4-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:5bce06b83be23225be1905dcdb6b789064fae92499fbc458f59a8c0e68718601"}, - {file = "multidict-6.4.4-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:66ed0731f8e5dfd8369a883b6e564aca085fb9289aacabd9decd70568b9a30de"}, - {file = "multidict-6.4.4-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:329ae97fc2f56f44d91bc47fe0972b1f52d21c4b7a2ac97040da02577e2daca2"}, - {file = "multidict-6.4.4-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c27e5dcf520923d6474d98b96749e6805f7677e93aaaf62656005b8643f907ab"}, - {file = "multidict-6.4.4-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:058cc59b9e9b143cc56715e59e22941a5d868c322242278d28123a5d09cdf6b0"}, - {file = "multidict-6.4.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:69133376bc9a03f8c47343d33f91f74a99c339e8b58cea90433d8e24bb298031"}, - {file = "multidict-6.4.4-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:d6b15c55721b1b115c5ba178c77104123745b1417527ad9641a4c5e2047450f0"}, - {file = "multidict-6.4.4-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:a887b77f51d3d41e6e1a63cf3bc7ddf24de5939d9ff69441387dfefa58ac2e26"}, - {file = "multidict-6.4.4-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:632a3bf8f1787f7ef7d3c2f68a7bde5be2f702906f8b5842ad6da9d974d0aab3"}, - {file = "multidict-6.4.4-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:a145c550900deb7540973c5cdb183b0d24bed6b80bf7bddf33ed8f569082535e"}, - {file = "multidict-6.4.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:cc5d83c6619ca5c9672cb78b39ed8542f1975a803dee2cda114ff73cbb076edd"}, - {file = "multidict-6.4.4-cp313-cp313t-win32.whl", hash = "sha256:3312f63261b9df49be9d57aaa6abf53a6ad96d93b24f9cc16cf979956355ce6e"}, - {file = "multidict-6.4.4-cp313-cp313t-win_amd64.whl", hash = "sha256:ba852168d814b2c73333073e1c7116d9395bea69575a01b0b3c89d2d5a87c8fb"}, - {file = "multidict-6.4.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:603f39bd1cf85705c6c1ba59644b480dfe495e6ee2b877908de93322705ad7cf"}, - {file = "multidict-6.4.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fc60f91c02e11dfbe3ff4e1219c085695c339af72d1641800fe6075b91850c8f"}, - {file = "multidict-6.4.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:496bcf01c76a70a31c3d746fd39383aad8d685ce6331e4c709e9af4ced5fa221"}, - {file = "multidict-6.4.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4219390fb5bf8e548e77b428bb36a21d9382960db5321b74d9d9987148074d6b"}, - {file = "multidict-6.4.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3ef4e9096ff86dfdcbd4a78253090ba13b1d183daa11b973e842465d94ae1772"}, - {file = "multidict-6.4.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:49a29d7133b1fc214e818bbe025a77cc6025ed9a4f407d2850373ddde07fd04a"}, - {file = "multidict-6.4.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e32053d6d3a8b0dfe49fde05b496731a0e6099a4df92154641c00aa76786aef5"}, - {file = "multidict-6.4.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cc403092a49509e8ef2d2fd636a8ecefc4698cc57bbe894606b14579bc2a955"}, - {file = "multidict-6.4.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5363f9b2a7f3910e5c87d8b1855c478c05a2dc559ac57308117424dfaad6805c"}, - {file = "multidict-6.4.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2e543a40e4946cf70a88a3be87837a3ae0aebd9058ba49e91cacb0b2cd631e2b"}, - {file = "multidict-6.4.4-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:60d849912350da557fe7de20aa8cf394aada6980d0052cc829eeda4a0db1c1db"}, - {file = "multidict-6.4.4-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:19d08b4f22eae45bb018b9f06e2838c1e4b853c67628ef8ae126d99de0da6395"}, - {file = "multidict-6.4.4-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:d693307856d1ef08041e8b6ff01d5b4618715007d288490ce2c7e29013c12b9a"}, - {file = "multidict-6.4.4-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:fad6daaed41021934917f4fb03ca2db8d8a4d79bf89b17ebe77228eb6710c003"}, - {file = "multidict-6.4.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c10d17371bff801af0daf8b073c30b6cf14215784dc08cd5c43ab5b7b8029bbc"}, - {file = "multidict-6.4.4-cp39-cp39-win32.whl", hash = "sha256:7e23f2f841fcb3ebd4724a40032d32e0892fbba4143e43d2a9e7695c5e50e6bd"}, - {file = "multidict-6.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:4d7b50b673ffb4ff4366e7ab43cf1f0aef4bd3608735c5fbdf0bdb6f690da411"}, - {file = "multidict-6.4.4-py3-none-any.whl", hash = "sha256:bd4557071b561a8b3b6075c3ce93cf9bfb6182cb241805c3d66ced3b75eff4ac"}, - {file = "multidict-6.4.4.tar.gz", hash = "sha256:69ee9e6ba214b5245031b76233dd95408a0fd57fdb019ddcc1ead4790932a8e8"}, + {file = "multidict-6.7.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:9f474ad5acda359c8758c8accc22032c6abe6dc87a8be2440d097785e27a9349"}, + {file = "multidict-6.7.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4b7a9db5a870f780220e931d0002bbfd88fb53aceb6293251e2c839415c1b20e"}, + {file = "multidict-6.7.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:03ca744319864e92721195fa28c7a3b2bc7b686246b35e4078c1e4d0eb5466d3"}, + {file = "multidict-6.7.0-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f0e77e3c0008bc9316e662624535b88d360c3a5d3f81e15cf12c139a75250046"}, + {file = "multidict-6.7.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:08325c9e5367aa379a3496aa9a022fe8837ff22e00b94db256d3a1378c76ab32"}, + {file = "multidict-6.7.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e2862408c99f84aa571ab462d25236ef9cb12a602ea959ba9c9009a54902fc73"}, + {file = "multidict-6.7.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4d72a9a2d885f5c208b0cb91ff2ed43636bb7e345ec839ff64708e04f69a13cc"}, + {file = "multidict-6.7.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:478cc36476687bac1514d651cbbaa94b86b0732fb6855c60c673794c7dd2da62"}, + {file = "multidict-6.7.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6843b28b0364dc605f21481c90fadb5f60d9123b442eb8a726bb74feef588a84"}, + {file = "multidict-6.7.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:23bfeee5316266e5ee2d625df2d2c602b829435fc3a235c2ba2131495706e4a0"}, + {file = "multidict-6.7.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:680878b9f3d45c31e1f730eef731f9b0bc1da456155688c6745ee84eb818e90e"}, + {file = "multidict-6.7.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:eb866162ef2f45063acc7a53a88ef6fe8bf121d45c30ea3c9cd87ce7e191a8d4"}, + {file = "multidict-6.7.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:df0e3bf7993bdbeca5ac25aa859cf40d39019e015c9c91809ba7093967f7a648"}, + {file = "multidict-6.7.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:661709cdcd919a2ece2234f9bae7174e5220c80b034585d7d8a755632d3e2111"}, + {file = "multidict-6.7.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:096f52730c3fb8ed419db2d44391932b63891b2c5ed14850a7e215c0ba9ade36"}, + {file = "multidict-6.7.0-cp310-cp310-win32.whl", hash = "sha256:afa8a2978ec65d2336305550535c9c4ff50ee527914328c8677b3973ade52b85"}, + {file = "multidict-6.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:b15b3afff74f707b9275d5ba6a91ae8f6429c3ffb29bbfd216b0b375a56f13d7"}, + {file = "multidict-6.7.0-cp310-cp310-win_arm64.whl", hash = "sha256:4b73189894398d59131a66ff157837b1fafea9974be486d036bb3d32331fdbf0"}, + {file = "multidict-6.7.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4d409aa42a94c0b3fa617708ef5276dfe81012ba6753a0370fcc9d0195d0a1fc"}, + {file = "multidict-6.7.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:14c9e076eede3b54c636f8ce1c9c252b5f057c62131211f0ceeec273810c9721"}, + {file = "multidict-6.7.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4c09703000a9d0fa3c3404b27041e574cc7f4df4c6563873246d0e11812a94b6"}, + {file = "multidict-6.7.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:a265acbb7bb33a3a2d626afbe756371dce0279e7b17f4f4eda406459c2b5ff1c"}, + {file = "multidict-6.7.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:51cb455de290ae462593e5b1cb1118c5c22ea7f0d3620d9940bf695cea5a4bd7"}, + {file = "multidict-6.7.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:db99677b4457c7a5c5a949353e125ba72d62b35f74e26da141530fbb012218a7"}, + {file = "multidict-6.7.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f470f68adc395e0183b92a2f4689264d1ea4b40504a24d9882c27375e6662bb9"}, + {file = "multidict-6.7.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0db4956f82723cc1c270de9c6e799b4c341d327762ec78ef82bb962f79cc07d8"}, + {file = "multidict-6.7.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3e56d780c238f9e1ae66a22d2adf8d16f485381878250db8d496623cd38b22bd"}, + {file = "multidict-6.7.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9d14baca2ee12c1a64740d4531356ba50b82543017f3ad6de0deb943c5979abb"}, + {file = "multidict-6.7.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:295a92a76188917c7f99cda95858c822f9e4aae5824246bba9b6b44004ddd0a6"}, + {file = "multidict-6.7.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:39f1719f57adbb767ef592a50ae5ebb794220d1188f9ca93de471336401c34d2"}, + {file = "multidict-6.7.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:0a13fb8e748dfc94749f622de065dd5c1def7e0d2216dba72b1d8069a389c6ff"}, + {file = "multidict-6.7.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e3aa16de190d29a0ea1b48253c57d99a68492c8dd8948638073ab9e74dc9410b"}, + {file = "multidict-6.7.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a048ce45dcdaaf1defb76b2e684f997fb5abf74437b6cb7b22ddad934a964e34"}, + {file = "multidict-6.7.0-cp311-cp311-win32.whl", hash = "sha256:a90af66facec4cebe4181b9e62a68be65e45ac9b52b67de9eec118701856e7ff"}, + {file = "multidict-6.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:95b5ffa4349df2887518bb839409bcf22caa72d82beec453216802f475b23c81"}, + {file = "multidict-6.7.0-cp311-cp311-win_arm64.whl", hash = "sha256:329aa225b085b6f004a4955271a7ba9f1087e39dcb7e65f6284a988264a63912"}, + {file = "multidict-6.7.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8a3862568a36d26e650a19bb5cbbba14b71789032aebc0423f8cc5f150730184"}, + {file = "multidict-6.7.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:960c60b5849b9b4f9dcc9bea6e3626143c252c74113df2c1540aebce70209b45"}, + {file = "multidict-6.7.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2049be98fb57a31b4ccf870bf377af2504d4ae35646a19037ec271e4c07998aa"}, + {file = "multidict-6.7.0-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:0934f3843a1860dd465d38895c17fce1f1cb37295149ab05cd1b9a03afacb2a7"}, + {file = "multidict-6.7.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b3e34f3a1b8131ba06f1a73adab24f30934d148afcd5f5de9a73565a4404384e"}, + {file = "multidict-6.7.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:efbb54e98446892590dc2458c19c10344ee9a883a79b5cec4bc34d6656e8d546"}, + {file = "multidict-6.7.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a35c5fc61d4f51eb045061e7967cfe3123d622cd500e8868e7c0c592a09fedc4"}, + {file = "multidict-6.7.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29fe6740ebccba4175af1b9b87bf553e9c15cd5868ee967e010efcf94e4fd0f1"}, + {file = "multidict-6.7.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:123e2a72e20537add2f33a79e605f6191fba2afda4cbb876e35c1a7074298a7d"}, + {file = "multidict-6.7.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b284e319754366c1aee2267a2036248b24eeb17ecd5dc16022095e747f2f4304"}, + {file = "multidict-6.7.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:803d685de7be4303b5a657b76e2f6d1240e7e0a8aa2968ad5811fa2285553a12"}, + {file = "multidict-6.7.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c04a328260dfd5db8c39538f999f02779012268f54614902d0afc775d44e0a62"}, + {file = "multidict-6.7.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8a19cdb57cd3df4cd865849d93ee14920fb97224300c88501f16ecfa2604b4e0"}, + {file = "multidict-6.7.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:9b2fd74c52accced7e75de26023b7dccee62511a600e62311b918ec5c168fc2a"}, + {file = "multidict-6.7.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3e8bfdd0e487acf992407a140d2589fe598238eaeffa3da8448d63a63cd363f8"}, + {file = "multidict-6.7.0-cp312-cp312-win32.whl", hash = "sha256:dd32a49400a2c3d52088e120ee00c1e3576cbff7e10b98467962c74fdb762ed4"}, + {file = "multidict-6.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:92abb658ef2d7ef22ac9f8bb88e8b6c3e571671534e029359b6d9e845923eb1b"}, + {file = "multidict-6.7.0-cp312-cp312-win_arm64.whl", hash = "sha256:490dab541a6a642ce1a9d61a4781656b346a55c13038f0b1244653828e3a83ec"}, + {file = "multidict-6.7.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:bee7c0588aa0076ce77c0ea5d19a68d76ad81fcd9fe8501003b9a24f9d4000f6"}, + {file = "multidict-6.7.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7ef6b61cad77091056ce0e7ce69814ef72afacb150b7ac6a3e9470def2198159"}, + {file = "multidict-6.7.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9c0359b1ec12b1d6849c59f9d319610b7f20ef990a6d454ab151aa0e3b9f78ca"}, + {file = "multidict-6.7.0-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cd240939f71c64bd658f186330603aac1a9a81bf6273f523fca63673cb7378a8"}, + {file = "multidict-6.7.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a60a4d75718a5efa473ebd5ab685786ba0c67b8381f781d1be14da49f1a2dc60"}, + {file = "multidict-6.7.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:53a42d364f323275126aff81fb67c5ca1b7a04fda0546245730a55c8c5f24bc4"}, + {file = "multidict-6.7.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3b29b980d0ddbecb736735ee5bef69bb2ddca56eff603c86f3f29a1128299b4f"}, + {file = "multidict-6.7.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f8a93b1c0ed2d04b97a5e9336fd2d33371b9a6e29ab7dd6503d63407c20ffbaf"}, + {file = "multidict-6.7.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9ff96e8815eecacc6645da76c413eb3b3d34cfca256c70b16b286a687d013c32"}, + {file = "multidict-6.7.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7516c579652f6a6be0e266aec0acd0db80829ca305c3d771ed898538804c2036"}, + {file = "multidict-6.7.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:040f393368e63fb0f3330e70c26bfd336656bed925e5cbe17c9da839a6ab13ec"}, + {file = "multidict-6.7.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b3bc26a951007b1057a1c543af845f1c7e3e71cc240ed1ace7bf4484aa99196e"}, + {file = "multidict-6.7.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:7b022717c748dd1992a83e219587aabe45980d88969f01b316e78683e6285f64"}, + {file = "multidict-6.7.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:9600082733859f00d79dee64effc7aef1beb26adb297416a4ad2116fd61374bd"}, + {file = "multidict-6.7.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:94218fcec4d72bc61df51c198d098ce2b378e0ccbac41ddbed5ef44092913288"}, + {file = "multidict-6.7.0-cp313-cp313-win32.whl", hash = "sha256:a37bd74c3fa9d00be2d7b8eca074dc56bd8077ddd2917a839bd989612671ed17"}, + {file = "multidict-6.7.0-cp313-cp313-win_amd64.whl", hash = "sha256:30d193c6cc6d559db42b6bcec8a5d395d34d60c9877a0b71ecd7c204fcf15390"}, + {file = "multidict-6.7.0-cp313-cp313-win_arm64.whl", hash = "sha256:ea3334cabe4d41b7ccd01e4d349828678794edbc2d3ae97fc162a3312095092e"}, + {file = "multidict-6.7.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:ad9ce259f50abd98a1ca0aa6e490b58c316a0fce0617f609723e40804add2c00"}, + {file = "multidict-6.7.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:07f5594ac6d084cbb5de2df218d78baf55ef150b91f0ff8a21cc7a2e3a5a58eb"}, + {file = "multidict-6.7.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:0591b48acf279821a579282444814a2d8d0af624ae0bc600aa4d1b920b6e924b"}, + {file = "multidict-6.7.0-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:749a72584761531d2b9467cfbdfd29487ee21124c304c4b6cb760d8777b27f9c"}, + {file = "multidict-6.7.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b4c3d199f953acd5b446bf7c0de1fe25d94e09e79086f8dc2f48a11a129cdf1"}, + {file = "multidict-6.7.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:9fb0211dfc3b51efea2f349ec92c114d7754dd62c01f81c3e32b765b70c45c9b"}, + {file = "multidict-6.7.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a027ec240fe73a8d6281872690b988eed307cd7d91b23998ff35ff577ca688b5"}, + {file = "multidict-6.7.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1d964afecdf3a8288789df2f5751dc0a8261138c3768d9af117ed384e538fad"}, + {file = "multidict-6.7.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:caf53b15b1b7df9fbd0709aa01409000a2b4dd03a5f6f5cc548183c7c8f8b63c"}, + {file = "multidict-6.7.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:654030da3197d927f05a536a66186070e98765aa5142794c9904555d3a9d8fb5"}, + {file = "multidict-6.7.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:2090d3718829d1e484706a2f525e50c892237b2bf9b17a79b059cb98cddc2f10"}, + {file = "multidict-6.7.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:2d2cfeec3f6f45651b3d408c4acec0ebf3daa9bc8a112a084206f5db5d05b754"}, + {file = "multidict-6.7.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:4ef089f985b8c194d341eb2c24ae6e7408c9a0e2e5658699c92f497437d88c3c"}, + {file = "multidict-6.7.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e93a0617cd16998784bf4414c7e40f17a35d2350e5c6f0bd900d3a8e02bd3762"}, + {file = "multidict-6.7.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:f0feece2ef8ebc42ed9e2e8c78fc4aa3cf455733b507c09ef7406364c94376c6"}, + {file = "multidict-6.7.0-cp313-cp313t-win32.whl", hash = "sha256:19a1d55338ec1be74ef62440ca9e04a2f001a04d0cc49a4983dc320ff0f3212d"}, + {file = "multidict-6.7.0-cp313-cp313t-win_amd64.whl", hash = "sha256:3da4fb467498df97e986af166b12d01f05d2e04f978a9c1c680ea1988e0bc4b6"}, + {file = "multidict-6.7.0-cp313-cp313t-win_arm64.whl", hash = "sha256:b4121773c49a0776461f4a904cdf6264c88e42218aaa8407e803ca8025872792"}, + {file = "multidict-6.7.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3bab1e4aff7adaa34410f93b1f8e57c4b36b9af0426a76003f441ee1d3c7e842"}, + {file = "multidict-6.7.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:b8512bac933afc3e45fb2b18da8e59b78d4f408399a960339598374d4ae3b56b"}, + {file = "multidict-6.7.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:79dcf9e477bc65414ebfea98ffd013cb39552b5ecd62908752e0e413d6d06e38"}, + {file = "multidict-6.7.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:31bae522710064b5cbeddaf2e9f32b1abab70ac6ac91d42572502299e9953128"}, + {file = "multidict-6.7.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a0df7ff02397bb63e2fd22af2c87dfa39e8c7f12947bc524dbdc528282c7e34"}, + {file = "multidict-6.7.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7a0222514e8e4c514660e182d5156a415c13ef0aabbd71682fc714e327b95e99"}, + {file = "multidict-6.7.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2397ab4daaf2698eb51a76721e98db21ce4f52339e535725de03ea962b5a3202"}, + {file = "multidict-6.7.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8891681594162635948a636c9fe0ff21746aeb3dd5463f6e25d9bea3a8a39ca1"}, + {file = "multidict-6.7.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:18706cc31dbf402a7945916dd5cddf160251b6dab8a2c5f3d6d5a55949f676b3"}, + {file = "multidict-6.7.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f844a1bbf1d207dd311a56f383f7eda2d0e134921d45751842d8235e7778965d"}, + {file = "multidict-6.7.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:d4393e3581e84e5645506923816b9cc81f5609a778c7e7534054091acc64d1c6"}, + {file = "multidict-6.7.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:fbd18dc82d7bf274b37aa48d664534330af744e03bccf696d6f4c6042e7d19e7"}, + {file = "multidict-6.7.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:b6234e14f9314731ec45c42fc4554b88133ad53a09092cc48a88e771c125dadb"}, + {file = "multidict-6.7.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:08d4379f9744d8f78d98c8673c06e202ffa88296f009c71bbafe8a6bf847d01f"}, + {file = "multidict-6.7.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:9fe04da3f79387f450fd0061d4dd2e45a72749d31bf634aecc9e27f24fdc4b3f"}, + {file = "multidict-6.7.0-cp314-cp314-win32.whl", hash = "sha256:fbafe31d191dfa7c4c51f7a6149c9fb7e914dcf9ffead27dcfd9f1ae382b3885"}, + {file = "multidict-6.7.0-cp314-cp314-win_amd64.whl", hash = "sha256:2f67396ec0310764b9222a1728ced1ab638f61aadc6226f17a71dd9324f9a99c"}, + {file = "multidict-6.7.0-cp314-cp314-win_arm64.whl", hash = "sha256:ba672b26069957ee369cfa7fc180dde1fc6f176eaf1e6beaf61fbebbd3d9c000"}, + {file = "multidict-6.7.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:c1dcc7524066fa918c6a27d61444d4ee7900ec635779058571f70d042d86ed63"}, + {file = "multidict-6.7.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:27e0b36c2d388dc7b6ced3406671b401e84ad7eb0656b8f3a2f46ed0ce483718"}, + {file = "multidict-6.7.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2a7baa46a22e77f0988e3b23d4ede5513ebec1929e34ee9495be535662c0dfe2"}, + {file = "multidict-6.7.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7bf77f54997a9166a2f5675d1201520586439424c2511723a7312bdb4bcc034e"}, + {file = "multidict-6.7.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e011555abada53f1578d63389610ac8a5400fc70ce71156b0aa30d326f1a5064"}, + {file = "multidict-6.7.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:28b37063541b897fd6a318007373930a75ca6d6ac7c940dbe14731ffdd8d498e"}, + {file = "multidict-6.7.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:05047ada7a2fde2631a0ed706f1fd68b169a681dfe5e4cf0f8e4cb6618bbc2cd"}, + {file = "multidict-6.7.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:716133f7d1d946a4e1b91b1756b23c088881e70ff180c24e864c26192ad7534a"}, + {file = "multidict-6.7.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d1bed1b467ef657f2a0ae62844a607909ef1c6889562de5e1d505f74457d0b96"}, + {file = "multidict-6.7.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:ca43bdfa5d37bd6aee89d85e1d0831fb86e25541be7e9d376ead1b28974f8e5e"}, + {file = "multidict-6.7.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:44b546bd3eb645fd26fb949e43c02a25a2e632e2ca21a35e2e132c8105dc8599"}, + {file = "multidict-6.7.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:a6ef16328011d3f468e7ebc326f24c1445f001ca1dec335b2f8e66bed3006394"}, + {file = "multidict-6.7.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:5aa873cbc8e593d361ae65c68f85faadd755c3295ea2c12040ee146802f23b38"}, + {file = "multidict-6.7.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:3d7b6ccce016e29df4b7ca819659f516f0bc7a4b3efa3bb2012ba06431b044f9"}, + {file = "multidict-6.7.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:171b73bd4ee683d307599b66793ac80981b06f069b62eea1c9e29c9241aa66b0"}, + {file = "multidict-6.7.0-cp314-cp314t-win32.whl", hash = "sha256:b2d7f80c4e1fd010b07cb26820aae86b7e73b681ee4889684fb8d2d4537aab13"}, + {file = "multidict-6.7.0-cp314-cp314t-win_amd64.whl", hash = "sha256:09929cab6fcb68122776d575e03c6cc64ee0b8fca48d17e135474b042ce515cd"}, + {file = "multidict-6.7.0-cp314-cp314t-win_arm64.whl", hash = "sha256:cc41db090ed742f32bd2d2c721861725e6109681eddf835d0a82bd3a5c382827"}, + {file = "multidict-6.7.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:363eb68a0a59bd2303216d2346e6c441ba10d36d1f9969fcb6f1ba700de7bb5c"}, + {file = "multidict-6.7.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d874eb056410ca05fed180b6642e680373688efafc7f077b2a2f61811e873a40"}, + {file = "multidict-6.7.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8b55d5497b51afdfde55925e04a022f1de14d4f4f25cdfd4f5d9b0aa96166851"}, + {file = "multidict-6.7.0-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:f8e5c0031b90ca9ce555e2e8fd5c3b02a25f14989cbc310701823832c99eb687"}, + {file = "multidict-6.7.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9cf41880c991716f3c7cec48e2f19ae4045fc9db5fc9cff27347ada24d710bb5"}, + {file = "multidict-6.7.0-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8cfc12a8630a29d601f48d47787bd7eb730e475e83edb5d6c5084317463373eb"}, + {file = "multidict-6.7.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3996b50c3237c4aec17459217c1e7bbdead9a22a0fcd3c365564fbd16439dde6"}, + {file = "multidict-6.7.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7f5170993a0dd3ab871c74f45c0a21a4e2c37a2f2b01b5f722a2ad9c6650469e"}, + {file = "multidict-6.7.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ec81878ddf0e98817def1e77d4f50dae5ef5b0e4fe796fae3bd674304172416e"}, + {file = "multidict-6.7.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:9281bf5b34f59afbc6b1e477a372e9526b66ca446f4bf62592839c195a718b32"}, + {file = "multidict-6.7.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:68af405971779d8b37198726f2b6fe3955db846fee42db7a4286fc542203934c"}, + {file = "multidict-6.7.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3ba3ef510467abb0667421a286dc906e30eb08569365f5cdb131d7aff7c2dd84"}, + {file = "multidict-6.7.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:b61189b29081a20c7e4e0b49b44d5d44bb0dc92be3c6d06a11cc043f81bf9329"}, + {file = "multidict-6.7.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:fb287618b9c7aa3bf8d825f02d9201b2f13078a5ed3b293c8f4d953917d84d5e"}, + {file = "multidict-6.7.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:521f33e377ff64b96c4c556b81c55d0cfffb96a11c194fd0c3f1e56f3d8dd5a4"}, + {file = "multidict-6.7.0-cp39-cp39-win32.whl", hash = "sha256:ce8fdc2dca699f8dbf055a61d73eaa10482569ad20ee3c36ef9641f69afa8c91"}, + {file = "multidict-6.7.0-cp39-cp39-win_amd64.whl", hash = "sha256:7e73299c99939f089dd9b2120a04a516b95cdf8c1cd2b18c53ebf0de80b1f18f"}, + {file = "multidict-6.7.0-cp39-cp39-win_arm64.whl", hash = "sha256:6bdce131e14b04fd34a809b6380dbfd826065c3e2fe8a50dbae659fa0c390546"}, + {file = "multidict-6.7.0-py3-none-any.whl", hash = "sha256:394fc5c42a333c9ffc3e421a4c85e08580d990e08b99f6bf35b4132114c5dcb3"}, + {file = "multidict-6.7.0.tar.gz", hash = "sha256:c6e99d9a65ca282e578dfea819cfa9c0a62b2499d8677392e09feaf305e9e6f5"}, ] [[package]] @@ -2859,77 +2248,27 @@ test = ["coverage[toml] (>=5.2)", "coveralls (>=2.1.1)", "hypothesis", "pyannota type = ["mypy", "mypy-extensions"] [[package]] -name = "mypy-boto3-s3" -version = "1.38.26" -description = "Type annotations for boto3 S3 1.38.26 service generated with mypy-boto3-builder 8.11.0" +name = "nemesis-dpapi" +version = "0.1.0" +description = "" optional = false -python-versions = ">=3.8" +python-versions = ">=3.12" groups = ["main"] -files = [ - {file = "mypy_boto3_s3-1.38.26-py3-none-any.whl", hash = "sha256:1129d64be1aee863e04f0c92ac8d315578f13ccae64fa199b20ad0950d2b9616"}, - {file = "mypy_boto3_s3-1.38.26.tar.gz", hash = "sha256:38a45dee5782d5c07ddea07ea50965c4d2ba7e77617c19f613b4c9f80f961b52"}, -] +files = [] +develop = true -[[package]] -name = "numpy" -version = "2.3.0" -description = "Fundamental package for array computing in Python" -optional = false -python-versions = ">=3.11" -groups = ["main"] -files = [ - {file = "numpy-2.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c3c9fdde0fa18afa1099d6257eb82890ea4f3102847e692193b54e00312a9ae9"}, - {file = "numpy-2.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:46d16f72c2192da7b83984aa5455baee640e33a9f1e61e656f29adf55e406c2b"}, - {file = "numpy-2.3.0-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:a0be278be9307c4ab06b788f2a077f05e180aea817b3e41cebbd5aaf7bd85ed3"}, - {file = "numpy-2.3.0-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:99224862d1412d2562248d4710126355d3a8db7672170a39d6909ac47687a8a4"}, - {file = "numpy-2.3.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:2393a914db64b0ead0ab80c962e42d09d5f385802006a6c87835acb1f58adb96"}, - {file = "numpy-2.3.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:7729c8008d55e80784bd113787ce876ca117185c579c0d626f59b87d433ea779"}, - {file = "numpy-2.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:06d4fb37a8d383b769281714897420c5cc3545c79dc427df57fc9b852ee0bf58"}, - {file = "numpy-2.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c39ec392b5db5088259c68250e342612db82dc80ce044cf16496cf14cf6bc6f8"}, - {file = "numpy-2.3.0-cp311-cp311-win32.whl", hash = "sha256:ee9d3ee70d62827bc91f3ea5eee33153212c41f639918550ac0475e3588da59f"}, - {file = "numpy-2.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:43c55b6a860b0eb44d42341438b03513cf3879cb3617afb749ad49307e164edd"}, - {file = "numpy-2.3.0-cp311-cp311-win_arm64.whl", hash = "sha256:2e6a1409eee0cb0316cb64640a49a49ca44deb1a537e6b1121dc7c458a1299a8"}, - {file = "numpy-2.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:389b85335838155a9076e9ad7f8fdba0827496ec2d2dc32ce69ce7898bde03ba"}, - {file = "numpy-2.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9498f60cd6bb8238d8eaf468a3d5bb031d34cd12556af53510f05fcf581c1b7e"}, - {file = "numpy-2.3.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:622a65d40d8eb427d8e722fd410ac3ad4958002f109230bc714fa551044ebae2"}, - {file = "numpy-2.3.0-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:b9446d9d8505aadadb686d51d838f2b6688c9e85636a0c3abaeb55ed54756459"}, - {file = "numpy-2.3.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:50080245365d75137a2bf46151e975de63146ae6d79f7e6bd5c0e85c9931d06a"}, - {file = "numpy-2.3.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:c24bb4113c66936eeaa0dc1e47c74770453d34f46ee07ae4efd853a2ed1ad10a"}, - {file = "numpy-2.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4d8d294287fdf685281e671886c6dcdf0291a7c19db3e5cb4178d07ccf6ecc67"}, - {file = "numpy-2.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6295f81f093b7f5769d1728a6bd8bf7466de2adfa771ede944ce6711382b89dc"}, - {file = "numpy-2.3.0-cp312-cp312-win32.whl", hash = "sha256:e6648078bdd974ef5d15cecc31b0c410e2e24178a6e10bf511e0557eed0f2570"}, - {file = "numpy-2.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:0898c67a58cdaaf29994bc0e2c65230fd4de0ac40afaf1584ed0b02cd74c6fdd"}, - {file = "numpy-2.3.0-cp312-cp312-win_arm64.whl", hash = "sha256:bd8df082b6c4695753ad6193018c05aac465d634834dca47a3ae06d4bb22d9ea"}, - {file = "numpy-2.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5754ab5595bfa2c2387d241296e0381c21f44a4b90a776c3c1d39eede13a746a"}, - {file = "numpy-2.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d11fa02f77752d8099573d64e5fe33de3229b6632036ec08f7080f46b6649959"}, - {file = "numpy-2.3.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:aba48d17e87688a765ab1cd557882052f238e2f36545dfa8e29e6a91aef77afe"}, - {file = "numpy-2.3.0-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:4dc58865623023b63b10d52f18abaac3729346a7a46a778381e0e3af4b7f3beb"}, - {file = "numpy-2.3.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:df470d376f54e052c76517393fa443758fefcdd634645bc9c1f84eafc67087f0"}, - {file = "numpy-2.3.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:87717eb24d4a8a64683b7a4e91ace04e2f5c7c77872f823f02a94feee186168f"}, - {file = "numpy-2.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d8fa264d56882b59dcb5ea4d6ab6f31d0c58a57b41aec605848b6eb2ef4a43e8"}, - {file = "numpy-2.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e651756066a0eaf900916497e20e02fe1ae544187cb0fe88de981671ee7f6270"}, - {file = "numpy-2.3.0-cp313-cp313-win32.whl", hash = "sha256:e43c3cce3b6ae5f94696669ff2a6eafd9a6b9332008bafa4117af70f4b88be6f"}, - {file = "numpy-2.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:81ae0bf2564cf475f94be4a27ef7bcf8af0c3e28da46770fc904da9abd5279b5"}, - {file = "numpy-2.3.0-cp313-cp313-win_arm64.whl", hash = "sha256:c8738baa52505fa6e82778580b23f945e3578412554d937093eac9205e845e6e"}, - {file = "numpy-2.3.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:39b27d8b38942a647f048b675f134dd5a567f95bfff481f9109ec308515c51d8"}, - {file = "numpy-2.3.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:0eba4a1ea88f9a6f30f56fdafdeb8da3774349eacddab9581a21234b8535d3d3"}, - {file = "numpy-2.3.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:b0f1f11d0a1da54927436505a5a7670b154eac27f5672afc389661013dfe3d4f"}, - {file = "numpy-2.3.0-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:690d0a5b60a47e1f9dcec7b77750a4854c0d690e9058b7bef3106e3ae9117808"}, - {file = "numpy-2.3.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:8b51ead2b258284458e570942137155978583e407babc22e3d0ed7af33ce06f8"}, - {file = "numpy-2.3.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:aaf81c7b82c73bd9b45e79cfb9476cb9c29e937494bfe9092c26aece812818ad"}, - {file = "numpy-2.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:f420033a20b4f6a2a11f585f93c843ac40686a7c3fa514060a97d9de93e5e72b"}, - {file = "numpy-2.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:d344ca32ab482bcf8735d8f95091ad081f97120546f3d250240868430ce52555"}, - {file = "numpy-2.3.0-cp313-cp313t-win32.whl", hash = "sha256:48a2e8eaf76364c32a1feaa60d6925eaf32ed7a040183b807e02674305beef61"}, - {file = "numpy-2.3.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ba17f93a94e503551f154de210e4d50c5e3ee20f7e7a1b5f6ce3f22d419b93bb"}, - {file = "numpy-2.3.0-cp313-cp313t-win_arm64.whl", hash = "sha256:f14e016d9409680959691c109be98c436c6249eaf7f118b424679793607b5944"}, - {file = "numpy-2.3.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:80b46117c7359de8167cc00a2c7d823bdd505e8c7727ae0871025a86d668283b"}, - {file = "numpy-2.3.0-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:5814a0f43e70c061f47abd5857d120179609ddc32a613138cbb6c4e9e2dbdda5"}, - {file = "numpy-2.3.0-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:ef6c1e88fd6b81ac6d215ed71dc8cd027e54d4bf1d2682d362449097156267a2"}, - {file = "numpy-2.3.0-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:33a5a12a45bb82d9997e2c0b12adae97507ad7c347546190a18ff14c28bbca12"}, - {file = "numpy-2.3.0-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:54dfc8681c1906d239e95ab1508d0a533c4a9505e52ee2d71a5472b04437ef97"}, - {file = "numpy-2.3.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:e017a8a251ff4d18d71f139e28bdc7c31edba7a507f72b1414ed902cbe48c74d"}, - {file = "numpy-2.3.0.tar.gz", hash = "sha256:581f87f9e9e9db2cba2141400e160e9dd644ee248788d6f90636eeb8fd9260a6"}, -] +[package.dependencies] +asyncpg = ">=0.29.0,<=0.30.0" +cryptography = ">=42.0.0,<43.0.0" +dapr = "1.16.0" +dpapick3 = ">=0.7.1,<0.8.0" +impacket = ">=0.12.0,<0.13.0" +pycryptodome = ">=3.23.0,<4.0.0" +pydantic = ">=2.0.0,<3.0.0" + +[package.source] +type = "directory" +url = "../../libs/nemesis_dpapi" [[package]] name = "olefile" @@ -2969,223 +2308,108 @@ pyparsing = ">=2.1.0,<4" [package.extras] full = ["XLMMacroDeobfuscator"] -[[package]] -name = "openai" -version = "1.86.0" -description = "The official Python library for the openai API" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "openai-1.86.0-py3-none-any.whl", hash = "sha256:c8889c39410621fe955c230cc4c21bfe36ec887f4e60a957de05f507d7e1f349"}, - {file = "openai-1.86.0.tar.gz", hash = "sha256:c64d5b788359a8fdf69bd605ae804ce41c1ce2e78b8dd93e2542e0ee267f1e4b"}, -] - -[package.dependencies] -anyio = ">=3.5.0,<5" -distro = ">=1.7.0,<2" -httpx = ">=0.23.0,<1" -jiter = ">=0.4.0,<1" -pydantic = ">=1.9.0,<3" -sniffio = "*" -tqdm = ">4" -typing-extensions = ">=4.11,<5" - -[package.extras] -datalib = ["numpy (>=1)", "pandas (>=1.2.3)", "pandas-stubs (>=1.1.0.11)"] -realtime = ["websockets (>=13,<16)"] -voice-helpers = ["numpy (>=2.0.2)", "sounddevice (>=0.5.1)"] - [[package]] name = "opentelemetry-api" -version = "1.30.0" +version = "1.38.0" description = "OpenTelemetry Python API" optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "opentelemetry_api-1.30.0-py3-none-any.whl", hash = "sha256:d5f5284890d73fdf47f843dda3210edf37a38d66f44f2b5aedc1e89ed455dc09"}, - {file = "opentelemetry_api-1.30.0.tar.gz", hash = "sha256:375893400c1435bf623f7dfb3bcd44825fe6b56c34d0667c542ea8257b1a1240"}, -] - -[package.dependencies] -deprecated = ">=1.2.6" -importlib-metadata = ">=6.0,<=8.5.0" - -[[package]] -name = "opentelemetry-exporter-otlp-proto-common" -version = "1.30.0" -description = "OpenTelemetry Protobuf encoding" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "opentelemetry_exporter_otlp_proto_common-1.30.0-py3-none-any.whl", hash = "sha256:5468007c81aa9c44dc961ab2cf368a29d3475977df83b4e30aeed42aa7bc3b38"}, - {file = "opentelemetry_exporter_otlp_proto_common-1.30.0.tar.gz", hash = "sha256:ddbfbf797e518411857d0ca062c957080279320d6235a279f7b64ced73c13897"}, -] - -[package.dependencies] -opentelemetry-proto = "1.30.0" - -[[package]] -name = "opentelemetry-exporter-otlp-proto-grpc" -version = "1.30.0" -description = "OpenTelemetry Collector Protobuf over gRPC Exporter" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "opentelemetry_exporter_otlp_proto_grpc-1.30.0-py3-none-any.whl", hash = "sha256:2906bcae3d80acc54fd1ffcb9e44d324e8631058b502ebe4643ca71d1ff30830"}, - {file = "opentelemetry_exporter_otlp_proto_grpc-1.30.0.tar.gz", hash = "sha256:d0f10f0b9b9a383b7d04a144d01cb280e70362cccc613987e234183fd1f01177"}, -] - -[package.dependencies] -deprecated = ">=1.2.6" -googleapis-common-protos = ">=1.52,<2.0" -grpcio = ">=1.63.2,<2.0.0" -opentelemetry-api = ">=1.15,<2.0" -opentelemetry-exporter-otlp-proto-common = "1.30.0" -opentelemetry-proto = "1.30.0" -opentelemetry-sdk = ">=1.30.0,<1.31.0" - -[[package]] -name = "opentelemetry-exporter-zipkin-json" -version = "1.34.1" -description = "Zipkin Span JSON Exporter for OpenTelemetry" -optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "opentelemetry_exporter_zipkin_json-1.34.1-py3-none-any.whl", hash = "sha256:cd4aa305919848b68a7d2ad5666bb3209f5adc4c78b0467870616e45a79c797e"}, - {file = "opentelemetry_exporter_zipkin_json-1.34.1.tar.gz", hash = "sha256:fe81b17655f797f48ba1ae6ed19891e70c4801f58597b5d1d838b57c2da55b2a"}, + {file = "opentelemetry_api-1.38.0-py3-none-any.whl", hash = "sha256:2891b0197f47124454ab9f0cf58f3be33faca394457ac3e09daba13ff50aa582"}, + {file = "opentelemetry_api-1.38.0.tar.gz", hash = "sha256:f4c193b5e8acb0912b06ac5b16321908dd0843d75049c091487322284a3eea12"}, ] [package.dependencies] -opentelemetry-api = ">=1.3,<2.0" -opentelemetry-sdk = ">=1.11,<2.0" -requests = ">=2.7,<3.0" +importlib-metadata = ">=6.0,<8.8.0" +typing-extensions = ">=4.5.0" [[package]] -name = "opentelemetry-instrumentation" -version = "0.51b0" -description = "Instrumentation Tools & Auto Instrumentation for OpenTelemetry Python" +name = "opentelemetry-exporter-otlp-proto-common" +version = "1.38.0" +description = "OpenTelemetry Protobuf encoding" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["main"] files = [ - {file = "opentelemetry_instrumentation-0.51b0-py3-none-any.whl", hash = "sha256:c6de8bd26b75ec8b0e54dff59e198946e29de6a10ec65488c357d4b34aa5bdcf"}, - {file = "opentelemetry_instrumentation-0.51b0.tar.gz", hash = "sha256:4ca266875e02f3988536982467f7ef8c32a38b8895490ddce9ad9604649424fa"}, + {file = "opentelemetry_exporter_otlp_proto_common-1.38.0-py3-none-any.whl", hash = "sha256:03cb76ab213300fe4f4c62b7d8f17d97fcfd21b89f0b5ce38ea156327ddda74a"}, + {file = "opentelemetry_exporter_otlp_proto_common-1.38.0.tar.gz", hash = "sha256:e333278afab4695aa8114eeb7bf4e44e65c6607d54968271a249c180b2cb605c"}, ] [package.dependencies] -opentelemetry-api = ">=1.4,<2.0" -opentelemetry-semantic-conventions = "0.51b0" -packaging = ">=18.0" -wrapt = ">=1.0.0,<2.0.0" +opentelemetry-proto = "1.38.0" [[package]] -name = "opentelemetry-instrumentation-asgi" -version = "0.51b0" -description = "ASGI instrumentation for OpenTelemetry" +name = "opentelemetry-exporter-otlp-proto-grpc" +version = "1.38.0" +description = "OpenTelemetry Collector Protobuf over gRPC Exporter" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["main"] files = [ - {file = "opentelemetry_instrumentation_asgi-0.51b0-py3-none-any.whl", hash = "sha256:e8072993db47303b633c6ec1bc74726ba4d32bd0c46c28dfadf99f79521a324c"}, - {file = "opentelemetry_instrumentation_asgi-0.51b0.tar.gz", hash = "sha256:b3fe97c00f0bfa934371a69674981d76591c68d937b6422a5716ca21081b4148"}, + {file = "opentelemetry_exporter_otlp_proto_grpc-1.38.0-py3-none-any.whl", hash = "sha256:7c49fd9b4bd0dbe9ba13d91f764c2d20b0025649a6e4ac35792fb8d84d764bc7"}, + {file = "opentelemetry_exporter_otlp_proto_grpc-1.38.0.tar.gz", hash = "sha256:2473935e9eac71f401de6101d37d6f3f0f1831db92b953c7dcc912536158ebd6"}, ] [package.dependencies] -asgiref = ">=3.0,<4.0" -opentelemetry-api = ">=1.12,<2.0" -opentelemetry-instrumentation = "0.51b0" -opentelemetry-semantic-conventions = "0.51b0" -opentelemetry-util-http = "0.51b0" - -[package.extras] -instruments = ["asgiref (>=3.0,<4.0)"] - -[[package]] -name = "opentelemetry-instrumentation-fastapi" -version = "0.51b0" -description = "OpenTelemetry FastAPI Instrumentation" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "opentelemetry_instrumentation_fastapi-0.51b0-py3-none-any.whl", hash = "sha256:10513bbc11a1188adb9c1d2c520695f7a8f2b5f4de14e8162098035901cd6493"}, - {file = "opentelemetry_instrumentation_fastapi-0.51b0.tar.gz", hash = "sha256:1624e70f2f4d12ceb792d8a0c331244cd6723190ccee01336273b4559bc13abc"}, +googleapis-common-protos = ">=1.57,<2.0" +grpcio = [ + {version = ">=1.63.2,<2.0.0", markers = "python_version < \"3.13\""}, + {version = ">=1.66.2,<2.0.0", markers = "python_version >= \"3.13\""}, ] - -[package.dependencies] -opentelemetry-api = ">=1.12,<2.0" -opentelemetry-instrumentation = "0.51b0" -opentelemetry-instrumentation-asgi = "0.51b0" -opentelemetry-semantic-conventions = "0.51b0" -opentelemetry-util-http = "0.51b0" - -[package.extras] -instruments = ["fastapi (>=0.58,<1.0)"] +opentelemetry-api = ">=1.15,<2.0" +opentelemetry-exporter-otlp-proto-common = "1.38.0" +opentelemetry-proto = "1.38.0" +opentelemetry-sdk = ">=1.38.0,<1.39.0" +typing-extensions = ">=4.6.0" [[package]] name = "opentelemetry-proto" -version = "1.30.0" +version = "1.38.0" description = "OpenTelemetry Python Proto" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["main"] files = [ - {file = "opentelemetry_proto-1.30.0-py3-none-any.whl", hash = "sha256:c6290958ff3ddacc826ca5abbeb377a31c2334387352a259ba0df37c243adc11"}, - {file = "opentelemetry_proto-1.30.0.tar.gz", hash = "sha256:afe5c9c15e8b68d7c469596e5b32e8fc085eb9febdd6fb4e20924a93a0389179"}, + {file = "opentelemetry_proto-1.38.0-py3-none-any.whl", hash = "sha256:b6ebe54d3217c42e45462e2a1ae28c3e2bf2ec5a5645236a490f55f45f1a0a18"}, + {file = "opentelemetry_proto-1.38.0.tar.gz", hash = "sha256:88b161e89d9d372ce723da289b7da74c3a8354a8e5359992be813942969ed468"}, ] [package.dependencies] -protobuf = ">=5.0,<6.0" +protobuf = ">=5.0,<7.0" [[package]] name = "opentelemetry-sdk" -version = "1.30.0" +version = "1.38.0" description = "OpenTelemetry Python SDK" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["main"] files = [ - {file = "opentelemetry_sdk-1.30.0-py3-none-any.whl", hash = "sha256:14fe7afc090caad881addb6926cec967129bd9260c4d33ae6a217359f6b61091"}, - {file = "opentelemetry_sdk-1.30.0.tar.gz", hash = "sha256:c9287a9e4a7614b9946e933a67168450b9ab35f08797eb9bc77d998fa480fa18"}, + {file = "opentelemetry_sdk-1.38.0-py3-none-any.whl", hash = "sha256:1c66af6564ecc1553d72d811a01df063ff097cdc82ce188da9951f93b8d10f6b"}, + {file = "opentelemetry_sdk-1.38.0.tar.gz", hash = "sha256:93df5d4d871ed09cb4272305be4d996236eedb232253e3ab864c8620f051cebe"}, ] [package.dependencies] -opentelemetry-api = "1.30.0" -opentelemetry-semantic-conventions = "0.51b0" -typing-extensions = ">=3.7.4" +opentelemetry-api = "1.38.0" +opentelemetry-semantic-conventions = "0.59b0" +typing-extensions = ">=4.5.0" [[package]] name = "opentelemetry-semantic-conventions" -version = "0.51b0" +version = "0.59b0" description = "OpenTelemetry Semantic Conventions" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["main"] files = [ - {file = "opentelemetry_semantic_conventions-0.51b0-py3-none-any.whl", hash = "sha256:fdc777359418e8d06c86012c3dc92c88a6453ba662e941593adb062e48c2eeae"}, - {file = "opentelemetry_semantic_conventions-0.51b0.tar.gz", hash = "sha256:3fabf47f35d1fd9aebcdca7e6802d86bd5ebc3bc3408b7e3248dde6e87a18c47"}, + {file = "opentelemetry_semantic_conventions-0.59b0-py3-none-any.whl", hash = "sha256:35d3b8833ef97d614136e253c1da9342b4c3c083bbaf29ce31d572a1c3825eed"}, + {file = "opentelemetry_semantic_conventions-0.59b0.tar.gz", hash = "sha256:7a6db3f30d70202d5bf9fa4b69bc866ca6a30437287de6c510fb594878aed6b0"}, ] [package.dependencies] -deprecated = ">=1.2.6" -opentelemetry-api = "1.30.0" - -[[package]] -name = "opentelemetry-util-http" -version = "0.51b0" -description = "Web util for OpenTelemetry" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "opentelemetry_util_http-0.51b0-py3-none-any.whl", hash = "sha256:0561d7a6e9c422b9ef9ae6e77eafcfcd32a2ab689f5e801475cbb67f189efa20"}, - {file = "opentelemetry_util_http-0.51b0.tar.gz", hash = "sha256:05edd19ca1cc3be3968b1e502fd94816901a365adbeaab6b6ddb974384d3a0b9"}, -] +opentelemetry-api = "1.38.0" +typing-extensions = ">=4.5.0" [[package]] name = "oscrypto" @@ -3208,95 +2432,12 @@ version = "25.0" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" -groups = ["main"] +groups = ["main", "dev"] files = [ {file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"}, {file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"}, ] -[[package]] -name = "pandas" -version = "2.3.0" -description = "Powerful data structures for data analysis, time series, and statistics" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "pandas-2.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:625466edd01d43b75b1883a64d859168e4556261a5035b32f9d743b67ef44634"}, - {file = "pandas-2.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a6872d695c896f00df46b71648eea332279ef4077a409e2fe94220208b6bb675"}, - {file = "pandas-2.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4dd97c19bd06bc557ad787a15b6489d2614ddaab5d104a0310eb314c724b2d2"}, - {file = "pandas-2.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:034abd6f3db8b9880aaee98f4f5d4dbec7c4829938463ec046517220b2f8574e"}, - {file = "pandas-2.3.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:23c2b2dc5213810208ca0b80b8666670eb4660bbfd9d45f58592cc4ddcfd62e1"}, - {file = "pandas-2.3.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:39ff73ec07be5e90330cc6ff5705c651ace83374189dcdcb46e6ff54b4a72cd6"}, - {file = "pandas-2.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:40cecc4ea5abd2921682b57532baea5588cc5f80f0231c624056b146887274d2"}, - {file = "pandas-2.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8adff9f138fc614347ff33812046787f7d43b3cef7c0f0171b3340cae333f6ca"}, - {file = "pandas-2.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e5f08eb9a445d07720776df6e641975665c9ea12c9d8a331e0f6890f2dcd76ef"}, - {file = "pandas-2.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fa35c266c8cd1a67d75971a1912b185b492d257092bdd2709bbdebe574ed228d"}, - {file = "pandas-2.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14a0cc77b0f089d2d2ffe3007db58f170dae9b9f54e569b299db871a3ab5bf46"}, - {file = "pandas-2.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c06f6f144ad0a1bf84699aeea7eff6068ca5c63ceb404798198af7eb86082e33"}, - {file = "pandas-2.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ed16339bc354a73e0a609df36d256672c7d296f3f767ac07257801aa064ff73c"}, - {file = "pandas-2.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:fa07e138b3f6c04addfeaf56cc7fdb96c3b68a3fe5e5401251f231fce40a0d7a"}, - {file = "pandas-2.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2eb4728a18dcd2908c7fccf74a982e241b467d178724545a48d0caf534b38ebf"}, - {file = "pandas-2.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b9d8c3187be7479ea5c3d30c32a5d73d62a621166675063b2edd21bc47614027"}, - {file = "pandas-2.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9ff730713d4c4f2f1c860e36c005c7cefc1c7c80c21c0688fd605aa43c9fcf09"}, - {file = "pandas-2.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba24af48643b12ffe49b27065d3babd52702d95ab70f50e1b34f71ca703e2c0d"}, - {file = "pandas-2.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:404d681c698e3c8a40a61d0cd9412cc7364ab9a9cc6e144ae2992e11a2e77a20"}, - {file = "pandas-2.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6021910b086b3ca756755e86ddc64e0ddafd5e58e076c72cb1585162e5ad259b"}, - {file = "pandas-2.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:094e271a15b579650ebf4c5155c05dcd2a14fd4fdd72cf4854b2f7ad31ea30be"}, - {file = "pandas-2.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2c7e2fc25f89a49a11599ec1e76821322439d90820108309bf42130d2f36c983"}, - {file = "pandas-2.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c6da97aeb6a6d233fb6b17986234cc723b396b50a3c6804776351994f2a658fd"}, - {file = "pandas-2.3.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb32dc743b52467d488e7a7c8039b821da2826a9ba4f85b89ea95274f863280f"}, - {file = "pandas-2.3.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:213cd63c43263dbb522c1f8a7c9d072e25900f6975596f883f4bebd77295d4f3"}, - {file = "pandas-2.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1d2b33e68d0ce64e26a4acc2e72d747292084f4e8db4c847c6f5f6cbe56ed6d8"}, - {file = "pandas-2.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:430a63bae10b5086995db1b02694996336e5a8ac9a96b4200572b413dfdfccb9"}, - {file = "pandas-2.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:4930255e28ff5545e2ca404637bcc56f031893142773b3468dc021c6c32a1390"}, - {file = "pandas-2.3.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:f925f1ef673b4bd0271b1809b72b3270384f2b7d9d14a189b12b7fc02574d575"}, - {file = "pandas-2.3.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e78ad363ddb873a631e92a3c063ade1ecfb34cae71e9a2be6ad100f875ac1042"}, - {file = "pandas-2.3.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:951805d146922aed8357e4cc5671b8b0b9be1027f0619cea132a9f3f65f2f09c"}, - {file = "pandas-2.3.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a881bc1309f3fce34696d07b00f13335c41f5f5a8770a33b09ebe23261cfc67"}, - {file = "pandas-2.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:e1991bbb96f4050b09b5f811253c4f3cf05ee89a589379aa36cd623f21a31d6f"}, - {file = "pandas-2.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:bb3be958022198531eb7ec2008cfc78c5b1eed51af8600c6c5d9160d89d8d249"}, - {file = "pandas-2.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9efc0acbbffb5236fbdf0409c04edce96bec4bdaa649d49985427bd1ec73e085"}, - {file = "pandas-2.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:75651c14fde635e680496148a8526b328e09fe0572d9ae9b638648c46a544ba3"}, - {file = "pandas-2.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf5be867a0541a9fb47a4be0c5790a4bccd5b77b92f0a59eeec9375fafc2aa14"}, - {file = "pandas-2.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:84141f722d45d0c2a89544dd29d35b3abfc13d2250ed7e68394eda7564bd6324"}, - {file = "pandas-2.3.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:f95a2aef32614ed86216d3c450ab12a4e82084e8102e355707a1d96e33d51c34"}, - {file = "pandas-2.3.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:e0f51973ba93a9f97185049326d75b942b9aeb472bec616a129806facb129ebb"}, - {file = "pandas-2.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:b198687ca9c8529662213538a9bb1e60fa0bf0f6af89292eb68fea28743fcd5a"}, - {file = "pandas-2.3.0.tar.gz", hash = "sha256:34600ab34ebf1131a7613a260a61dbe8b62c188ec0ea4c296da7c9a06b004133"}, -] - -[package.dependencies] -numpy = {version = ">=1.26.0", markers = "python_version >= \"3.12\""} -python-dateutil = ">=2.8.2" -pytz = ">=2020.1" -tzdata = ">=2022.7" - -[package.extras] -all = ["PyQt5 (>=5.15.9)", "SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "adbc-driver-sqlite (>=0.8.0)", "beautifulsoup4 (>=4.11.2)", "bottleneck (>=1.3.6)", "dataframe-api-compat (>=0.1.7)", "fastparquet (>=2022.12.0)", "fsspec (>=2022.11.0)", "gcsfs (>=2022.11.0)", "html5lib (>=1.1)", "hypothesis (>=6.46.1)", "jinja2 (>=3.1.2)", "lxml (>=4.9.2)", "matplotlib (>=3.6.3)", "numba (>=0.56.4)", "numexpr (>=2.8.4)", "odfpy (>=1.4.1)", "openpyxl (>=3.1.0)", "pandas-gbq (>=0.19.0)", "psycopg2 (>=2.9.6)", "pyarrow (>=10.0.1)", "pymysql (>=1.0.2)", "pyreadstat (>=1.2.0)", "pytest (>=7.3.2)", "pytest-xdist (>=2.2.0)", "python-calamine (>=0.1.7)", "pyxlsb (>=1.0.10)", "qtpy (>=2.3.0)", "s3fs (>=2022.11.0)", "scipy (>=1.10.0)", "tables (>=3.8.0)", "tabulate (>=0.9.0)", "xarray (>=2022.12.0)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.0.5)", "zstandard (>=0.19.0)"] -aws = ["s3fs (>=2022.11.0)"] -clipboard = ["PyQt5 (>=5.15.9)", "qtpy (>=2.3.0)"] -compression = ["zstandard (>=0.19.0)"] -computation = ["scipy (>=1.10.0)", "xarray (>=2022.12.0)"] -consortium-standard = ["dataframe-api-compat (>=0.1.7)"] -excel = ["odfpy (>=1.4.1)", "openpyxl (>=3.1.0)", "python-calamine (>=0.1.7)", "pyxlsb (>=1.0.10)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.0.5)"] -feather = ["pyarrow (>=10.0.1)"] -fss = ["fsspec (>=2022.11.0)"] -gcp = ["gcsfs (>=2022.11.0)", "pandas-gbq (>=0.19.0)"] -hdf5 = ["tables (>=3.8.0)"] -html = ["beautifulsoup4 (>=4.11.2)", "html5lib (>=1.1)", "lxml (>=4.9.2)"] -mysql = ["SQLAlchemy (>=2.0.0)", "pymysql (>=1.0.2)"] -output-formatting = ["jinja2 (>=3.1.2)", "tabulate (>=0.9.0)"] -parquet = ["pyarrow (>=10.0.1)"] -performance = ["bottleneck (>=1.3.6)", "numba (>=0.56.4)", "numexpr (>=2.8.4)"] -plot = ["matplotlib (>=3.6.3)"] -postgresql = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "psycopg2 (>=2.9.6)"] -pyarrow = ["pyarrow (>=10.0.1)"] -spss = ["pyreadstat (>=1.2.0)"] -sql-other = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "adbc-driver-sqlite (>=0.8.0)"] -test = ["hypothesis (>=6.46.1)", "pytest (>=7.3.2)", "pytest-xdist (>=2.2.0)"] -xml = ["lxml (>=4.9.2)"] - [[package]] name = "pcodedmp" version = "1.2.6" @@ -3326,17 +2467,146 @@ files = [ ] [[package]] -name = "ply" -version = "3.11" -description = "Python Lex & Yacc" +name = "pillow" +version = "11.3.0" +description = "Python Imaging Library (Fork)" optional = false -python-versions = "*" +python-versions = ">=3.9" groups = ["main"] files = [ - {file = "ply-3.11-py2.py3-none-any.whl", hash = "sha256:096f9b8350b65ebd2fd1346b12452efe5b9607f7482813ffca50c22722a807ce"}, - {file = "ply-3.11.tar.gz", hash = "sha256:00c7c1aaa88358b9c765b6d3000c6eec0ba42abca5351b095321aef446081da3"}, + {file = "pillow-11.3.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:1b9c17fd4ace828b3003dfd1e30bff24863e0eb59b535e8f80194d9cc7ecf860"}, + {file = "pillow-11.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:65dc69160114cdd0ca0f35cb434633c75e8e7fad4cf855177a05bf38678f73ad"}, + {file = "pillow-11.3.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7107195ddc914f656c7fc8e4a5e1c25f32e9236ea3ea860f257b0436011fddd0"}, + {file = "pillow-11.3.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cc3e831b563b3114baac7ec2ee86819eb03caa1a2cef0b481a5675b59c4fe23b"}, + {file = "pillow-11.3.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f1f182ebd2303acf8c380a54f615ec883322593320a9b00438eb842c1f37ae50"}, + {file = "pillow-11.3.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4445fa62e15936a028672fd48c4c11a66d641d2c05726c7ec1f8ba6a572036ae"}, + {file = "pillow-11.3.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:71f511f6b3b91dd543282477be45a033e4845a40278fa8dcdbfdb07109bf18f9"}, + {file = "pillow-11.3.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:040a5b691b0713e1f6cbe222e0f4f74cd233421e105850ae3b3c0ceda520f42e"}, + {file = "pillow-11.3.0-cp310-cp310-win32.whl", hash = "sha256:89bd777bc6624fe4115e9fac3352c79ed60f3bb18651420635f26e643e3dd1f6"}, + {file = "pillow-11.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:19d2ff547c75b8e3ff46f4d9ef969a06c30ab2d4263a9e287733aa8b2429ce8f"}, + {file = "pillow-11.3.0-cp310-cp310-win_arm64.whl", hash = "sha256:819931d25e57b513242859ce1876c58c59dc31587847bf74cfe06b2e0cb22d2f"}, + {file = "pillow-11.3.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:1cd110edf822773368b396281a2293aeb91c90a2db00d78ea43e7e861631b722"}, + {file = "pillow-11.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9c412fddd1b77a75aa904615ebaa6001f169b26fd467b4be93aded278266b288"}, + {file = "pillow-11.3.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7d1aa4de119a0ecac0a34a9c8bde33f34022e2e8f99104e47a3ca392fd60e37d"}, + {file = "pillow-11.3.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:91da1d88226663594e3f6b4b8c3c8d85bd504117d043740a8e0ec449087cc494"}, + {file = "pillow-11.3.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:643f189248837533073c405ec2f0bb250ba54598cf80e8c1e043381a60632f58"}, + {file = "pillow-11.3.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:106064daa23a745510dabce1d84f29137a37224831d88eb4ce94bb187b1d7e5f"}, + {file = "pillow-11.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cd8ff254faf15591e724dc7c4ddb6bf4793efcbe13802a4ae3e863cd300b493e"}, + {file = "pillow-11.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:932c754c2d51ad2b2271fd01c3d121daaa35e27efae2a616f77bf164bc0b3e94"}, + {file = "pillow-11.3.0-cp311-cp311-win32.whl", hash = "sha256:b4b8f3efc8d530a1544e5962bd6b403d5f7fe8b9e08227c6b255f98ad82b4ba0"}, + {file = "pillow-11.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:1a992e86b0dd7aeb1f053cd506508c0999d710a8f07b4c791c63843fc6a807ac"}, + {file = "pillow-11.3.0-cp311-cp311-win_arm64.whl", hash = "sha256:30807c931ff7c095620fe04448e2c2fc673fcbb1ffe2a7da3fb39613489b1ddd"}, + {file = "pillow-11.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fdae223722da47b024b867c1ea0be64e0df702c5e0a60e27daad39bf960dd1e4"}, + {file = "pillow-11.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:921bd305b10e82b4d1f5e802b6850677f965d8394203d182f078873851dada69"}, + {file = "pillow-11.3.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:eb76541cba2f958032d79d143b98a3a6b3ea87f0959bbe256c0b5e416599fd5d"}, + {file = "pillow-11.3.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:67172f2944ebba3d4a7b54f2e95c786a3a50c21b88456329314caaa28cda70f6"}, + {file = "pillow-11.3.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97f07ed9f56a3b9b5f49d3661dc9607484e85c67e27f3e8be2c7d28ca032fec7"}, + {file = "pillow-11.3.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:676b2815362456b5b3216b4fd5bd89d362100dc6f4945154ff172e206a22c024"}, + {file = "pillow-11.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3e184b2f26ff146363dd07bde8b711833d7b0202e27d13540bfe2e35a323a809"}, + {file = "pillow-11.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6be31e3fc9a621e071bc17bb7de63b85cbe0bfae91bb0363c893cbe67247780d"}, + {file = "pillow-11.3.0-cp312-cp312-win32.whl", hash = "sha256:7b161756381f0918e05e7cb8a371fff367e807770f8fe92ecb20d905d0e1c149"}, + {file = "pillow-11.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:a6444696fce635783440b7f7a9fc24b3ad10a9ea3f0ab66c5905be1c19ccf17d"}, + {file = "pillow-11.3.0-cp312-cp312-win_arm64.whl", hash = "sha256:2aceea54f957dd4448264f9bf40875da0415c83eb85f55069d89c0ed436e3542"}, + {file = "pillow-11.3.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:1c627742b539bba4309df89171356fcb3cc5a9178355b2727d1b74a6cf155fbd"}, + {file = "pillow-11.3.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:30b7c02f3899d10f13d7a48163c8969e4e653f8b43416d23d13d1bbfdc93b9f8"}, + {file = "pillow-11.3.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:7859a4cc7c9295f5838015d8cc0a9c215b77e43d07a25e460f35cf516df8626f"}, + {file = "pillow-11.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ec1ee50470b0d050984394423d96325b744d55c701a439d2bd66089bff963d3c"}, + {file = "pillow-11.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7db51d222548ccfd274e4572fdbf3e810a5e66b00608862f947b163e613b67dd"}, + {file = "pillow-11.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2d6fcc902a24ac74495df63faad1884282239265c6839a0a6416d33faedfae7e"}, + {file = "pillow-11.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f0f5d8f4a08090c6d6d578351a2b91acf519a54986c055af27e7a93feae6d3f1"}, + {file = "pillow-11.3.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c37d8ba9411d6003bba9e518db0db0c58a680ab9fe5179f040b0463644bc9805"}, + {file = "pillow-11.3.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:13f87d581e71d9189ab21fe0efb5a23e9f28552d5be6979e84001d3b8505abe8"}, + {file = "pillow-11.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:023f6d2d11784a465f09fd09a34b150ea4672e85fb3d05931d89f373ab14abb2"}, + {file = "pillow-11.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:45dfc51ac5975b938e9809451c51734124e73b04d0f0ac621649821a63852e7b"}, + {file = "pillow-11.3.0-cp313-cp313-win32.whl", hash = "sha256:a4d336baed65d50d37b88ca5b60c0fa9d81e3a87d4a7930d3880d1624d5b31f3"}, + {file = "pillow-11.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:0bce5c4fd0921f99d2e858dc4d4d64193407e1b99478bc5cacecba2311abde51"}, + {file = "pillow-11.3.0-cp313-cp313-win_arm64.whl", hash = "sha256:1904e1264881f682f02b7f8167935cce37bc97db457f8e7849dc3a6a52b99580"}, + {file = "pillow-11.3.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:4c834a3921375c48ee6b9624061076bc0a32a60b5532b322cc0ea64e639dd50e"}, + {file = "pillow-11.3.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5e05688ccef30ea69b9317a9ead994b93975104a677a36a8ed8106be9260aa6d"}, + {file = "pillow-11.3.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1019b04af07fc0163e2810167918cb5add8d74674b6267616021ab558dc98ced"}, + {file = "pillow-11.3.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f944255db153ebb2b19c51fe85dd99ef0ce494123f21b9db4877ffdfc5590c7c"}, + {file = "pillow-11.3.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1f85acb69adf2aaee8b7da124efebbdb959a104db34d3a2cb0f3793dbae422a8"}, + {file = "pillow-11.3.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:05f6ecbeff5005399bb48d198f098a9b4b6bdf27b8487c7f38ca16eeb070cd59"}, + {file = "pillow-11.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a7bc6e6fd0395bc052f16b1a8670859964dbd7003bd0af2ff08342eb6e442cfe"}, + {file = "pillow-11.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:83e1b0161c9d148125083a35c1c5a89db5b7054834fd4387499e06552035236c"}, + {file = "pillow-11.3.0-cp313-cp313t-win32.whl", hash = "sha256:2a3117c06b8fb646639dce83694f2f9eac405472713fcb1ae887469c0d4f6788"}, + {file = "pillow-11.3.0-cp313-cp313t-win_amd64.whl", hash = "sha256:857844335c95bea93fb39e0fa2726b4d9d758850b34075a7e3ff4f4fa3aa3b31"}, + {file = "pillow-11.3.0-cp313-cp313t-win_arm64.whl", hash = "sha256:8797edc41f3e8536ae4b10897ee2f637235c94f27404cac7297f7b607dd0716e"}, + {file = "pillow-11.3.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:d9da3df5f9ea2a89b81bb6087177fb1f4d1c7146d583a3fe5c672c0d94e55e12"}, + {file = "pillow-11.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0b275ff9b04df7b640c59ec5a3cb113eefd3795a8df80bac69646ef699c6981a"}, + {file = "pillow-11.3.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0743841cabd3dba6a83f38a92672cccbd69af56e3e91777b0ee7f4dba4385632"}, + {file = "pillow-11.3.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2465a69cf967b8b49ee1b96d76718cd98c4e925414ead59fdf75cf0fd07df673"}, + {file = "pillow-11.3.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:41742638139424703b4d01665b807c6468e23e699e8e90cffefe291c5832b027"}, + {file = "pillow-11.3.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:93efb0b4de7e340d99057415c749175e24c8864302369e05914682ba642e5d77"}, + {file = "pillow-11.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7966e38dcd0fa11ca390aed7c6f20454443581d758242023cf36fcb319b1a874"}, + {file = "pillow-11.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:98a9afa7b9007c67ed84c57c9e0ad86a6000da96eaa638e4f8abe5b65ff83f0a"}, + {file = "pillow-11.3.0-cp314-cp314-win32.whl", hash = "sha256:02a723e6bf909e7cea0dac1b0e0310be9d7650cd66222a5f1c571455c0a45214"}, + {file = "pillow-11.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:a418486160228f64dd9e9efcd132679b7a02a5f22c982c78b6fc7dab3fefb635"}, + {file = "pillow-11.3.0-cp314-cp314-win_arm64.whl", hash = "sha256:155658efb5e044669c08896c0c44231c5e9abcaadbc5cd3648df2f7c0b96b9a6"}, + {file = "pillow-11.3.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:59a03cdf019efbfeeed910bf79c7c93255c3d54bc45898ac2a4140071b02b4ae"}, + {file = "pillow-11.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f8a5827f84d973d8636e9dc5764af4f0cf2318d26744b3d902931701b0d46653"}, + {file = "pillow-11.3.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ee92f2fd10f4adc4b43d07ec5e779932b4eb3dbfbc34790ada5a6669bc095aa6"}, + {file = "pillow-11.3.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c96d333dcf42d01f47b37e0979b6bd73ec91eae18614864622d9b87bbd5bbf36"}, + {file = "pillow-11.3.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4c96f993ab8c98460cd0c001447bff6194403e8b1d7e149ade5f00594918128b"}, + {file = "pillow-11.3.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:41342b64afeba938edb034d122b2dda5db2139b9a4af999729ba8818e0056477"}, + {file = "pillow-11.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:068d9c39a2d1b358eb9f245ce7ab1b5c3246c7c8c7d9ba58cfa5b43146c06e50"}, + {file = "pillow-11.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a1bc6ba083b145187f648b667e05a2534ecc4b9f2784c2cbe3089e44868f2b9b"}, + {file = "pillow-11.3.0-cp314-cp314t-win32.whl", hash = "sha256:118ca10c0d60b06d006be10a501fd6bbdfef559251ed31b794668ed569c87e12"}, + {file = "pillow-11.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:8924748b688aa210d79883357d102cd64690e56b923a186f35a82cbc10f997db"}, + {file = "pillow-11.3.0-cp314-cp314t-win_arm64.whl", hash = "sha256:79ea0d14d3ebad43ec77ad5272e6ff9bba5b679ef73375ea760261207fa8e0aa"}, + {file = "pillow-11.3.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:48d254f8a4c776de343051023eb61ffe818299eeac478da55227d96e241de53f"}, + {file = "pillow-11.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7aee118e30a4cf54fdd873bd3a29de51e29105ab11f9aad8c32123f58c8f8081"}, + {file = "pillow-11.3.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:23cff760a9049c502721bdb743a7cb3e03365fafcdfc2ef9784610714166e5a4"}, + {file = "pillow-11.3.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6359a3bc43f57d5b375d1ad54a0074318a0844d11b76abccf478c37c986d3cfc"}, + {file = "pillow-11.3.0-cp39-cp39-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:092c80c76635f5ecb10f3f83d76716165c96f5229addbd1ec2bdbbda7d496e06"}, + {file = "pillow-11.3.0-cp39-cp39-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cadc9e0ea0a2431124cde7e1697106471fc4c1da01530e679b2391c37d3fbb3a"}, + {file = "pillow-11.3.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:6a418691000f2a418c9135a7cf0d797c1bb7d9a485e61fe8e7722845b95ef978"}, + {file = "pillow-11.3.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:97afb3a00b65cc0804d1c7abddbf090a81eaac02768af58cbdcaaa0a931e0b6d"}, + {file = "pillow-11.3.0-cp39-cp39-win32.whl", hash = "sha256:ea944117a7974ae78059fcc1800e5d3295172bb97035c0c1d9345fca1419da71"}, + {file = "pillow-11.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:e5c5858ad8ec655450a7c7df532e9842cf8df7cc349df7225c60d5d348c8aada"}, + {file = "pillow-11.3.0-cp39-cp39-win_arm64.whl", hash = "sha256:6abdbfd3aea42be05702a8dd98832329c167ee84400a1d1f61ab11437f1717eb"}, + {file = "pillow-11.3.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:3cee80663f29e3843b68199b9d6f4f54bd1d4a6b59bdd91bceefc51238bcb967"}, + {file = "pillow-11.3.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:b5f56c3f344f2ccaf0dd875d3e180f631dc60a51b314295a3e681fe8cf851fbe"}, + {file = "pillow-11.3.0-pp310-pypy310_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e67d793d180c9df62f1f40aee3accca4829d3794c95098887edc18af4b8b780c"}, + {file = "pillow-11.3.0-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d000f46e2917c705e9fb93a3606ee4a819d1e3aa7a9b442f6444f07e77cf5e25"}, + {file = "pillow-11.3.0-pp310-pypy310_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:527b37216b6ac3a12d7838dc3bd75208ec57c1c6d11ef01902266a5a0c14fc27"}, + {file = "pillow-11.3.0-pp310-pypy310_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:be5463ac478b623b9dd3937afd7fb7ab3d79dd290a28e2b6df292dc75063eb8a"}, + {file = "pillow-11.3.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:8dc70ca24c110503e16918a658b869019126ecfe03109b754c402daff12b3d9f"}, + {file = "pillow-11.3.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7c8ec7a017ad1bd562f93dbd8505763e688d388cde6e4a010ae1486916e713e6"}, + {file = "pillow-11.3.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:9ab6ae226de48019caa8074894544af5b53a117ccb9d3b3dcb2871464c829438"}, + {file = "pillow-11.3.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fe27fb049cdcca11f11a7bfda64043c37b30e6b91f10cb5bab275806c32f6ab3"}, + {file = "pillow-11.3.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:465b9e8844e3c3519a983d58b80be3f668e2a7a5db97f2784e7079fbc9f9822c"}, + {file = "pillow-11.3.0-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5418b53c0d59b3824d05e029669efa023bbef0f3e92e75ec8428f3799487f361"}, + {file = "pillow-11.3.0-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:504b6f59505f08ae014f724b6207ff6222662aab5cc9542577fb084ed0676ac7"}, + {file = "pillow-11.3.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:c84d689db21a1c397d001aa08241044aa2069e7587b398c8cc63020390b1c1b8"}, + {file = "pillow-11.3.0.tar.gz", hash = "sha256:3828ee7586cd0b2091b6209e5ad53e20d0649bbe87164a459d0676e035e8f523"}, ] +[package.extras] +docs = ["furo", "olefile", "sphinx (>=8.2)", "sphinx-autobuild", "sphinx-copybutton", "sphinx-inline-tabs", "sphinxext-opengraph"] +fpx = ["olefile"] +mic = ["olefile"] +test-arrow = ["pyarrow"] +tests = ["check-manifest", "coverage (>=7.4.2)", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "trove-classifiers (>=2024.10.12)"] +typing = ["typing-extensions"] +xmp = ["defusedxml"] + +[[package]] +name = "pluggy" +version = "1.6.0" +description = "plugin and hook calling mechanisms for python" +optional = false +python-versions = ">=3.9" +groups = ["main", "dev"] +files = [ + {file = "pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746"}, + {file = "pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3"}, +] + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["coverage", "pytest", "pytest-benchmark"] + [[package]] name = "plyara" version = "2.2.8" @@ -3354,14 +2624,14 @@ tests = ["coverage", "pycodestyle", "pydocstyle", "pyflakes"] [[package]] name = "prompt-toolkit" -version = "3.0.51" +version = "3.0.52" description = "Library for building powerful interactive command lines in Python" optional = false python-versions = ">=3.8" groups = ["main"] files = [ - {file = "prompt_toolkit-3.0.51-py3-none-any.whl", hash = "sha256:52742911fde84e2d423e2f9a4cf1de7d7ac4e51958f648d9540e0fb8db077b07"}, - {file = "prompt_toolkit-3.0.51.tar.gz", hash = "sha256:931a162e3b27fc90c86f1b48bb1fb2c528c2761475e57c9c06de13311c7b54ed"}, + {file = "prompt_toolkit-3.0.52-py3-none-any.whl", hash = "sha256:9aac639a3bbd33284347de5ad8d68ecc044b91a762dc39b7c21095fcd6a19955"}, + {file = "prompt_toolkit-3.0.52.tar.gz", hash = "sha256:28cde192929c8e7321de85de1ddbe736f1375148b02f2e17edd840042b1be855"}, ] [package.dependencies] @@ -3369,183 +2639,278 @@ wcwidth = "*" [[package]] name = "propcache" -version = "0.3.2" +version = "0.4.1" description = "Accelerated property cache" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "propcache-0.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:22d9962a358aedbb7a2e36187ff273adeaab9743373a272976d2e348d08c7770"}, - {file = "propcache-0.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0d0fda578d1dc3f77b6b5a5dce3b9ad69a8250a891760a548df850a5e8da87f3"}, - {file = "propcache-0.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3def3da3ac3ce41562d85db655d18ebac740cb3fa4367f11a52b3da9d03a5cc3"}, - {file = "propcache-0.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9bec58347a5a6cebf239daba9bda37dffec5b8d2ce004d9fe4edef3d2815137e"}, - {file = "propcache-0.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:55ffda449a507e9fbd4aca1a7d9aa6753b07d6166140e5a18d2ac9bc49eac220"}, - {file = "propcache-0.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:64a67fb39229a8a8491dd42f864e5e263155e729c2e7ff723d6e25f596b1e8cb"}, - {file = "propcache-0.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9da1cf97b92b51253d5b68cf5a2b9e0dafca095e36b7f2da335e27dc6172a614"}, - {file = "propcache-0.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5f559e127134b07425134b4065be45b166183fdcb433cb6c24c8e4149056ad50"}, - {file = "propcache-0.3.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:aff2e4e06435d61f11a428360a932138d0ec288b0a31dd9bd78d200bd4a2b339"}, - {file = "propcache-0.3.2-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:4927842833830942a5d0a56e6f4839bc484785b8e1ce8d287359794818633ba0"}, - {file = "propcache-0.3.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:6107ddd08b02654a30fb8ad7a132021759d750a82578b94cd55ee2772b6ebea2"}, - {file = "propcache-0.3.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:70bd8b9cd6b519e12859c99f3fc9a93f375ebd22a50296c3a295028bea73b9e7"}, - {file = "propcache-0.3.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2183111651d710d3097338dd1893fcf09c9f54e27ff1a8795495a16a469cc90b"}, - {file = "propcache-0.3.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:fb075ad271405dcad8e2a7ffc9a750a3bf70e533bd86e89f0603e607b93aa64c"}, - {file = "propcache-0.3.2-cp310-cp310-win32.whl", hash = "sha256:404d70768080d3d3bdb41d0771037da19d8340d50b08e104ca0e7f9ce55fce70"}, - {file = "propcache-0.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:7435d766f978b4ede777002e6b3b6641dd229cd1da8d3d3106a45770365f9ad9"}, - {file = "propcache-0.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0b8d2f607bd8f80ddc04088bc2a037fdd17884a6fcadc47a96e334d72f3717be"}, - {file = "propcache-0.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:06766d8f34733416e2e34f46fea488ad5d60726bb9481d3cddf89a6fa2d9603f"}, - {file = "propcache-0.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a2dc1f4a1df4fecf4e6f68013575ff4af84ef6f478fe5344317a65d38a8e6dc9"}, - {file = "propcache-0.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be29c4f4810c5789cf10ddf6af80b041c724e629fa51e308a7a0fb19ed1ef7bf"}, - {file = "propcache-0.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59d61f6970ecbd8ff2e9360304d5c8876a6abd4530cb752c06586849ac8a9dc9"}, - {file = "propcache-0.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:62180e0b8dbb6b004baec00a7983e4cc52f5ada9cd11f48c3528d8cfa7b96a66"}, - {file = "propcache-0.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c144ca294a204c470f18cf4c9d78887810d04a3e2fbb30eea903575a779159df"}, - {file = "propcache-0.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c5c2a784234c28854878d68978265617aa6dc0780e53d44b4d67f3651a17a9a2"}, - {file = "propcache-0.3.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5745bc7acdafa978ca1642891b82c19238eadc78ba2aaa293c6863b304e552d7"}, - {file = "propcache-0.3.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:c0075bf773d66fa8c9d41f66cc132ecc75e5bb9dd7cce3cfd14adc5ca184cb95"}, - {file = "propcache-0.3.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5f57aa0847730daceff0497f417c9de353c575d8da3579162cc74ac294c5369e"}, - {file = "propcache-0.3.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:eef914c014bf72d18efb55619447e0aecd5fb7c2e3fa7441e2e5d6099bddff7e"}, - {file = "propcache-0.3.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:2a4092e8549031e82facf3decdbc0883755d5bbcc62d3aea9d9e185549936dcf"}, - {file = "propcache-0.3.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:85871b050f174bc0bfb437efbdb68aaf860611953ed12418e4361bc9c392749e"}, - {file = "propcache-0.3.2-cp311-cp311-win32.whl", hash = "sha256:36c8d9b673ec57900c3554264e630d45980fd302458e4ac801802a7fd2ef7897"}, - {file = "propcache-0.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:e53af8cb6a781b02d2ea079b5b853ba9430fcbe18a8e3ce647d5982a3ff69f39"}, - {file = "propcache-0.3.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8de106b6c84506b31c27168582cd3cb3000a6412c16df14a8628e5871ff83c10"}, - {file = "propcache-0.3.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:28710b0d3975117239c76600ea351934ac7b5ff56e60953474342608dbbb6154"}, - {file = "propcache-0.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce26862344bdf836650ed2487c3d724b00fbfec4233a1013f597b78c1cb73615"}, - {file = "propcache-0.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bca54bd347a253af2cf4544bbec232ab982f4868de0dd684246b67a51bc6b1db"}, - {file = "propcache-0.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:55780d5e9a2ddc59711d727226bb1ba83a22dd32f64ee15594b9392b1f544eb1"}, - {file = "propcache-0.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:035e631be25d6975ed87ab23153db6a73426a48db688070d925aa27e996fe93c"}, - {file = "propcache-0.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee6f22b6eaa39297c751d0e80c0d3a454f112f5c6481214fcf4c092074cecd67"}, - {file = "propcache-0.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7ca3aee1aa955438c4dba34fc20a9f390e4c79967257d830f137bd5a8a32ed3b"}, - {file = "propcache-0.3.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7a4f30862869fa2b68380d677cc1c5fcf1e0f2b9ea0cf665812895c75d0ca3b8"}, - {file = "propcache-0.3.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:b77ec3c257d7816d9f3700013639db7491a434644c906a2578a11daf13176251"}, - {file = "propcache-0.3.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:cab90ac9d3f14b2d5050928483d3d3b8fb6b4018893fc75710e6aa361ecb2474"}, - {file = "propcache-0.3.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:0b504d29f3c47cf6b9e936c1852246c83d450e8e063d50562115a6be6d3a2535"}, - {file = "propcache-0.3.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:ce2ac2675a6aa41ddb2a0c9cbff53780a617ac3d43e620f8fd77ba1c84dcfc06"}, - {file = "propcache-0.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:62b4239611205294cc433845b914131b2a1f03500ff3c1ed093ed216b82621e1"}, - {file = "propcache-0.3.2-cp312-cp312-win32.whl", hash = "sha256:df4a81b9b53449ebc90cc4deefb052c1dd934ba85012aa912c7ea7b7e38b60c1"}, - {file = "propcache-0.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:7046e79b989d7fe457bb755844019e10f693752d169076138abf17f31380800c"}, - {file = "propcache-0.3.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ca592ed634a73ca002967458187109265e980422116c0a107cf93d81f95af945"}, - {file = "propcache-0.3.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9ecb0aad4020e275652ba3975740f241bd12a61f1a784df044cf7477a02bc252"}, - {file = "propcache-0.3.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7f08f1cc28bd2eade7a8a3d2954ccc673bb02062e3e7da09bc75d843386b342f"}, - {file = "propcache-0.3.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1a342c834734edb4be5ecb1e9fb48cb64b1e2320fccbd8c54bf8da8f2a84c33"}, - {file = "propcache-0.3.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8a544caaae1ac73f1fecfae70ded3e93728831affebd017d53449e3ac052ac1e"}, - {file = "propcache-0.3.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:310d11aa44635298397db47a3ebce7db99a4cc4b9bbdfcf6c98a60c8d5261cf1"}, - {file = "propcache-0.3.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c1396592321ac83157ac03a2023aa6cc4a3cc3cfdecb71090054c09e5a7cce3"}, - {file = "propcache-0.3.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8cabf5b5902272565e78197edb682017d21cf3b550ba0460ee473753f28d23c1"}, - {file = "propcache-0.3.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0a2f2235ac46a7aa25bdeb03a9e7060f6ecbd213b1f9101c43b3090ffb971ef6"}, - {file = "propcache-0.3.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:92b69e12e34869a6970fd2f3da91669899994b47c98f5d430b781c26f1d9f387"}, - {file = "propcache-0.3.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:54e02207c79968ebbdffc169591009f4474dde3b4679e16634d34c9363ff56b4"}, - {file = "propcache-0.3.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4adfb44cb588001f68c5466579d3f1157ca07f7504fc91ec87862e2b8e556b88"}, - {file = "propcache-0.3.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:fd3e6019dc1261cd0291ee8919dd91fbab7b169bb76aeef6c716833a3f65d206"}, - {file = "propcache-0.3.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4c181cad81158d71c41a2bce88edce078458e2dd5ffee7eddd6b05da85079f43"}, - {file = "propcache-0.3.2-cp313-cp313-win32.whl", hash = "sha256:8a08154613f2249519e549de2330cf8e2071c2887309a7b07fb56098f5170a02"}, - {file = "propcache-0.3.2-cp313-cp313-win_amd64.whl", hash = "sha256:e41671f1594fc4ab0a6dec1351864713cb3a279910ae8b58f884a88a0a632c05"}, - {file = "propcache-0.3.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:9a3cf035bbaf035f109987d9d55dc90e4b0e36e04bbbb95af3055ef17194057b"}, - {file = "propcache-0.3.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:156c03d07dc1323d8dacaa221fbe028c5c70d16709cdd63502778e6c3ccca1b0"}, - {file = "propcache-0.3.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:74413c0ba02ba86f55cf60d18daab219f7e531620c15f1e23d95563f505efe7e"}, - {file = "propcache-0.3.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f066b437bb3fa39c58ff97ab2ca351db465157d68ed0440abecb21715eb24b28"}, - {file = "propcache-0.3.2-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f1304b085c83067914721e7e9d9917d41ad87696bf70f0bc7dee450e9c71ad0a"}, - {file = "propcache-0.3.2-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ab50cef01b372763a13333b4e54021bdcb291fc9a8e2ccb9c2df98be51bcde6c"}, - {file = "propcache-0.3.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fad3b2a085ec259ad2c2842666b2a0a49dea8463579c606426128925af1ed725"}, - {file = "propcache-0.3.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:261fa020c1c14deafd54c76b014956e2f86991af198c51139faf41c4d5e83892"}, - {file = "propcache-0.3.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:46d7f8aa79c927e5f987ee3a80205c987717d3659f035c85cf0c3680526bdb44"}, - {file = "propcache-0.3.2-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:6d8f3f0eebf73e3c0ff0e7853f68be638b4043c65a70517bb575eff54edd8dbe"}, - {file = "propcache-0.3.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:03c89c1b14a5452cf15403e291c0ccd7751d5b9736ecb2c5bab977ad6c5bcd81"}, - {file = "propcache-0.3.2-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:0cc17efde71e12bbaad086d679ce575268d70bc123a5a71ea7ad76f70ba30bba"}, - {file = "propcache-0.3.2-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:acdf05d00696bc0447e278bb53cb04ca72354e562cf88ea6f9107df8e7fd9770"}, - {file = "propcache-0.3.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4445542398bd0b5d32df908031cb1b30d43ac848e20470a878b770ec2dcc6330"}, - {file = "propcache-0.3.2-cp313-cp313t-win32.whl", hash = "sha256:f86e5d7cd03afb3a1db8e9f9f6eff15794e79e791350ac48a8c924e6f439f394"}, - {file = "propcache-0.3.2-cp313-cp313t-win_amd64.whl", hash = "sha256:9704bedf6e7cbe3c65eca4379a9b53ee6a83749f047808cbb5044d40d7d72198"}, - {file = "propcache-0.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a7fad897f14d92086d6b03fdd2eb844777b0c4d7ec5e3bac0fbae2ab0602bbe5"}, - {file = "propcache-0.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1f43837d4ca000243fd7fd6301947d7cb93360d03cd08369969450cc6b2ce3b4"}, - {file = "propcache-0.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:261df2e9474a5949c46e962065d88eb9b96ce0f2bd30e9d3136bcde84befd8f2"}, - {file = "propcache-0.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e514326b79e51f0a177daab1052bc164d9d9e54133797a3a58d24c9c87a3fe6d"}, - {file = "propcache-0.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d4a996adb6904f85894570301939afeee65f072b4fd265ed7e569e8d9058e4ec"}, - {file = "propcache-0.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:76cace5d6b2a54e55b137669b30f31aa15977eeed390c7cbfb1dafa8dfe9a701"}, - {file = "propcache-0.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31248e44b81d59d6addbb182c4720f90b44e1efdc19f58112a3c3a1615fb47ef"}, - {file = "propcache-0.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abb7fa19dbf88d3857363e0493b999b8011eea856b846305d8c0512dfdf8fbb1"}, - {file = "propcache-0.3.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:d81ac3ae39d38588ad0549e321e6f773a4e7cc68e7751524a22885d5bbadf886"}, - {file = "propcache-0.3.2-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:cc2782eb0f7a16462285b6f8394bbbd0e1ee5f928034e941ffc444012224171b"}, - {file = "propcache-0.3.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:db429c19a6c7e8a1c320e6a13c99799450f411b02251fb1b75e6217cf4a14fcb"}, - {file = "propcache-0.3.2-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:21d8759141a9e00a681d35a1f160892a36fb6caa715ba0b832f7747da48fb6ea"}, - {file = "propcache-0.3.2-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:2ca6d378f09adb13837614ad2754fa8afaee330254f404299611bce41a8438cb"}, - {file = "propcache-0.3.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:34a624af06c048946709f4278b4176470073deda88d91342665d95f7c6270fbe"}, - {file = "propcache-0.3.2-cp39-cp39-win32.whl", hash = "sha256:4ba3fef1c30f306b1c274ce0b8baaa2c3cdd91f645c48f06394068f37d3837a1"}, - {file = "propcache-0.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:7a2368eed65fc69a7a7a40b27f22e85e7627b74216f0846b04ba5c116e191ec9"}, - {file = "propcache-0.3.2-py3-none-any.whl", hash = "sha256:98f1ec44fb675f5052cccc8e609c46ed23a35a1cfd18545ad4e29002d858a43f"}, - {file = "propcache-0.3.2.tar.gz", hash = "sha256:20d7d62e4e7ef05f221e0db2856b979540686342e7dd9973b815599c7057e168"}, + {file = "propcache-0.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c2d1fa3201efaf55d730400d945b5b3ab6e672e100ba0f9a409d950ab25d7db"}, + {file = "propcache-0.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1eb2994229cc8ce7fe9b3db88f5465f5fd8651672840b2e426b88cdb1a30aac8"}, + {file = "propcache-0.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:66c1f011f45a3b33d7bcb22daed4b29c0c9e2224758b6be00686731e1b46f925"}, + {file = "propcache-0.4.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9a52009f2adffe195d0b605c25ec929d26b36ef986ba85244891dee3b294df21"}, + {file = "propcache-0.4.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5d4e2366a9c7b837555cf02fb9be2e3167d333aff716332ef1b7c3a142ec40c5"}, + {file = "propcache-0.4.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:9d2b6caef873b4f09e26ea7e33d65f42b944837563a47a94719cc3544319a0db"}, + {file = "propcache-0.4.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2b16ec437a8c8a965ecf95739448dd938b5c7f56e67ea009f4300d8df05f32b7"}, + {file = "propcache-0.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:296f4c8ed03ca7476813fe666c9ea97869a8d7aec972618671b33a38a5182ef4"}, + {file = "propcache-0.4.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:1f0978529a418ebd1f49dad413a2b68af33f85d5c5ca5c6ca2a3bed375a7ac60"}, + {file = "propcache-0.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fd138803047fb4c062b1c1dd95462f5209456bfab55c734458f15d11da288f8f"}, + {file = "propcache-0.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8c9b3cbe4584636d72ff556d9036e0c9317fa27b3ac1f0f558e7e84d1c9c5900"}, + {file = "propcache-0.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f93243fdc5657247533273ac4f86ae106cc6445a0efacb9a1bfe982fcfefd90c"}, + {file = "propcache-0.4.1-cp310-cp310-win32.whl", hash = "sha256:a0ee98db9c5f80785b266eb805016e36058ac72c51a064040f2bc43b61101cdb"}, + {file = "propcache-0.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:1cdb7988c4e5ac7f6d175a28a9aa0c94cb6f2ebe52756a3c0cda98d2809a9e37"}, + {file = "propcache-0.4.1-cp310-cp310-win_arm64.whl", hash = "sha256:d82ad62b19645419fe79dd63b3f9253e15b30e955c0170e5cebc350c1844e581"}, + {file = "propcache-0.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:60a8fda9644b7dfd5dece8c61d8a85e271cb958075bfc4e01083c148b61a7caf"}, + {file = "propcache-0.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c30b53e7e6bda1d547cabb47c825f3843a0a1a42b0496087bb58d8fedf9f41b5"}, + {file = "propcache-0.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6918ecbd897443087a3b7cd978d56546a812517dcaaca51b49526720571fa93e"}, + {file = "propcache-0.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3d902a36df4e5989763425a8ab9e98cd8ad5c52c823b34ee7ef307fd50582566"}, + {file = "propcache-0.4.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a9695397f85973bb40427dedddf70d8dc4a44b22f1650dd4af9eedf443d45165"}, + {file = "propcache-0.4.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2bb07ffd7eaad486576430c89f9b215f9e4be68c4866a96e97db9e97fead85dc"}, + {file = "propcache-0.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fd6f30fdcf9ae2a70abd34da54f18da086160e4d7d9251f81f3da0ff84fc5a48"}, + {file = "propcache-0.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fc38cba02d1acba4e2869eef1a57a43dfbd3d49a59bf90dda7444ec2be6a5570"}, + {file = "propcache-0.4.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:67fad6162281e80e882fb3ec355398cf72864a54069d060321f6cd0ade95fe85"}, + {file = "propcache-0.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f10207adf04d08bec185bae14d9606a1444715bc99180f9331c9c02093e1959e"}, + {file = "propcache-0.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e9b0d8d0845bbc4cfcdcbcdbf5086886bc8157aa963c31c777ceff7846c77757"}, + {file = "propcache-0.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:981333cb2f4c1896a12f4ab92a9cc8f09ea664e9b7dbdc4eff74627af3a11c0f"}, + {file = "propcache-0.4.1-cp311-cp311-win32.whl", hash = "sha256:f1d2f90aeec838a52f1c1a32fe9a619fefd5e411721a9117fbf82aea638fe8a1"}, + {file = "propcache-0.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:364426a62660f3f699949ac8c621aad6977be7126c5807ce48c0aeb8e7333ea6"}, + {file = "propcache-0.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:e53f3a38d3510c11953f3e6a33f205c6d1b001129f972805ca9b42fc308bc239"}, + {file = "propcache-0.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e153e9cd40cc8945138822807139367f256f89c6810c2634a4f6902b52d3b4e2"}, + {file = "propcache-0.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cd547953428f7abb73c5ad82cbb32109566204260d98e41e5dfdc682eb7f8403"}, + {file = "propcache-0.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f048da1b4f243fc44f205dfd320933a951b8d89e0afd4c7cacc762a8b9165207"}, + {file = "propcache-0.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ec17c65562a827bba85e3872ead335f95405ea1674860d96483a02f5c698fa72"}, + {file = "propcache-0.4.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:405aac25c6394ef275dee4c709be43745d36674b223ba4eb7144bf4d691b7367"}, + {file = "propcache-0.4.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0013cb6f8dde4b2a2f66903b8ba740bdfe378c943c4377a200551ceb27f379e4"}, + {file = "propcache-0.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:15932ab57837c3368b024473a525e25d316d8353016e7cc0e5ba9eb343fbb1cf"}, + {file = "propcache-0.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:031dce78b9dc099f4c29785d9cf5577a3faf9ebf74ecbd3c856a7b92768c3df3"}, + {file = "propcache-0.4.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ab08df6c9a035bee56e31af99be621526bd237bea9f32def431c656b29e41778"}, + {file = "propcache-0.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4d7af63f9f93fe593afbf104c21b3b15868efb2c21d07d8732c0c4287e66b6a6"}, + {file = "propcache-0.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:cfc27c945f422e8b5071b6e93169679e4eb5bf73bbcbf1ba3ae3a83d2f78ebd9"}, + {file = "propcache-0.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:35c3277624a080cc6ec6f847cbbbb5b49affa3598c4535a0a4682a697aaa5c75"}, + {file = "propcache-0.4.1-cp312-cp312-win32.whl", hash = "sha256:671538c2262dadb5ba6395e26c1731e1d52534bfe9ae56d0b5573ce539266aa8"}, + {file = "propcache-0.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:cb2d222e72399fcf5890d1d5cc1060857b9b236adff2792ff48ca2dfd46c81db"}, + {file = "propcache-0.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:204483131fb222bdaaeeea9f9e6c6ed0cac32731f75dfc1d4a567fc1926477c1"}, + {file = "propcache-0.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:43eedf29202c08550aac1d14e0ee619b0430aaef78f85864c1a892294fbc28cf"}, + {file = "propcache-0.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d62cdfcfd89ccb8de04e0eda998535c406bf5e060ffd56be6c586cbcc05b3311"}, + {file = "propcache-0.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cae65ad55793da34db5f54e4029b89d3b9b9490d8abe1b4c7ab5d4b8ec7ebf74"}, + {file = "propcache-0.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:333ddb9031d2704a301ee3e506dc46b1fe5f294ec198ed6435ad5b6a085facfe"}, + {file = "propcache-0.4.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:fd0858c20f078a32cf55f7e81473d96dcf3b93fd2ccdb3d40fdf54b8573df3af"}, + {file = "propcache-0.4.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:678ae89ebc632c5c204c794f8dab2837c5f159aeb59e6ed0539500400577298c"}, + {file = "propcache-0.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d472aeb4fbf9865e0c6d622d7f4d54a4e101a89715d8904282bb5f9a2f476c3f"}, + {file = "propcache-0.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4d3df5fa7e36b3225954fba85589da77a0fe6a53e3976de39caf04a0db4c36f1"}, + {file = "propcache-0.4.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:ee17f18d2498f2673e432faaa71698032b0127ebf23ae5974eeaf806c279df24"}, + {file = "propcache-0.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:580e97762b950f993ae618e167e7be9256b8353c2dcd8b99ec100eb50f5286aa"}, + {file = "propcache-0.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:501d20b891688eb8e7aa903021f0b72d5a55db40ffaab27edefd1027caaafa61"}, + {file = "propcache-0.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a0bd56e5b100aef69bd8562b74b46254e7c8812918d3baa700c8a8009b0af66"}, + {file = "propcache-0.4.1-cp313-cp313-win32.whl", hash = "sha256:bcc9aaa5d80322bc2fb24bb7accb4a30f81e90ab8d6ba187aec0744bc302ad81"}, + {file = "propcache-0.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:381914df18634f5494334d201e98245c0596067504b9372d8cf93f4bb23e025e"}, + {file = "propcache-0.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:8873eb4460fd55333ea49b7d189749ecf6e55bf85080f11b1c4530ed3034cba1"}, + {file = "propcache-0.4.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:92d1935ee1f8d7442da9c0c4fa7ac20d07e94064184811b685f5c4fada64553b"}, + {file = "propcache-0.4.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:473c61b39e1460d386479b9b2f337da492042447c9b685f28be4f74d3529e566"}, + {file = "propcache-0.4.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:c0ef0aaafc66fbd87842a3fe3902fd889825646bc21149eafe47be6072725835"}, + {file = "propcache-0.4.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f95393b4d66bfae908c3ca8d169d5f79cd65636ae15b5e7a4f6e67af675adb0e"}, + {file = "propcache-0.4.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c07fda85708bc48578467e85099645167a955ba093be0a2dcba962195676e859"}, + {file = "propcache-0.4.1-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:af223b406d6d000830c6f65f1e6431783fc3f713ba3e6cc8c024d5ee96170a4b"}, + {file = "propcache-0.4.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a78372c932c90ee474559c5ddfffd718238e8673c340dc21fe45c5b8b54559a0"}, + {file = "propcache-0.4.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:564d9f0d4d9509e1a870c920a89b2fec951b44bf5ba7d537a9e7c1ccec2c18af"}, + {file = "propcache-0.4.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:17612831fda0138059cc5546f4d12a2aacfb9e47068c06af35c400ba58ba7393"}, + {file = "propcache-0.4.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:41a89040cb10bd345b3c1a873b2bf36413d48da1def52f268a055f7398514874"}, + {file = "propcache-0.4.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e35b88984e7fa64aacecea39236cee32dd9bd8c55f57ba8a75cf2399553f9bd7"}, + {file = "propcache-0.4.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6f8b465489f927b0df505cbe26ffbeed4d6d8a2bbc61ce90eb074ff129ef0ab1"}, + {file = "propcache-0.4.1-cp313-cp313t-win32.whl", hash = "sha256:2ad890caa1d928c7c2965b48f3a3815c853180831d0e5503d35cf00c472f4717"}, + {file = "propcache-0.4.1-cp313-cp313t-win_amd64.whl", hash = "sha256:f7ee0e597f495cf415bcbd3da3caa3bd7e816b74d0d52b8145954c5e6fd3ff37"}, + {file = "propcache-0.4.1-cp313-cp313t-win_arm64.whl", hash = "sha256:929d7cbe1f01bb7baffb33dc14eb5691c95831450a26354cd210a8155170c93a"}, + {file = "propcache-0.4.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3f7124c9d820ba5548d431afb4632301acf965db49e666aa21c305cbe8c6de12"}, + {file = "propcache-0.4.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:c0d4b719b7da33599dfe3b22d3db1ef789210a0597bc650b7cee9c77c2be8c5c"}, + {file = "propcache-0.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9f302f4783709a78240ebc311b793f123328716a60911d667e0c036bc5dcbded"}, + {file = "propcache-0.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c80ee5802e3fb9ea37938e7eecc307fb984837091d5fd262bb37238b1ae97641"}, + {file = "propcache-0.4.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ed5a841e8bb29a55fb8159ed526b26adc5bdd7e8bd7bf793ce647cb08656cdf4"}, + {file = "propcache-0.4.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:55c72fd6ea2da4c318e74ffdf93c4fe4e926051133657459131a95c846d16d44"}, + {file = "propcache-0.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8326e144341460402713f91df60ade3c999d601e7eb5ff8f6f7862d54de0610d"}, + {file = "propcache-0.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:060b16ae65bc098da7f6d25bf359f1f31f688384858204fe5d652979e0015e5b"}, + {file = "propcache-0.4.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:89eb3fa9524f7bec9de6e83cf3faed9d79bffa560672c118a96a171a6f55831e"}, + {file = "propcache-0.4.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:dee69d7015dc235f526fe80a9c90d65eb0039103fe565776250881731f06349f"}, + {file = "propcache-0.4.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:5558992a00dfd54ccbc64a32726a3357ec93825a418a401f5cc67df0ac5d9e49"}, + {file = "propcache-0.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c9b822a577f560fbd9554812526831712c1436d2c046cedee4c3796d3543b144"}, + {file = "propcache-0.4.1-cp314-cp314-win32.whl", hash = "sha256:ab4c29b49d560fe48b696cdcb127dd36e0bc2472548f3bf56cc5cb3da2b2984f"}, + {file = "propcache-0.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:5a103c3eb905fcea0ab98be99c3a9a5ab2de60228aa5aceedc614c0281cf6153"}, + {file = "propcache-0.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:74c1fb26515153e482e00177a1ad654721bf9207da8a494a0c05e797ad27b992"}, + {file = "propcache-0.4.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:824e908bce90fb2743bd6b59db36eb4f45cd350a39637c9f73b1c1ea66f5b75f"}, + {file = "propcache-0.4.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:c2b5e7db5328427c57c8e8831abda175421b709672f6cfc3d630c3b7e2146393"}, + {file = "propcache-0.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6f6ff873ed40292cd4969ef5310179afd5db59fdf055897e282485043fc80ad0"}, + {file = "propcache-0.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:49a2dc67c154db2c1463013594c458881a069fcf98940e61a0569016a583020a"}, + {file = "propcache-0.4.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:005f08e6a0529984491e37d8dbc3dd86f84bd78a8ceb5fa9a021f4c48d4984be"}, + {file = "propcache-0.4.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5c3310452e0d31390da9035c348633b43d7e7feb2e37be252be6da45abd1abcc"}, + {file = "propcache-0.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c3c70630930447f9ef1caac7728c8ad1c56bc5015338b20fed0d08ea2480b3a"}, + {file = "propcache-0.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8e57061305815dfc910a3634dcf584f08168a8836e6999983569f51a8544cd89"}, + {file = "propcache-0.4.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:521a463429ef54143092c11a77e04056dd00636f72e8c45b70aaa3140d639726"}, + {file = "propcache-0.4.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:120c964da3fdc75e3731aa392527136d4ad35868cc556fd09bb6d09172d9a367"}, + {file = "propcache-0.4.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:d8f353eb14ee3441ee844ade4277d560cdd68288838673273b978e3d6d2c8f36"}, + {file = "propcache-0.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ab2943be7c652f09638800905ee1bab2c544e537edb57d527997a24c13dc1455"}, + {file = "propcache-0.4.1-cp314-cp314t-win32.whl", hash = "sha256:05674a162469f31358c30bcaa8883cb7829fa3110bf9c0991fe27d7896c42d85"}, + {file = "propcache-0.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:990f6b3e2a27d683cb7602ed6c86f15ee6b43b1194736f9baaeb93d0016633b1"}, + {file = "propcache-0.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:ecef2343af4cc68e05131e45024ba34f6095821988a9d0a02aa7c73fcc448aa9"}, + {file = "propcache-0.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3d233076ccf9e450c8b3bc6720af226b898ef5d051a2d145f7d765e6e9f9bcff"}, + {file = "propcache-0.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:357f5bb5c377a82e105e44bd3d52ba22b616f7b9773714bff93573988ef0a5fb"}, + {file = "propcache-0.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cbc3b6dfc728105b2a57c06791eb07a94229202ea75c59db644d7d496b698cac"}, + {file = "propcache-0.4.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:182b51b421f0501952d938dc0b0eb45246a5b5153c50d42b495ad5fb7517c888"}, + {file = "propcache-0.4.1-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4b536b39c5199b96fc6245eb5fb796c497381d3942f169e44e8e392b29c9ebcc"}, + {file = "propcache-0.4.1-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:db65d2af507bbfbdcedb254a11149f894169d90488dd3e7190f7cdcb2d6cd57a"}, + {file = "propcache-0.4.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fd2dbc472da1f772a4dae4fa24be938a6c544671a912e30529984dd80400cd88"}, + {file = "propcache-0.4.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:daede9cd44e0f8bdd9e6cc9a607fc81feb80fae7a5fc6cecaff0e0bb32e42d00"}, + {file = "propcache-0.4.1-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:71b749281b816793678ae7f3d0d84bd36e694953822eaad408d682efc5ca18e0"}, + {file = "propcache-0.4.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:0002004213ee1f36cfb3f9a42b5066100c44276b9b72b4e1504cddd3d692e86e"}, + {file = "propcache-0.4.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:fe49d0a85038f36ba9e3ffafa1103e61170b28e95b16622e11be0a0ea07c6781"}, + {file = "propcache-0.4.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:99d43339c83aaf4d32bda60928231848eee470c6bda8d02599cc4cebe872d183"}, + {file = "propcache-0.4.1-cp39-cp39-win32.whl", hash = "sha256:a129e76735bc792794d5177069691c3217898b9f5cee2b2661471e52ffe13f19"}, + {file = "propcache-0.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:948dab269721ae9a87fd16c514a0a2c2a1bdb23a9a61b969b0f9d9ee2968546f"}, + {file = "propcache-0.4.1-cp39-cp39-win_arm64.whl", hash = "sha256:5fd37c406dd6dc85aa743e214cef35dc54bbdd1419baac4f6ae5e5b1a2976938"}, + {file = "propcache-0.4.1-py3-none-any.whl", hash = "sha256:af2a6052aeb6cf17d3e46ee169099044fd8224cbaf75c76a2ef596e8163e2237"}, + {file = "propcache-0.4.1.tar.gz", hash = "sha256:f48107a8c637e80362555f37ecf49abe20370e557cc4ab374f04ec4423c97c3d"}, ] [[package]] name = "protobuf" -version = "5.29.5" +version = "6.31.1" description = "" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["main"] files = [ - {file = "protobuf-5.29.5-cp310-abi3-win32.whl", hash = "sha256:3f1c6468a2cfd102ff4703976138844f78ebd1fb45f49011afc5139e9e283079"}, - {file = "protobuf-5.29.5-cp310-abi3-win_amd64.whl", hash = "sha256:3f76e3a3675b4a4d867b52e4a5f5b78a2ef9565549d4037e06cf7b0942b1d3fc"}, - {file = "protobuf-5.29.5-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:e38c5add5a311f2a6eb0340716ef9b039c1dfa428b28f25a7838ac329204a671"}, - {file = "protobuf-5.29.5-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:fa18533a299d7ab6c55a238bf8629311439995f2e7eca5caaff08663606e9015"}, - {file = "protobuf-5.29.5-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:63848923da3325e1bf7e9003d680ce6e14b07e55d0473253a690c3a8b8fd6e61"}, - {file = "protobuf-5.29.5-cp38-cp38-win32.whl", hash = "sha256:ef91363ad4faba7b25d844ef1ada59ff1604184c0bcd8b39b8a6bef15e1af238"}, - {file = "protobuf-5.29.5-cp38-cp38-win_amd64.whl", hash = "sha256:7318608d56b6402d2ea7704ff1e1e4597bee46d760e7e4dd42a3d45e24b87f2e"}, - {file = "protobuf-5.29.5-cp39-cp39-win32.whl", hash = "sha256:6f642dc9a61782fa72b90878af134c5afe1917c89a568cd3476d758d3c3a0736"}, - {file = "protobuf-5.29.5-cp39-cp39-win_amd64.whl", hash = "sha256:470f3af547ef17847a28e1f47200a1cbf0ba3ff57b7de50d22776607cd2ea353"}, - {file = "protobuf-5.29.5-py3-none-any.whl", hash = "sha256:6cf42630262c59b2d8de33954443d94b746c952b01434fc58a417fdbd2e84bd5"}, - {file = "protobuf-5.29.5.tar.gz", hash = "sha256:bc1463bafd4b0929216c35f437a8e28731a2b7fe3d98bb77a600efced5a15c84"}, + {file = "protobuf-6.31.1-cp310-abi3-win32.whl", hash = "sha256:7fa17d5a29c2e04b7d90e5e32388b8bfd0e7107cd8e616feef7ed3fa6bdab5c9"}, + {file = "protobuf-6.31.1-cp310-abi3-win_amd64.whl", hash = "sha256:426f59d2964864a1a366254fa703b8632dcec0790d8862d30034d8245e1cd447"}, + {file = "protobuf-6.31.1-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:6f1227473dc43d44ed644425268eb7c2e488ae245d51c6866d19fe158e207402"}, + {file = "protobuf-6.31.1-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:a40fc12b84c154884d7d4c4ebd675d5b3b5283e155f324049ae396b95ddebc39"}, + {file = "protobuf-6.31.1-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:4ee898bf66f7a8b0bd21bce523814e6fbd8c6add948045ce958b73af7e8878c6"}, + {file = "protobuf-6.31.1-cp39-cp39-win32.whl", hash = "sha256:0414e3aa5a5f3ff423828e1e6a6e907d6c65c1d5b7e6e975793d5590bdeecc16"}, + {file = "protobuf-6.31.1-cp39-cp39-win_amd64.whl", hash = "sha256:8764cf4587791e7564051b35524b72844f845ad0bb011704c3736cce762d8fe9"}, + {file = "protobuf-6.31.1-py3-none-any.whl", hash = "sha256:720a6c7e6b77288b85063569baae8536671b39f15cc22037ec7045658d80489e"}, + {file = "protobuf-6.31.1.tar.gz", hash = "sha256:d8cac4c982f0b957a4dc73a80e2ea24fab08e679c0de9deb835f4a12d69aca9a"}, ] [[package]] name = "psutil" -version = "7.0.0" -description = "Cross-platform lib for process and system monitoring in Python. NOTE: the syntax of this script MUST be kept compatible with Python 2.7." +version = "7.1.0" +description = "Cross-platform lib for process and system monitoring." optional = false python-versions = ">=3.6" groups = ["main"] markers = "sys_platform != \"cygwin\"" files = [ - {file = "psutil-7.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:101d71dc322e3cffd7cea0650b09b3d08b8e7c4109dd6809fe452dfd00e58b25"}, - {file = "psutil-7.0.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:39db632f6bb862eeccf56660871433e111b6ea58f2caea825571951d4b6aa3da"}, - {file = "psutil-7.0.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fcee592b4c6f146991ca55919ea3d1f8926497a713ed7faaf8225e174581e91"}, - {file = "psutil-7.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b1388a4f6875d7e2aff5c4ca1cc16c545ed41dd8bb596cefea80111db353a34"}, - {file = "psutil-7.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5f098451abc2828f7dc6b58d44b532b22f2088f4999a937557b603ce72b1993"}, - {file = "psutil-7.0.0-cp36-cp36m-win32.whl", hash = "sha256:84df4eb63e16849689f76b1ffcb36db7b8de703d1bc1fe41773db487621b6c17"}, - {file = "psutil-7.0.0-cp36-cp36m-win_amd64.whl", hash = "sha256:1e744154a6580bc968a0195fd25e80432d3afec619daf145b9e5ba16cc1d688e"}, - {file = "psutil-7.0.0-cp37-abi3-win32.whl", hash = "sha256:ba3fcef7523064a6c9da440fc4d6bd07da93ac726b5733c29027d7dc95b39d99"}, - {file = "psutil-7.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:4cf3d4eb1aa9b348dec30105c55cd9b7d4629285735a102beb4441e38db90553"}, - {file = "psutil-7.0.0.tar.gz", hash = "sha256:7be9c3eba38beccb6495ea33afd982a44074b78f28c434a1f51cc07fd315c456"}, + {file = "psutil-7.1.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:76168cef4397494250e9f4e73eb3752b146de1dd950040b29186d0cce1d5ca13"}, + {file = "psutil-7.1.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:5d007560c8c372efdff9e4579c2846d71de737e4605f611437255e81efcca2c5"}, + {file = "psutil-7.1.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:22e4454970b32472ce7deaa45d045b34d3648ce478e26a04c7e858a0a6e75ff3"}, + {file = "psutil-7.1.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c70e113920d51e89f212dd7be06219a9b88014e63a4cec69b684c327bc474e3"}, + {file = "psutil-7.1.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7d4a113425c037300de3ac8b331637293da9be9713855c4fc9d2d97436d7259d"}, + {file = "psutil-7.1.0-cp37-abi3-win32.whl", hash = "sha256:09ad740870c8d219ed8daae0ad3b726d3bf9a028a198e7f3080f6a1888b99bca"}, + {file = "psutil-7.1.0-cp37-abi3-win_amd64.whl", hash = "sha256:57f5e987c36d3146c0dd2528cd42151cf96cd359b9d67cfff836995cc5df9a3d"}, + {file = "psutil-7.1.0-cp37-abi3-win_arm64.whl", hash = "sha256:6937cb68133e7c97b6cc9649a570c9a18ba0efebed46d8c5dae4c07fa1b67a07"}, + {file = "psutil-7.1.0.tar.gz", hash = "sha256:655708b3c069387c8b77b072fc429a57d0e214221d01c0a772df7dfedcb3bcd2"}, ] [package.extras] -dev = ["abi3audit", "black (==24.10.0)", "check-manifest", "coverage", "packaging", "pylint", "pyperf", "pypinfo", "pytest", "pytest-cov", "pytest-xdist", "requests", "rstcheck", "ruff", "setuptools", "sphinx", "sphinx_rtd_theme", "toml-sort", "twine", "virtualenv", "vulture", "wheel"] -test = ["pytest", "pytest-xdist", "setuptools"] +dev = ["abi3audit", "black", "check-manifest", "coverage", "packaging", "pylint", "pyperf", "pypinfo", "pyreadline", "pytest", "pytest-cov", "pytest-instafail", "pytest-subtests", "pytest-xdist", "pywin32", "requests", "rstcheck", "ruff", "setuptools", "sphinx", "sphinx_rtd_theme", "toml-sort", "twine", "virtualenv", "vulture", "wheel", "wheel", "wmi"] +test = ["pytest", "pytest-instafail", "pytest-subtests", "pytest-xdist", "pywin32", "setuptools", "wheel", "wmi"] [[package]] name = "psycopg" -version = "3.2.9" +version = "3.2.10" description = "PostgreSQL database adapter for Python" optional = false python-versions = ">=3.8" groups = ["main"] files = [ - {file = "psycopg-3.2.9-py3-none-any.whl", hash = "sha256:01a8dadccdaac2123c916208c96e06631641c0566b22005493f09663c7a8d3b6"}, - {file = "psycopg-3.2.9.tar.gz", hash = "sha256:2fbb46fcd17bc81f993f28c47f1ebea38d66ae97cc2dbc3cad73b37cefbff700"}, + {file = "psycopg-3.2.10-py3-none-any.whl", hash = "sha256:ab5caf09a9ec42e314a21f5216dbcceac528e0e05142e42eea83a3b28b320ac3"}, + {file = "psycopg-3.2.10.tar.gz", hash = "sha256:0bce99269d16ed18401683a8569b2c5abd94f72f8364856d56c0389bcd50972a"}, ] [package.dependencies] +psycopg-binary = {version = "3.2.10", optional = true, markers = "implementation_name != \"pypy\" and extra == \"binary\""} psycopg-pool = {version = "*", optional = true, markers = "extra == \"pool\""} typing-extensions = {version = ">=4.6", markers = "python_version < \"3.13\""} tzdata = {version = "*", markers = "sys_platform == \"win32\""} [package.extras] -binary = ["psycopg-binary (==3.2.9)"] -c = ["psycopg-c (==3.2.9)"] +binary = ["psycopg-binary (==3.2.10)"] +c = ["psycopg-c (==3.2.10)"] dev = ["ast-comments (>=1.1.2)", "black (>=24.1.0)", "codespell (>=2.2)", "dnspython (>=2.1)", "flake8 (>=4.0)", "isort-psycopg", "isort[colors] (>=6.0)", "mypy (>=1.14)", "pre-commit (>=4.0.1)", "types-setuptools (>=57.4)", "types-shapely (>=2.0)", "wheel (>=0.37)"] docs = ["Sphinx (>=5.0)", "furo (==2022.6.21)", "sphinx-autobuild (>=2021.3.14)", "sphinx-autodoc-typehints (>=1.12)"] pool = ["psycopg-pool"] test = ["anyio (>=4.0)", "mypy (>=1.14)", "pproxy (>=2.7)", "pytest (>=6.2.5)", "pytest-cov (>=3.0)", "pytest-randomly (>=3.5)"] +[[package]] +name = "psycopg-binary" +version = "3.2.10" +description = "PostgreSQL database adapter for Python -- C optimisation distribution" +optional = false +python-versions = ">=3.8" +groups = ["main"] +markers = "implementation_name != \"pypy\"" +files = [ + {file = "psycopg_binary-3.2.10-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:037dc92fc7d3f2adae7680e17216934c15b919d6528b908ac2eb52aecc0addcf"}, + {file = "psycopg_binary-3.2.10-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:84f7e8c5e5031db342ae697c2e8fb48cd708ba56990573b33e53ce626445371d"}, + {file = "psycopg_binary-3.2.10-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:a5a81104d88780018005fe17c37fa55b4afbb6dd3c205963cc56c025d5f1cc32"}, + {file = "psycopg_binary-3.2.10-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:0c23e88e048bbc33f32f5a35981707c9418723d469552dd5ac4e956366e58492"}, + {file = "psycopg_binary-3.2.10-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9c9f2728488ac5848acdbf14bb4fde50f8ba783cbf3c19e9abd506741389fa7f"}, + {file = "psycopg_binary-3.2.10-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ab1c6d761c4ee581016823dcc02f29b16ad69177fcbba88a9074c924fc31813e"}, + {file = "psycopg_binary-3.2.10-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:a024b3ee539a475cbc59df877c8ecdd6f8552a1b522b69196935bc26dc6152fb"}, + {file = "psycopg_binary-3.2.10-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:50130c0d1a2a01ec3d41631df86b6c1646c76718be000600a399dc1aad80b813"}, + {file = "psycopg_binary-3.2.10-cp310-cp310-win_amd64.whl", hash = "sha256:7fa1626225a162924d2da0ff4ef77869f7a8501d320355d2732be5bf2dda6138"}, + {file = "psycopg_binary-3.2.10-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:db0eb06a19e4c64a08db0db80875ede44939af6a2afc281762c338fad5d6e547"}, + {file = "psycopg_binary-3.2.10-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d922fdd49ed17c558b6b2f9ae2054c3d0cced2a34e079ce5a41c86904d0203f7"}, + {file = "psycopg_binary-3.2.10-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d557a94cd6d2e775b3af6cc0bd0ff0d9d641820b5cc3060ccf1f5ca2bf971217"}, + {file = "psycopg_binary-3.2.10-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:29b6bb87959515bc8b6abef10d8d23a9a681f03e48e9f0c8adb4b9fb7fa73f11"}, + {file = "psycopg_binary-3.2.10-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1b29285474e3339d0840e1b5079fdb0481914108f92ec62de0c87ae333c60b24"}, + {file = "psycopg_binary-3.2.10-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:62590dd113d10cd9c08251cb80b32e2e8aaf01ece04a700322e776b1d216959f"}, + {file = "psycopg_binary-3.2.10-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:764a5b9b40ad371c55dfdf95374d89e44a82fd62272d4fceebea0adb8930e2fb"}, + {file = "psycopg_binary-3.2.10-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bd3676a04970cf825d2c771b0c147f91182c5a3653e0dbe958e12383668d0f79"}, + {file = "psycopg_binary-3.2.10-cp311-cp311-win_amd64.whl", hash = "sha256:646048f46192c8d23786cc6ef19f35b7488d4110396391e407eca695fdfe9dcd"}, + {file = "psycopg_binary-3.2.10-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:1dee2f4d2adc9adacbfecf8254bd82f6ac95cff707e1b9b99aa721cd1ef16b47"}, + {file = "psycopg_binary-3.2.10-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8b45e65383da9c4a42a56f817973e521e893f4faae897fe9f1a971f9fe799742"}, + {file = "psycopg_binary-3.2.10-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:484d2b1659afe0f8f1cef5ea960bb640e96fa864faf917086f9f833f5c7a8034"}, + {file = "psycopg_binary-3.2.10-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:3bb4046973264ebc8cb7e20a83882d68577c1f26a6f8ad4fe52e4468cd9a8eee"}, + {file = "psycopg_binary-3.2.10-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:14bcbcac0cab465d88b2581e43ec01af4b01c9833e663f1352e05cb41be19e44"}, + {file = "psycopg_binary-3.2.10-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:70bb7f665587dfd79e69f48b34efe226149454d7aab138ed22d5431d703de2f6"}, + {file = "psycopg_binary-3.2.10-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:d2fe9eaa367f6171ab1a21a7dcb335eb2398be7f8bb7e04a20e2260aedc6f782"}, + {file = "psycopg_binary-3.2.10-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:299834cce3eec0c48aae5a5207fc8f0c558fd65f2ceab1a36693329847da956b"}, + {file = "psycopg_binary-3.2.10-cp312-cp312-win_amd64.whl", hash = "sha256:e037aac8dc894d147ef33056fc826ee5072977107a3fdf06122224353a057598"}, + {file = "psycopg_binary-3.2.10-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:55b14f2402be027fe1568bc6c4d75ac34628ff5442a70f74137dadf99f738e3b"}, + {file = "psycopg_binary-3.2.10-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:43d803fb4e108a67c78ba58f3e6855437ca25d56504cae7ebbfbd8fce9b59247"}, + {file = "psycopg_binary-3.2.10-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:470594d303928ab72a1ffd179c9c7bde9d00f76711d6b0c28f8a46ddf56d9807"}, + {file = "psycopg_binary-3.2.10-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:a1d4e4d309049e3cb61269652a3ca56cb598da30ecd7eb8cea561e0d18bc1a43"}, + {file = "psycopg_binary-3.2.10-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a92ff1c2cd79b3966d6a87e26ceb222ecd5581b5ae4b58961f126af806a861ed"}, + {file = "psycopg_binary-3.2.10-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac0365398947879c9827b319217096be727da16c94422e0eb3cf98c930643162"}, + {file = "psycopg_binary-3.2.10-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:42ee399c2613b470a87084ed79b06d9d277f19b0457c10e03a4aef7059097abc"}, + {file = "psycopg_binary-3.2.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2028073fc12cd70ba003309d1439c0c4afab4a7eee7653b8c91213064fffe12b"}, + {file = "psycopg_binary-3.2.10-cp313-cp313-win_amd64.whl", hash = "sha256:8390db6d2010ffcaf7f2b42339a2da620a7125d37029c1f9b72dfb04a8e7be6f"}, + {file = "psycopg_binary-3.2.10-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:b34c278a58aa79562afe7f45e0455b1f4cad5974fc3d5674cc5f1f9f57e97fc5"}, + {file = "psycopg_binary-3.2.10-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:810f65b9ef1fe9dddb5c05937884ea9563aaf4e1a2c3d138205231ed5f439511"}, + {file = "psycopg_binary-3.2.10-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8923487c3898c65e1450847e15d734bb2e6adbd2e79d2d1dd5ad829a1306bdc0"}, + {file = "psycopg_binary-3.2.10-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:7950ff79df7a453ac8a7d7a74694055b6c15905b0a2b6e3c99eb59c51a3f9bf7"}, + {file = "psycopg_binary-3.2.10-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0c2b95e83fda70ed2b0b4fadd8538572e4a4d987b721823981862d1ab56cc760"}, + {file = "psycopg_binary-3.2.10-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:20384985fbc650c09a547a13c6d7f91bb42020d38ceafd2b68b7fc4a48a1f160"}, + {file = "psycopg_binary-3.2.10-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:1f6982609b8ff8fcd67299b67cd5787da1876f3bb28fedd547262cfa8ddedf94"}, + {file = "psycopg_binary-3.2.10-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:bf30dcf6aaaa8d4779a20d2158bdf81cc8e84ce8eee595d748a7671c70c7b890"}, + {file = "psycopg_binary-3.2.10-cp314-cp314-win_amd64.whl", hash = "sha256:d5c6a66a76022af41970bf19f51bc6bf87bd10165783dd1d40484bfd87d6b382"}, + {file = "psycopg_binary-3.2.10-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:901729188b3fd5625970650ca1167786847dee0b92930c2858724d1a5e25dee1"}, + {file = "psycopg_binary-3.2.10-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d7d05174276bb403b8a57e01b857d96b0ac2a6879c5ce06a5cac2d1115763081"}, + {file = "psycopg_binary-3.2.10-cp38-cp38-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:37b42b2f5f58df1f07a5df1b0c2bcc9bd3b9c105e2e988923bfa47aa4ae967da"}, + {file = "psycopg_binary-3.2.10-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6fe450a98a0788b721b1b8302f0ba9be6eca82faf74bf7a86d794cd6484c7e27"}, + {file = "psycopg_binary-3.2.10-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:a28f24a7b68456bd31209b027a5b04304d37eb1d622ef847bf8c47933218a738"}, + {file = "psycopg_binary-3.2.10-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:5369202e0e764193eac311b5a337d8cd58b1e23b822ddb7a559ed9f683d97623"}, + {file = "psycopg_binary-3.2.10-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:8f4ae059c6c9e491cdc3f39f9fc4f09373ef281c6cc381499269dcff21abafc9"}, + {file = "psycopg_binary-3.2.10-cp38-cp38-win_amd64.whl", hash = "sha256:3e115930af2f38f4bbb5f1b61b598ceb802f091c1592c0fe0571c796b714b89a"}, + {file = "psycopg_binary-3.2.10-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0738320a8d405f98743227ff70ed8fac9670870289435f4861dc640cef4a61d3"}, + {file = "psycopg_binary-3.2.10-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:89440355d1b163b11dc661ae64a5667578aab1b80bbf71ced90693d88e9863e1"}, + {file = "psycopg_binary-3.2.10-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3234605839e7d7584bd0a20716395eba34d368a5099dafe7896c943facac98fc"}, + {file = "psycopg_binary-3.2.10-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:725843fd444075cc6c9989f5b25ca83ac68d8d70b58e1f476fbb4096975e43cc"}, + {file = "psycopg_binary-3.2.10-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:447afc326cbc95ed67c0cd27606c0f81fa933b830061e096dbd37e08501cb3de"}, + {file = "psycopg_binary-3.2.10-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5334a61a00ccb722f0b28789e265c7a273cfd10d5a1ed6bf062686fbb71e7032"}, + {file = "psycopg_binary-3.2.10-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:183a59cbdcd7e156669577fd73a9e917b1ee664e620f1e31ae138d24c7714693"}, + {file = "psycopg_binary-3.2.10-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:8fa2efaf5e2f8c289a185c91c80a624a8f97aa17fbedcbc68f373d089b332afd"}, + {file = "psycopg_binary-3.2.10-cp39-cp39-win_amd64.whl", hash = "sha256:6220d6efd6e2df7b67d70ed60d653106cd3b70c5cb8cbe4e9f0a142a5db14015"}, +] + [[package]] name = "psycopg-pool" version = "3.2.6" @@ -3736,14 +3101,15 @@ test = ["coverage[toml] (>=5.2)", "hypothesis", "pytest (>=6.0)", "pytest-cov"] [[package]] name = "pycparser" -version = "2.22" +version = "2.23" description = "C parser in Python" optional = false python-versions = ">=3.8" groups = ["main"] +markers = "implementation_name != \"PyPy\"" files = [ - {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, - {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, + {file = "pycparser-2.23-py3-none-any.whl", hash = "sha256:e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934"}, + {file = "pycparser-2.23.tar.gz", hash = "sha256:78816d4f24add8f10a06d6f05b4d424ad9e96cfebf68a4ddc99c65c0720d00c2"}, ] [[package]] @@ -3850,21 +3216,21 @@ files = [ [[package]] name = "pydantic" -version = "2.11.6" +version = "2.12.2" description = "Data validation using Python type hints" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "pydantic-2.11.6-py3-none-any.whl", hash = "sha256:a24478d2be1b91b6d3bc9597439f69ed5e87f68ebd285d86f7c7932a084b72e7"}, - {file = "pydantic-2.11.6.tar.gz", hash = "sha256:12b45cfb4af17e555d3c6283d0b55271865fb0b43cc16dd0d52749dc7abf70e7"}, + {file = "pydantic-2.12.2-py3-none-any.whl", hash = "sha256:25ff718ee909acd82f1ff9b1a4acfd781bb23ab3739adaa7144f19a6a4e231ae"}, + {file = "pydantic-2.12.2.tar.gz", hash = "sha256:7b8fa15b831a4bbde9d5b84028641ac3080a4ca2cbd4a621a661687e741624fd"}, ] [package.dependencies] annotated-types = ">=0.6.0" -pydantic-core = "2.33.2" -typing-extensions = ">=4.12.2" -typing-inspection = ">=0.4.0" +pydantic-core = "2.41.4" +typing-extensions = ">=4.14.1" +typing-inspection = ">=0.4.2" [package.extras] email = ["email-validator (>=2.0.0)"] @@ -3872,135 +3238,148 @@ timezone = ["tzdata"] [[package]] name = "pydantic-core" -version = "2.33.2" +version = "2.41.4" description = "Core functionality for Pydantic validation and serialization" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "pydantic_core-2.33.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2b3d326aaef0c0399d9afffeb6367d5e26ddc24d351dbc9c636840ac355dc5d8"}, - {file = "pydantic_core-2.33.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e5b2671f05ba48b94cb90ce55d8bdcaaedb8ba00cc5359f6810fc918713983d"}, - {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0069c9acc3f3981b9ff4cdfaf088e98d83440a4c7ea1bc07460af3d4dc22e72d"}, - {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d53b22f2032c42eaaf025f7c40c2e3b94568ae077a606f006d206a463bc69572"}, - {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0405262705a123b7ce9f0b92f123334d67b70fd1f20a9372b907ce1080c7ba02"}, - {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4b25d91e288e2c4e0662b8038a28c6a07eaac3e196cfc4ff69de4ea3db992a1b"}, - {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bdfe4b3789761f3bcb4b1ddf33355a71079858958e3a552f16d5af19768fef2"}, - {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:efec8db3266b76ef9607c2c4c419bdb06bf335ae433b80816089ea7585816f6a"}, - {file = "pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:031c57d67ca86902726e0fae2214ce6770bbe2f710dc33063187a68744a5ecac"}, - {file = "pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:f8de619080e944347f5f20de29a975c2d815d9ddd8be9b9b7268e2e3ef68605a"}, - {file = "pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:73662edf539e72a9440129f231ed3757faab89630d291b784ca99237fb94db2b"}, - {file = "pydantic_core-2.33.2-cp310-cp310-win32.whl", hash = "sha256:0a39979dcbb70998b0e505fb1556a1d550a0781463ce84ebf915ba293ccb7e22"}, - {file = "pydantic_core-2.33.2-cp310-cp310-win_amd64.whl", hash = "sha256:b0379a2b24882fef529ec3b4987cb5d003b9cda32256024e6fe1586ac45fc640"}, - {file = "pydantic_core-2.33.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4c5b0a576fb381edd6d27f0a85915c6daf2f8138dc5c267a57c08a62900758c7"}, - {file = "pydantic_core-2.33.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e799c050df38a639db758c617ec771fd8fb7a5f8eaaa4b27b101f266b216a246"}, - {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc46a01bf8d62f227d5ecee74178ffc448ff4e5197c756331f71efcc66dc980f"}, - {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a144d4f717285c6d9234a66778059f33a89096dfb9b39117663fd8413d582dcc"}, - {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73cf6373c21bc80b2e0dc88444f41ae60b2f070ed02095754eb5a01df12256de"}, - {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3dc625f4aa79713512d1976fe9f0bc99f706a9dee21dfd1810b4bbbf228d0e8a"}, - {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b21b5549499972441da4758d662aeea93f1923f953e9cbaff14b8b9565aef"}, - {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bdc25f3681f7b78572699569514036afe3c243bc3059d3942624e936ec93450e"}, - {file = "pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fe5b32187cbc0c862ee201ad66c30cf218e5ed468ec8dc1cf49dec66e160cc4d"}, - {file = "pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:bc7aee6f634a6f4a95676fcb5d6559a2c2a390330098dba5e5a5f28a2e4ada30"}, - {file = "pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:235f45e5dbcccf6bd99f9f472858849f73d11120d76ea8707115415f8e5ebebf"}, - {file = "pydantic_core-2.33.2-cp311-cp311-win32.whl", hash = "sha256:6368900c2d3ef09b69cb0b913f9f8263b03786e5b2a387706c5afb66800efd51"}, - {file = "pydantic_core-2.33.2-cp311-cp311-win_amd64.whl", hash = "sha256:1e063337ef9e9820c77acc768546325ebe04ee38b08703244c1309cccc4f1bab"}, - {file = "pydantic_core-2.33.2-cp311-cp311-win_arm64.whl", hash = "sha256:6b99022f1d19bc32a4c2a0d544fc9a76e3be90f0b3f4af413f87d38749300e65"}, - {file = "pydantic_core-2.33.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a7ec89dc587667f22b6a0b6579c249fca9026ce7c333fc142ba42411fa243cdc"}, - {file = "pydantic_core-2.33.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3c6db6e52c6d70aa0d00d45cdb9b40f0433b96380071ea80b09277dba021ddf7"}, - {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e61206137cbc65e6d5256e1166f88331d3b6238e082d9f74613b9b765fb9025"}, - {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb8c529b2819c37140eb51b914153063d27ed88e3bdc31b71198a198e921e011"}, - {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c52b02ad8b4e2cf14ca7b3d918f3eb0ee91e63b3167c32591e57c4317e134f8f"}, - {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:96081f1605125ba0855dfda83f6f3df5ec90c61195421ba72223de35ccfb2f88"}, - {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f57a69461af2a5fa6e6bbd7a5f60d3b7e6cebb687f55106933188e79ad155c1"}, - {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:572c7e6c8bb4774d2ac88929e3d1f12bc45714ae5ee6d9a788a9fb35e60bb04b"}, - {file = "pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:db4b41f9bd95fbe5acd76d89920336ba96f03e149097365afe1cb092fceb89a1"}, - {file = "pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:fa854f5cf7e33842a892e5c73f45327760bc7bc516339fda888c75ae60edaeb6"}, - {file = "pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5f483cfb75ff703095c59e365360cb73e00185e01aaea067cd19acffd2ab20ea"}, - {file = "pydantic_core-2.33.2-cp312-cp312-win32.whl", hash = "sha256:9cb1da0f5a471435a7bc7e439b8a728e8b61e59784b2af70d7c169f8dd8ae290"}, - {file = "pydantic_core-2.33.2-cp312-cp312-win_amd64.whl", hash = "sha256:f941635f2a3d96b2973e867144fde513665c87f13fe0e193c158ac51bfaaa7b2"}, - {file = "pydantic_core-2.33.2-cp312-cp312-win_arm64.whl", hash = "sha256:cca3868ddfaccfbc4bfb1d608e2ccaaebe0ae628e1416aeb9c4d88c001bb45ab"}, - {file = "pydantic_core-2.33.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1082dd3e2d7109ad8b7da48e1d4710c8d06c253cbc4a27c1cff4fbcaa97a9e3f"}, - {file = "pydantic_core-2.33.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f517ca031dfc037a9c07e748cefd8d96235088b83b4f4ba8939105d20fa1dcd6"}, - {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a9f2c9dd19656823cb8250b0724ee9c60a82f3cdf68a080979d13092a3b0fef"}, - {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b0a451c263b01acebe51895bfb0e1cc842a5c666efe06cdf13846c7418caa9a"}, - {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ea40a64d23faa25e62a70ad163571c0b342b8bf66d5fa612ac0dec4f069d916"}, - {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb2d542b4d66f9470e8065c5469ec676978d625a8b7a363f07d9a501a9cb36a"}, - {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdac5d6ffa1b5a83bca06ffe7583f5576555e6c8b3a91fbd25ea7780f825f7d"}, - {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04a1a413977ab517154eebb2d326da71638271477d6ad87a769102f7c2488c56"}, - {file = "pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c8e7af2f4e0194c22b5b37205bfb293d166a7344a5b0d0eaccebc376546d77d5"}, - {file = "pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:5c92edd15cd58b3c2d34873597a1e20f13094f59cf88068adb18947df5455b4e"}, - {file = "pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:65132b7b4a1c0beded5e057324b7e16e10910c106d43675d9bd87d4f38dde162"}, - {file = "pydantic_core-2.33.2-cp313-cp313-win32.whl", hash = "sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849"}, - {file = "pydantic_core-2.33.2-cp313-cp313-win_amd64.whl", hash = "sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9"}, - {file = "pydantic_core-2.33.2-cp313-cp313-win_arm64.whl", hash = "sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9"}, - {file = "pydantic_core-2.33.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac"}, - {file = "pydantic_core-2.33.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5"}, - {file = "pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9"}, - {file = "pydantic_core-2.33.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a2b911a5b90e0374d03813674bf0a5fbbb7741570dcd4b4e85a2e48d17def29d"}, - {file = "pydantic_core-2.33.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6fa6dfc3e4d1f734a34710f391ae822e0a8eb8559a85c6979e14e65ee6ba2954"}, - {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c54c939ee22dc8e2d545da79fc5381f1c020d6d3141d3bd747eab59164dc89fb"}, - {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:53a57d2ed685940a504248187d5685e49eb5eef0f696853647bf37c418c538f7"}, - {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09fb9dd6571aacd023fe6aaca316bd01cf60ab27240d7eb39ebd66a3a15293b4"}, - {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0e6116757f7959a712db11f3e9c0a99ade00a5bbedae83cb801985aa154f071b"}, - {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d55ab81c57b8ff8548c3e4947f119551253f4e3787a7bbc0b6b3ca47498a9d3"}, - {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c20c462aa4434b33a2661701b861604913f912254e441ab8d78d30485736115a"}, - {file = "pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:44857c3227d3fb5e753d5fe4a3420d6376fa594b07b621e220cd93703fe21782"}, - {file = "pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:eb9b459ca4df0e5c87deb59d37377461a538852765293f9e6ee834f0435a93b9"}, - {file = "pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9fcd347d2cc5c23b06de6d3b7b8275be558a0c90549495c699e379a80bf8379e"}, - {file = "pydantic_core-2.33.2-cp39-cp39-win32.whl", hash = "sha256:83aa99b1285bc8f038941ddf598501a86f1536789740991d7d8756e34f1e74d9"}, - {file = "pydantic_core-2.33.2-cp39-cp39-win_amd64.whl", hash = "sha256:f481959862f57f29601ccced557cc2e817bce7533ab8e01a797a48b49c9692b3"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5c4aa4e82353f65e548c476b37e64189783aa5384903bfea4f41580f255fddfa"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d946c8bf0d5c24bf4fe333af284c59a19358aa3ec18cb3dc4370080da1e8ad29"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87b31b6846e361ef83fedb187bb5b4372d0da3f7e28d85415efa92d6125d6e6d"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa9d91b338f2df0508606f7009fde642391425189bba6d8c653afd80fd6bb64e"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2058a32994f1fde4ca0480ab9d1e75a0e8c87c22b53a3ae66554f9af78f2fe8c"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:0e03262ab796d986f978f79c943fc5f620381be7287148b8010b4097f79a39ec"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:1a8695a8d00c73e50bff9dfda4d540b7dee29ff9b8053e38380426a85ef10052"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:fa754d1850735a0b0e03bcffd9d4b4343eb417e47196e4485d9cca326073a42c"}, - {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a11c8d26a50bfab49002947d3d237abe4d9e4b5bdc8846a63537b6488e197808"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:dd14041875d09cc0f9308e37a6f8b65f5585cf2598a53aa0123df8b129d481f8"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d87c561733f66531dced0da6e864f44ebf89a8fba55f31407b00c2f7f9449593"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f82865531efd18d6e07a04a17331af02cb7a651583c418df8266f17a63c6612"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bfb5112df54209d820d7bf9317c7a6c9025ea52e49f46b6a2060104bba37de7"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:64632ff9d614e5eecfb495796ad51b0ed98c453e447a76bcbeeb69615079fc7e"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:f889f7a40498cc077332c7ab6b4608d296d852182211787d4f3ee377aaae66e8"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:de4b83bb311557e439b9e186f733f6c645b9417c84e2eb8203f3f820a4b988bf"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:82f68293f055f51b51ea42fafc74b6aad03e70e191799430b90c13d643059ebb"}, - {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:329467cecfb529c925cf2bbd4d60d2c509bc2fb52a20c1045bf09bb70971a9c1"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:87acbfcf8e90ca885206e98359d7dca4bcbb35abdc0ff66672a293e1d7a19101"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:7f92c15cd1e97d4b12acd1cc9004fa092578acfa57b67ad5e43a197175d01a64"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3f26877a748dc4251cfcfda9dfb5f13fcb034f5308388066bcfe9031b63ae7d"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dac89aea9af8cd672fa7b510e7b8c33b0bba9a43186680550ccf23020f32d535"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:970919794d126ba8645f3837ab6046fb4e72bbc057b3709144066204c19a455d"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:3eb3fe62804e8f859c49ed20a8451342de53ed764150cb14ca71357c765dc2a6"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:3abcd9392a36025e3bd55f9bd38d908bd17962cc49bc6da8e7e96285336e2bca"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:3a1c81334778f9e3af2f8aeb7a960736e5cab1dfebfb26aabca09afd2906c039"}, - {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2807668ba86cb38c6817ad9bc66215ab8584d1d304030ce4f0887336f28a5e27"}, - {file = "pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc"}, + {file = "pydantic_core-2.41.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2442d9a4d38f3411f22eb9dd0912b7cbf4b7d5b6c92c4173b75d3e1ccd84e36e"}, + {file = "pydantic_core-2.41.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:30a9876226dda131a741afeab2702e2d127209bde3c65a2b8133f428bc5d006b"}, + {file = "pydantic_core-2.41.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d55bbac04711e2980645af68b97d445cdbcce70e5216de444a6c4b6943ebcccd"}, + {file = "pydantic_core-2.41.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e1d778fb7849a42d0ee5927ab0f7453bf9f85eef8887a546ec87db5ddb178945"}, + {file = "pydantic_core-2.41.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1b65077a4693a98b90ec5ad8f203ad65802a1b9b6d4a7e48066925a7e1606706"}, + {file = "pydantic_core-2.41.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:62637c769dee16eddb7686bf421be48dfc2fae93832c25e25bc7242e698361ba"}, + {file = "pydantic_core-2.41.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2dfe3aa529c8f501babf6e502936b9e8d4698502b2cfab41e17a028d91b1ac7b"}, + {file = "pydantic_core-2.41.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ca2322da745bf2eeb581fc9ea3bbb31147702163ccbcbf12a3bb630e4bf05e1d"}, + {file = "pydantic_core-2.41.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e8cd3577c796be7231dcf80badcf2e0835a46665eaafd8ace124d886bab4d700"}, + {file = "pydantic_core-2.41.4-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:1cae8851e174c83633f0833e90636832857297900133705ee158cf79d40f03e6"}, + {file = "pydantic_core-2.41.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a26d950449aae348afe1ac8be5525a00ae4235309b729ad4d3399623125b43c9"}, + {file = "pydantic_core-2.41.4-cp310-cp310-win32.whl", hash = "sha256:0cf2a1f599efe57fa0051312774280ee0f650e11152325e41dfd3018ef2c1b57"}, + {file = "pydantic_core-2.41.4-cp310-cp310-win_amd64.whl", hash = "sha256:a8c2e340d7e454dc3340d3d2e8f23558ebe78c98aa8f68851b04dcb7bc37abdc"}, + {file = "pydantic_core-2.41.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:28ff11666443a1a8cf2a044d6a545ebffa8382b5f7973f22c36109205e65dc80"}, + {file = "pydantic_core-2.41.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:61760c3925d4633290292bad462e0f737b840508b4f722247d8729684f6539ae"}, + {file = "pydantic_core-2.41.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eae547b7315d055b0de2ec3965643b0ab82ad0106a7ffd29615ee9f266a02827"}, + {file = "pydantic_core-2.41.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ef9ee5471edd58d1fcce1c80ffc8783a650e3e3a193fe90d52e43bb4d87bff1f"}, + {file = "pydantic_core-2.41.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:15dd504af121caaf2c95cb90c0ebf71603c53de98305621b94da0f967e572def"}, + {file = "pydantic_core-2.41.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3a926768ea49a8af4d36abd6a8968b8790f7f76dd7cbd5a4c180db2b4ac9a3a2"}, + {file = "pydantic_core-2.41.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6916b9b7d134bff5440098a4deb80e4cb623e68974a87883299de9124126c2a8"}, + {file = "pydantic_core-2.41.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5cf90535979089df02e6f17ffd076f07237efa55b7343d98760bde8743c4b265"}, + {file = "pydantic_core-2.41.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:7533c76fa647fade2d7ec75ac5cc079ab3f34879626dae5689b27790a6cf5a5c"}, + {file = "pydantic_core-2.41.4-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:37e516bca9264cbf29612539801ca3cd5d1be465f940417b002905e6ed79d38a"}, + {file = "pydantic_core-2.41.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0c19cb355224037c83642429b8ce261ae108e1c5fbf5c028bac63c77b0f8646e"}, + {file = "pydantic_core-2.41.4-cp311-cp311-win32.whl", hash = "sha256:09c2a60e55b357284b5f31f5ab275ba9f7f70b7525e18a132ec1f9160b4f1f03"}, + {file = "pydantic_core-2.41.4-cp311-cp311-win_amd64.whl", hash = "sha256:711156b6afb5cb1cb7c14a2cc2c4a8b4c717b69046f13c6b332d8a0a8f41ca3e"}, + {file = "pydantic_core-2.41.4-cp311-cp311-win_arm64.whl", hash = "sha256:6cb9cf7e761f4f8a8589a45e49ed3c0d92d1d696a45a6feaee8c904b26efc2db"}, + {file = "pydantic_core-2.41.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:ab06d77e053d660a6faaf04894446df7b0a7e7aba70c2797465a0a1af00fc887"}, + {file = "pydantic_core-2.41.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c53ff33e603a9c1179a9364b0a24694f183717b2e0da2b5ad43c316c956901b2"}, + {file = "pydantic_core-2.41.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:304c54176af2c143bd181d82e77c15c41cbacea8872a2225dd37e6544dce9999"}, + {file = "pydantic_core-2.41.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:025ba34a4cf4fb32f917d5d188ab5e702223d3ba603be4d8aca2f82bede432a4"}, + {file = "pydantic_core-2.41.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b9f5f30c402ed58f90c70e12eff65547d3ab74685ffe8283c719e6bead8ef53f"}, + {file = "pydantic_core-2.41.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd96e5d15385d301733113bcaa324c8bcf111275b7675a9c6e88bfb19fc05e3b"}, + {file = "pydantic_core-2.41.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98f348cbb44fae6e9653c1055db7e29de67ea6a9ca03a5fa2c2e11a47cff0e47"}, + {file = "pydantic_core-2.41.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ec22626a2d14620a83ca583c6f5a4080fa3155282718b6055c2ea48d3ef35970"}, + {file = "pydantic_core-2.41.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3a95d4590b1f1a43bf33ca6d647b990a88f4a3824a8c4572c708f0b45a5290ed"}, + {file = "pydantic_core-2.41.4-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:f9672ab4d398e1b602feadcffcdd3af44d5f5e6ddc15bc7d15d376d47e8e19f8"}, + {file = "pydantic_core-2.41.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:84d8854db5f55fead3b579f04bda9a36461dab0730c5d570e1526483e7bb8431"}, + {file = "pydantic_core-2.41.4-cp312-cp312-win32.whl", hash = "sha256:9be1c01adb2ecc4e464392c36d17f97e9110fbbc906bcbe1c943b5b87a74aabd"}, + {file = "pydantic_core-2.41.4-cp312-cp312-win_amd64.whl", hash = "sha256:d682cf1d22bab22a5be08539dca3d1593488a99998f9f412137bc323179067ff"}, + {file = "pydantic_core-2.41.4-cp312-cp312-win_arm64.whl", hash = "sha256:833eebfd75a26d17470b58768c1834dfc90141b7afc6eb0429c21fc5a21dcfb8"}, + {file = "pydantic_core-2.41.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:85e050ad9e5f6fe1004eec65c914332e52f429bc0ae12d6fa2092407a462c746"}, + {file = "pydantic_core-2.41.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e7393f1d64792763a48924ba31d1e44c2cfbc05e3b1c2c9abb4ceeadd912cced"}, + {file = "pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94dab0940b0d1fb28bcab847adf887c66a27a40291eedf0b473be58761c9799a"}, + {file = "pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:de7c42f897e689ee6f9e93c4bec72b99ae3b32a2ade1c7e4798e690ff5246e02"}, + {file = "pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:664b3199193262277b8b3cd1e754fb07f2c6023289c815a1e1e8fb415cb247b1"}, + {file = "pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d95b253b88f7d308b1c0b417c4624f44553ba4762816f94e6986819b9c273fb2"}, + {file = "pydantic_core-2.41.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a1351f5bbdbbabc689727cb91649a00cb9ee7203e0a6e54e9f5ba9e22e384b84"}, + {file = "pydantic_core-2.41.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1affa4798520b148d7182da0615d648e752de4ab1a9566b7471bc803d88a062d"}, + {file = "pydantic_core-2.41.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7b74e18052fea4aa8dea2fb7dbc23d15439695da6cbe6cfc1b694af1115df09d"}, + {file = "pydantic_core-2.41.4-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:285b643d75c0e30abda9dc1077395624f314a37e3c09ca402d4015ef5979f1a2"}, + {file = "pydantic_core-2.41.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:f52679ff4218d713b3b33f88c89ccbf3a5c2c12ba665fb80ccc4192b4608dbab"}, + {file = "pydantic_core-2.41.4-cp313-cp313-win32.whl", hash = "sha256:ecde6dedd6fff127c273c76821bb754d793be1024bc33314a120f83a3c69460c"}, + {file = "pydantic_core-2.41.4-cp313-cp313-win_amd64.whl", hash = "sha256:d081a1f3800f05409ed868ebb2d74ac39dd0c1ff6c035b5162356d76030736d4"}, + {file = "pydantic_core-2.41.4-cp313-cp313-win_arm64.whl", hash = "sha256:f8e49c9c364a7edcbe2a310f12733aad95b022495ef2a8d653f645e5d20c1564"}, + {file = "pydantic_core-2.41.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:ed97fd56a561f5eb5706cebe94f1ad7c13b84d98312a05546f2ad036bafe87f4"}, + {file = "pydantic_core-2.41.4-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a870c307bf1ee91fc58a9a61338ff780d01bfae45922624816878dce784095d2"}, + {file = "pydantic_core-2.41.4-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d25e97bc1f5f8f7985bdc2335ef9e73843bb561eb1fa6831fdfc295c1c2061cf"}, + {file = "pydantic_core-2.41.4-cp313-cp313t-win_amd64.whl", hash = "sha256:d405d14bea042f166512add3091c1af40437c2e7f86988f3915fabd27b1e9cd2"}, + {file = "pydantic_core-2.41.4-cp313-cp313t-win_arm64.whl", hash = "sha256:19f3684868309db5263a11bace3c45d93f6f24afa2ffe75a647583df22a2ff89"}, + {file = "pydantic_core-2.41.4-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:e9205d97ed08a82ebb9a307e92914bb30e18cdf6f6b12ca4bedadb1588a0bfe1"}, + {file = "pydantic_core-2.41.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:82df1f432b37d832709fbcc0e24394bba04a01b6ecf1ee87578145c19cde12ac"}, + {file = "pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc3b4cc4539e055cfa39a3763c939f9d409eb40e85813257dcd761985a108554"}, + {file = "pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b1eb1754fce47c63d2ff57fdb88c351a6c0150995890088b33767a10218eaa4e"}, + {file = "pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e6ab5ab30ef325b443f379ddb575a34969c333004fca5a1daa0133a6ffaad616"}, + {file = "pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:31a41030b1d9ca497634092b46481b937ff9397a86f9f51bd41c4767b6fc04af"}, + {file = "pydantic_core-2.41.4-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a44ac1738591472c3d020f61c6df1e4015180d6262ebd39bf2aeb52571b60f12"}, + {file = "pydantic_core-2.41.4-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d72f2b5e6e82ab8f94ea7d0d42f83c487dc159c5240d8f83beae684472864e2d"}, + {file = "pydantic_core-2.41.4-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:c4d1e854aaf044487d31143f541f7aafe7b482ae72a022c664b2de2e466ed0ad"}, + {file = "pydantic_core-2.41.4-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:b568af94267729d76e6ee5ececda4e283d07bbb28e8148bb17adad93d025d25a"}, + {file = "pydantic_core-2.41.4-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:6d55fb8b1e8929b341cc313a81a26e0d48aa3b519c1dbaadec3a6a2b4fcad025"}, + {file = "pydantic_core-2.41.4-cp314-cp314-win32.whl", hash = "sha256:5b66584e549e2e32a1398df11da2e0a7eff45d5c2d9db9d5667c5e6ac764d77e"}, + {file = "pydantic_core-2.41.4-cp314-cp314-win_amd64.whl", hash = "sha256:557a0aab88664cc552285316809cab897716a372afaf8efdbef756f8b890e894"}, + {file = "pydantic_core-2.41.4-cp314-cp314-win_arm64.whl", hash = "sha256:3f1ea6f48a045745d0d9f325989d8abd3f1eaf47dd00485912d1a3a63c623a8d"}, + {file = "pydantic_core-2.41.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6c1fe4c5404c448b13188dd8bd2ebc2bdd7e6727fa61ff481bcc2cca894018da"}, + {file = "pydantic_core-2.41.4-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:523e7da4d43b113bf8e7b49fa4ec0c35bf4fe66b2230bfc5c13cc498f12c6c3e"}, + {file = "pydantic_core-2.41.4-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5729225de81fb65b70fdb1907fcf08c75d498f4a6f15af005aabb1fdadc19dfa"}, + {file = "pydantic_core-2.41.4-cp314-cp314t-win_amd64.whl", hash = "sha256:de2cfbb09e88f0f795fd90cf955858fc2c691df65b1f21f0aa00b99f3fbc661d"}, + {file = "pydantic_core-2.41.4-cp314-cp314t-win_arm64.whl", hash = "sha256:d34f950ae05a83e0ede899c595f312ca976023ea1db100cd5aa188f7005e3ab0"}, + {file = "pydantic_core-2.41.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:646e76293345954acea6966149683047b7b2ace793011922208c8e9da12b0062"}, + {file = "pydantic_core-2.41.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cc8e85a63085a137d286e2791037f5fdfff0aabb8b899483ca9c496dd5797338"}, + {file = "pydantic_core-2.41.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:692c622c8f859a17c156492783902d8370ac7e121a611bd6fe92cc71acf9ee8d"}, + {file = "pydantic_core-2.41.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d1e2906efb1031a532600679b424ef1d95d9f9fb507f813951f23320903adbd7"}, + {file = "pydantic_core-2.41.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e04e2f7f8916ad3ddd417a7abdd295276a0bf216993d9318a5d61cc058209166"}, + {file = "pydantic_core-2.41.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:df649916b81822543d1c8e0e1d079235f68acdc7d270c911e8425045a8cfc57e"}, + {file = "pydantic_core-2.41.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66c529f862fdba70558061bb936fe00ddbaaa0c647fd26e4a4356ef1d6561891"}, + {file = "pydantic_core-2.41.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fc3b4c5a1fd3a311563ed866c2c9b62da06cb6398bee186484ce95c820db71cb"}, + {file = "pydantic_core-2.41.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:6e0fc40d84448f941df9b3334c4b78fe42f36e3bf631ad54c3047a0cdddc2514"}, + {file = "pydantic_core-2.41.4-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:44e7625332683b6c1c8b980461475cde9595eff94447500e80716db89b0da005"}, + {file = "pydantic_core-2.41.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:170ee6835f6c71081d031ef1c3b4dc4a12b9efa6a9540f93f95b82f3c7571ae8"}, + {file = "pydantic_core-2.41.4-cp39-cp39-win32.whl", hash = "sha256:3adf61415efa6ce977041ba9745183c0e1f637ca849773afa93833e04b163feb"}, + {file = "pydantic_core-2.41.4-cp39-cp39-win_amd64.whl", hash = "sha256:a238dd3feee263eeaeb7dc44aea4ba1364682c4f9f9467e6af5596ba322c2332"}, + {file = "pydantic_core-2.41.4-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:a1b2cfec3879afb742a7b0bcfa53e4f22ba96571c9e54d6a3afe1052d17d843b"}, + {file = "pydantic_core-2.41.4-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:d175600d975b7c244af6eb9c9041f10059f20b8bbffec9e33fdd5ee3f67cdc42"}, + {file = "pydantic_core-2.41.4-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f184d657fa4947ae5ec9c47bd7e917730fa1cbb78195037e32dcbab50aca5ee"}, + {file = "pydantic_core-2.41.4-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ed810568aeffed3edc78910af32af911c835cc39ebbfacd1f0ab5dd53028e5c"}, + {file = "pydantic_core-2.41.4-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:4f5d640aeebb438517150fdeec097739614421900e4a08db4a3ef38898798537"}, + {file = "pydantic_core-2.41.4-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:4a9ab037b71927babc6d9e7fc01aea9e66dc2a4a34dff06ef0724a4049629f94"}, + {file = "pydantic_core-2.41.4-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4dab9484ec605c3016df9ad4fd4f9a390bc5d816a3b10c6550f8424bb80b18c"}, + {file = "pydantic_core-2.41.4-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd8a5028425820731d8c6c098ab642d7b8b999758e24acae03ed38a66eca8335"}, + {file = "pydantic_core-2.41.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:1e5ab4fc177dd41536b3c32b2ea11380dd3d4619a385860621478ac2d25ceb00"}, + {file = "pydantic_core-2.41.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:3d88d0054d3fa11ce936184896bed3c1c5441d6fa483b498fac6a5d0dd6f64a9"}, + {file = "pydantic_core-2.41.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b2a054a8725f05b4b6503357e0ac1c4e8234ad3b0c2ac130d6ffc66f0e170e2"}, + {file = "pydantic_core-2.41.4-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b0d9db5a161c99375a0c68c058e227bee1d89303300802601d76a3d01f74e258"}, + {file = "pydantic_core-2.41.4-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:6273ea2c8ffdac7b7fda2653c49682db815aebf4a89243a6feccf5e36c18c347"}, + {file = "pydantic_core-2.41.4-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:4c973add636efc61de22530b2ef83a65f39b6d6f656df97f678720e20de26caa"}, + {file = "pydantic_core-2.41.4-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:b69d1973354758007f46cf2d44a4f3d0933f10b6dc9bf15cf1356e037f6f731a"}, + {file = "pydantic_core-2.41.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:3619320641fd212aaf5997b6ca505e97540b7e16418f4a241f44cdf108ffb50d"}, + {file = "pydantic_core-2.41.4-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:491535d45cd7ad7e4a2af4a5169b0d07bebf1adfd164b0368da8aa41e19907a5"}, + {file = "pydantic_core-2.41.4-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:54d86c0cada6aba4ec4c047d0e348cbad7063b87ae0f005d9f8c9ad04d4a92a2"}, + {file = "pydantic_core-2.41.4-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eca1124aced216b2500dc2609eade086d718e8249cb9696660ab447d50a758bd"}, + {file = "pydantic_core-2.41.4-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6c9024169becccf0cb470ada03ee578d7348c119a0d42af3dcf9eda96e3a247c"}, + {file = "pydantic_core-2.41.4-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:26895a4268ae5a2849269f4991cdc97236e4b9c010e51137becf25182daac405"}, + {file = "pydantic_core-2.41.4-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:ca4df25762cf71308c446e33c9b1fdca2923a3f13de616e2a949f38bf21ff5a8"}, + {file = "pydantic_core-2.41.4-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:5a28fcedd762349519276c36634e71853b4541079cab4acaaac60c4421827308"}, + {file = "pydantic_core-2.41.4-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:c173ddcd86afd2535e2b695217e82191580663a1d1928239f877f5a1649ef39f"}, + {file = "pydantic_core-2.41.4.tar.gz", hash = "sha256:70e47929a9d4a1905a67e4b687d5946026390568a8e952b92824118063cee4d5"}, ] [package.dependencies] -typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" +typing-extensions = ">=4.14.1" [[package]] -name = "pydantic-xml" -version = "2.17.1" -description = "pydantic xml extension" +name = "pygments" +version = "2.19.2" +description = "Pygments is a syntax highlighting package written in Python." optional = false python-versions = ">=3.8" -groups = ["main"] +groups = ["main", "dev"] files = [ - {file = "pydantic_xml-2.17.1-py3-none-any.whl", hash = "sha256:27e8762e8b3b85f649b69db7b95f838416db21a7d3fcdce4f5ca6f2bee1397b6"}, - {file = "pydantic_xml-2.17.1.tar.gz", hash = "sha256:7ee476ee6ea86055b89a29c1d7dc7ba3763c842f4eff473f31b8b65a86f65f8d"}, + {file = "pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b"}, + {file = "pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887"}, ] -[package.dependencies] -pydantic = ">=2.6.0,<2.10.0b1 || >2.10.0b1" -pydantic-core = ">=2.15.0" - [package.extras] -docs = ["Sphinx (>=5.3.0,<6.0.0)", "furo (>=2022.12.7,<2023.0.0)", "sphinx-copybutton (>=0.5.1,<0.6.0)", "sphinx_design (>=0.3.0,<0.4.0)", "toml (>=0.10.2,<0.11.0)"] -lxml = ["lxml (>=4.9.0)"] +windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pyopenssl" @@ -4023,14 +3402,14 @@ test = ["flaky", "pretend", "pytest (>=3.0.1)"] [[package]] name = "pyparsing" -version = "3.2.3" -description = "pyparsing module - Classes and methods to define and execute parsing grammars" +version = "3.2.5" +description = "pyparsing - Classes and methods to define and execute parsing grammars" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "pyparsing-3.2.3-py3-none-any.whl", hash = "sha256:a749938e02d6fd0b59b356ca504a24982314bb090c383e3cf201c95ef7e2bfcf"}, - {file = "pyparsing-3.2.3.tar.gz", hash = "sha256:b9c13f1ab8b3b542f72e28f634bad4de758ab3ce4546e4301970ad6fa77c38be"}, + {file = "pyparsing-3.2.5-py3-none-any.whl", hash = "sha256:e38a4f02064cf41fe6593d328d0512495ad1f3d8a91c4f73fc401b3079a59a5e"}, + {file = "pyparsing-3.2.5.tar.gz", hash = "sha256:2df8d5b7b2802ef88e8d016a2eb9c7aeaa923529cd251ed0fe4608275d4105b6"}, ] [package.extras] @@ -4038,14 +3417,14 @@ diagrams = ["jinja2", "railroad-diagrams"] [[package]] name = "pypdf" -version = "5.6.0" +version = "5.9.0" description = "A pure-python PDF library capable of splitting, merging, cropping, and transforming PDF files" optional = false python-versions = ">=3.8" groups = ["main"] files = [ - {file = "pypdf-5.6.0-py3-none-any.whl", hash = "sha256:ca6bf446bfb0a2d8d71d6d6bb860798d864c36a29b3d9ae8d7fc7958c59f88e7"}, - {file = "pypdf-5.6.0.tar.gz", hash = "sha256:a4b6538b77fc796622000db7127e4e58039ec5e6afd292f8e9bf42e2e985a749"}, + {file = "pypdf-5.9.0-py3-none-any.whl", hash = "sha256:be10a4c54202f46d9daceaa8788be07aa8cd5ea8c25c529c50dd509206382c35"}, + {file = "pypdf-5.9.0.tar.gz", hash = "sha256:30f67a614d558e495e1fbb157ba58c1de91ffc1718f5e0dfeb82a029233890a1"}, ] [package.extras] @@ -4166,6 +3545,48 @@ files = [ [package.extras] dev = ["build", "flake8", "mypy", "pytest", "twine"] +[[package]] +name = "pytest" +version = "8.4.2" +description = "pytest: simple powerful testing with Python" +optional = false +python-versions = ">=3.9" +groups = ["main", "dev"] +files = [ + {file = "pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79"}, + {file = "pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01"}, +] + +[package.dependencies] +colorama = {version = ">=0.4", markers = "sys_platform == \"win32\""} +iniconfig = ">=1" +packaging = ">=20" +pluggy = ">=1.5,<2" +pygments = ">=2.7.2" + +[package.extras] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "requests", "setuptools", "xmlschema"] + +[[package]] +name = "pytest-asyncio" +version = "1.2.0" +description = "Pytest support for asyncio" +optional = false +python-versions = ">=3.9" +groups = ["main", "dev"] +files = [ + {file = "pytest_asyncio-1.2.0-py3-none-any.whl", hash = "sha256:8e17ae5e46d8e7efe51ab6494dd2010f4ca8dae51652aa3c8d55acf50bfb2e99"}, + {file = "pytest_asyncio-1.2.0.tar.gz", hash = "sha256:c609a64a2a8768462d0c99811ddb8bd2583c33fd33cf7f21af1c142e824ffb57"}, +] + +[package.dependencies] +pytest = ">=8.2,<9" +typing-extensions = {version = ">=4.12", markers = "python_version < \"3.13\""} + +[package.extras] +docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1)"] +testing = ["coverage (>=6.2)", "hypothesis (>=5.7.1)"] + [[package]] name = "python-dateutil" version = "2.9.0.post0" @@ -4181,21 +3602,6 @@ files = [ [package.dependencies] six = ">=1.5" -[[package]] -name = "python-dotenv" -version = "1.1.0" -description = "Read key-value pairs from a .env file and set them as environment variables" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "python_dotenv-1.1.0-py3-none-any.whl", hash = "sha256:d7c01d9e2293916c18baf562d95698754b0dbbb5e74d457c45d4f6561fb9d55d"}, - {file = "python_dotenv-1.1.0.tar.gz", hash = "sha256:41f90bc6f5f177fb41f53e87666db362025010eb28f60a01c9143bfa33a2b2d5"}, -] - -[package.extras] -cli = ["click (>=5.0)"] - [[package]] name = "python-magic" version = "0.4.27" @@ -4208,6 +3614,23 @@ files = [ {file = "python_magic-0.4.27-py2.py3-none-any.whl", hash = "sha256:c212960ad306f700aa0d01e5d7a325d20548ff97eb9920dcd29513174f0294d3"}, ] +[[package]] +name = "python-registry" +version = "1.3.1" +description = "Read access to Windows Registry files." +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "python-registry-1.3.1.tar.gz", hash = "sha256:99185f67d5601be3e7843e55902d5769aea1740869b0882f34ff1bd4b43b1eb2"}, + {file = "python_registry-1.3.1-py2-none-any.whl", hash = "sha256:59d3b00c04bca0c4e1a12be0404da6ccf76b87537ee3a3ad2d8fc1bccf6f63ca"}, + {file = "python_registry-1.3.1-py3-none-any.whl", hash = "sha256:b5b8ae07c271dce12dacd24e16af8aa8d56167ebdb360112a4f152b6d04a4ca9"}, +] + +[package.dependencies] +enum-compat = "*" +unicodecsv = "*" + [[package]] name = "pytz" version = "2025.2" @@ -4222,169 +3645,189 @@ files = [ [[package]] name = "pyyaml" -version = "6.0.2" +version = "6.0.3" description = "YAML parser and emitter for Python" optional = false python-versions = ">=3.8" groups = ["main"] files = [ - {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, - {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, - {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237"}, - {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b"}, - {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed"}, - {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180"}, - {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68"}, - {file = "PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99"}, - {file = "PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e"}, - {file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"}, - {file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"}, - {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c"}, - {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317"}, - {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85"}, - {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"}, - {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e"}, - {file = "PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5"}, - {file = "PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44"}, - {file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"}, - {file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"}, - {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5"}, - {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425"}, - {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476"}, - {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48"}, - {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b"}, - {file = "PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4"}, - {file = "PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8"}, - {file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"}, - {file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"}, - {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"}, - {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"}, - {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"}, - {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"}, - {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"}, - {file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"}, - {file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"}, - {file = "PyYAML-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a"}, - {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5"}, - {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d"}, - {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083"}, - {file = "PyYAML-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706"}, - {file = "PyYAML-6.0.2-cp38-cp38-win32.whl", hash = "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a"}, - {file = "PyYAML-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff"}, - {file = "PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d"}, - {file = "PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f"}, - {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290"}, - {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12"}, - {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19"}, - {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e"}, - {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725"}, - {file = "PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631"}, - {file = "PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8"}, - {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"}, + {file = "PyYAML-6.0.3-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:c2514fceb77bc5e7a2f7adfaa1feb2fb311607c9cb518dbc378688ec73d8292f"}, + {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9c57bb8c96f6d1808c030b1687b9b5fb476abaa47f0db9c0101f5e9f394e97f4"}, + {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:efd7b85f94a6f21e4932043973a7ba2613b059c4a000551892ac9f1d11f5baf3"}, + {file = "PyYAML-6.0.3-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:22ba7cfcad58ef3ecddc7ed1db3409af68d023b7f940da23c6c2a1890976eda6"}, + {file = "PyYAML-6.0.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:6344df0d5755a2c9a276d4473ae6b90647e216ab4757f8426893b5dd2ac3f369"}, + {file = "PyYAML-6.0.3-cp38-cp38-win32.whl", hash = "sha256:3ff07ec89bae51176c0549bc4c63aa6202991da2d9a6129d7aef7f1407d3f295"}, + {file = "PyYAML-6.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:5cf4e27da7e3fbed4d6c3d8e797387aaad68102272f8f9752883bc32d61cb87b"}, + {file = "pyyaml-6.0.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b"}, + {file = "pyyaml-6.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956"}, + {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8"}, + {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198"}, + {file = "pyyaml-6.0.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b"}, + {file = "pyyaml-6.0.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0"}, + {file = "pyyaml-6.0.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69"}, + {file = "pyyaml-6.0.3-cp310-cp310-win32.whl", hash = "sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e"}, + {file = "pyyaml-6.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c"}, + {file = "pyyaml-6.0.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e"}, + {file = "pyyaml-6.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824"}, + {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c"}, + {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00"}, + {file = "pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d"}, + {file = "pyyaml-6.0.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a"}, + {file = "pyyaml-6.0.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4"}, + {file = "pyyaml-6.0.3-cp311-cp311-win32.whl", hash = "sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b"}, + {file = "pyyaml-6.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf"}, + {file = "pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196"}, + {file = "pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0"}, + {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28"}, + {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c"}, + {file = "pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc"}, + {file = "pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e"}, + {file = "pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea"}, + {file = "pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5"}, + {file = "pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b"}, + {file = "pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd"}, + {file = "pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8"}, + {file = "pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1"}, + {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c"}, + {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5"}, + {file = "pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6"}, + {file = "pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6"}, + {file = "pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be"}, + {file = "pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26"}, + {file = "pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c"}, + {file = "pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb"}, + {file = "pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac"}, + {file = "pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310"}, + {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7"}, + {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788"}, + {file = "pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5"}, + {file = "pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764"}, + {file = "pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35"}, + {file = "pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac"}, + {file = "pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3"}, + {file = "pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3"}, + {file = "pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba"}, + {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c"}, + {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702"}, + {file = "pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c"}, + {file = "pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065"}, + {file = "pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65"}, + {file = "pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9"}, + {file = "pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b"}, + {file = "pyyaml-6.0.3-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:b865addae83924361678b652338317d1bd7e79b1f4596f96b96c77a5a34b34da"}, + {file = "pyyaml-6.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c3355370a2c156cffb25e876646f149d5d68f5e0a3ce86a5084dd0b64a994917"}, + {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3c5677e12444c15717b902a5798264fa7909e41153cdf9ef7ad571b704a63dd9"}, + {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5ed875a24292240029e4483f9d4a4b8a1ae08843b9c54f43fcc11e404532a8a5"}, + {file = "pyyaml-6.0.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0150219816b6a1fa26fb4699fb7daa9caf09eb1999f3b70fb6e786805e80375a"}, + {file = "pyyaml-6.0.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:fa160448684b4e94d80416c0fa4aac48967a969efe22931448d853ada8baf926"}, + {file = "pyyaml-6.0.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:27c0abcb4a5dac13684a37f76e701e054692a9b2d3064b70f5e4eb54810553d7"}, + {file = "pyyaml-6.0.3-cp39-cp39-win32.whl", hash = "sha256:1ebe39cb5fc479422b83de611d14e2c0d3bb2a18bbcb01f229ab3cfbd8fee7a0"}, + {file = "pyyaml-6.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:2e71d11abed7344e42a8849600193d15b6def118602c4c176f748e4583246007"}, + {file = "pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f"}, ] [[package]] name = "pyzstd" -version = "0.17.0" +version = "0.18.0" description = "Python bindings to Zstandard (zstd) compression library." optional = false python-versions = ">=3.5" groups = ["main"] files = [ - {file = "pyzstd-0.17.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8ac857abb4c4daea71f134e74af7fe16bcfeec40911d13cf9128ddc600d46d92"}, - {file = "pyzstd-0.17.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2d84e8d1cbecd3b661febf5ca8ce12c5e112cfeb8401ceedfb84ab44365298ac"}, - {file = "pyzstd-0.17.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f829fa1e7daac2e45b46656bdee13923150f329e53554aeaef75cceec706dd8c"}, - {file = "pyzstd-0.17.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:994de7a13bb683c190a1b2a0fb99fe0c542126946f0345360582d7d5e8ce8cda"}, - {file = "pyzstd-0.17.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d3eb213a22823e2155aa252d9093c62ac12d7a9d698a4b37c5613f99cb9de327"}, - {file = "pyzstd-0.17.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c451cfa31e70860334cc7dffe46e5178de1756642d972bc3a570fc6768673868"}, - {file = "pyzstd-0.17.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d66dc6f15249625e537ea4e5e64c195f50182556c3731f260b13c775b7888d6b"}, - {file = "pyzstd-0.17.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:308d4888083913fac2b7b6f4a88f67c0773d66db37e6060971c3f173cfa92d1e"}, - {file = "pyzstd-0.17.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:a3b636f37af9de52efb7dd2d2f15deaeabdeeacf8e69c29bf3e7e731931e6d66"}, - {file = "pyzstd-0.17.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:4c07391c67b496d851b18aa29ff552a552438187900965df57f64d5cf2100c40"}, - {file = "pyzstd-0.17.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:e8bd12a13313ffa27347d7abe20840dcd2092852ab835a8e86008f38f11bd5ac"}, - {file = "pyzstd-0.17.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2e27bfab45f9cdab0c336c747f493a00680a52a018a8bb7a1f787ddde4b29410"}, - {file = "pyzstd-0.17.0-cp310-cp310-win32.whl", hash = "sha256:7370c0978edfcb679419f43ec504c128463858a7ea78cf6d0538c39dfb36fce3"}, - {file = "pyzstd-0.17.0-cp310-cp310-win_amd64.whl", hash = "sha256:564f7aa66cda4acd9b2a8461ff0c6a6e39a977be3e2e7317411a9f7860d7338d"}, - {file = "pyzstd-0.17.0-cp310-cp310-win_arm64.whl", hash = "sha256:fccff3a37fa4c513fe1ebf94cb9dc0369c714da22b5671f78ddcbc7ec8f581cc"}, - {file = "pyzstd-0.17.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:06d1e7afafe86b90f3d763f83d2f6b6a437a8d75119fe1ff52b955eb9df04eaa"}, - {file = "pyzstd-0.17.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cc827657f644e4510211b49f5dab6b04913216bc316206d98f9a75214361f16e"}, - {file = "pyzstd-0.17.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ecffadaa2ee516ecea3e432ebf45348fa8c360017f03b88800dd312d62ecb063"}, - {file = "pyzstd-0.17.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:596de361948d3aad98a837c98fcee4598e51b608f7e0912e0e725f82e013f00f"}, - {file = "pyzstd-0.17.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dd3a8d0389c103e93853bf794b9a35ac5d0d11ca3e7e9f87e3305a10f6dfa6b2"}, - {file = "pyzstd-0.17.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1356f72c7b8bb99b942d582b61d1a93c5065e66b6df3914dac9f2823136c3228"}, - {file = "pyzstd-0.17.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f514c339b013b0b0a2ed8ea6e44684524223bd043267d7644d7c3a70e74a0dd"}, - {file = "pyzstd-0.17.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d4de16306821021c2d82a45454b612e2a8683d99bfb98cff51a883af9334bea0"}, - {file = "pyzstd-0.17.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:aeb9759c04b6a45c1b56be21efb0a738e49b0b75c4d096a38707497a7ff2be82"}, - {file = "pyzstd-0.17.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:7a5b31ddeada0027e67464d99f09167cf08bab5f346c3c628b2d3c84e35e239a"}, - {file = "pyzstd-0.17.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:8338e4e91c52af839abcf32f1f65f3b21e2597ffe411609bdbdaf10274991bd0"}, - {file = "pyzstd-0.17.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:628e93862feb372b4700085ec4d1d389f1283ac31900af29591ae01019910ff3"}, - {file = "pyzstd-0.17.0-cp311-cp311-win32.whl", hash = "sha256:c27773f9c95ebc891cfcf1ef282584d38cde0a96cb8d64127953ad752592d3d7"}, - {file = "pyzstd-0.17.0-cp311-cp311-win_amd64.whl", hash = "sha256:c043a5766e00a2b7844705c8fa4563b7c195987120afee8f4cf594ecddf7e9ac"}, - {file = "pyzstd-0.17.0-cp311-cp311-win_arm64.whl", hash = "sha256:efd371e41153ef55bf51f97e1ce4c1c0b05ceb59ed1d8972fc9aa1e9b20a790f"}, - {file = "pyzstd-0.17.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2ac330fc4f64f97a411b6f3fc179d2fe3050b86b79140e75a9a6dd9d6d82087f"}, - {file = "pyzstd-0.17.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:725180c0c4eb2e643b7048ebfb45ddf43585b740535907f70ff6088f5eda5096"}, - {file = "pyzstd-0.17.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9c20fe0a60019685fa1f7137cb284f09e3f64680a503d9c0d50be4dd0a3dc5ec"}, - {file = "pyzstd-0.17.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d97f7aaadc3b6e2f8e51bfa6aa203ead9c579db36d66602382534afaf296d0db"}, - {file = "pyzstd-0.17.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:42dcb34c5759b59721997036ff2d94210515d3ef47a9de84814f1c51a1e07e8a"}, - {file = "pyzstd-0.17.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6bf05e18be6f6c003c7129e2878cffd76fcbebda4e7ebd7774e34ae140426cbf"}, - {file = "pyzstd-0.17.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c40f7c3a5144aa4fbccf37c30411f6b1db4c0f2cb6ad4df470b37929bffe6ca0"}, - {file = "pyzstd-0.17.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9efd4007f8369fd0890701a4fc77952a0a8c4cb3bd30f362a78a1adfb3c53c12"}, - {file = "pyzstd-0.17.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5f8add139b5fd23b95daa844ca13118197f85bd35ce7507e92fcdce66286cc34"}, - {file = "pyzstd-0.17.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:259a60e8ce9460367dcb4b34d8b66e44ca3d8c9c30d53ed59ae7037622b3bfc7"}, - {file = "pyzstd-0.17.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:86011a93cc3455c5d2e35988feacffbf2fa106812a48e17eb32c2a52d25a95b3"}, - {file = "pyzstd-0.17.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:425c31bc3de80313054e600398e4f1bd229ee61327896d5d015e2cd0283c9012"}, - {file = "pyzstd-0.17.0-cp312-cp312-win32.whl", hash = "sha256:7c4b88183bb36eb2cebbc0352e6e9fe8e2d594f15859ae1ef13b63ebc58be158"}, - {file = "pyzstd-0.17.0-cp312-cp312-win_amd64.whl", hash = "sha256:3c31947e0120468342d74e0fa936d43f7e1dad66a2262f939735715aa6c730e8"}, - {file = "pyzstd-0.17.0-cp312-cp312-win_arm64.whl", hash = "sha256:1d0346418abcef11507356a31bef5470520f6a5a786d4e2c69109408361b1020"}, - {file = "pyzstd-0.17.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:6cd1a1d37a7abe9c01d180dad699e3ac3889e4f48ac5dcca145cc46b04e9abd2"}, - {file = "pyzstd-0.17.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1a44fd596eda06b6265dc0358d5b309715a93f8e96e8a4b5292c2fe0e14575b3"}, - {file = "pyzstd-0.17.0-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a99b37453f92f0691b2454d0905bbf2f430522612f6f12bbc81133ad947eb97"}, - {file = "pyzstd-0.17.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63d864e9f9e624a466070a121ace9d9cbf579eac4ed575dee3b203ab1b3cbeee"}, - {file = "pyzstd-0.17.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e58bc02b055f96d1f83c791dd197d8c80253275a56cd84f917a006e9f528420d"}, - {file = "pyzstd-0.17.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3e62df7c0ba74618481149c849bc3ed7d551b9147e1274b4b3170bbcc0bfcc0a"}, - {file = "pyzstd-0.17.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42ecdd7136294f1becb8e57441df00eaa6dfd7444a8b0c96a1dfba5c81b066e7"}, - {file = "pyzstd-0.17.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:be07a57af75f99fc39b8e2d35f8fb823ecd7ef099cd1f6203829a5094a991ae2"}, - {file = "pyzstd-0.17.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:0d41e6f7ec2a70dab4982157a099562de35a6735c890945b4cebb12fb7eb0be0"}, - {file = "pyzstd-0.17.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f482d906426756e7cc9a43f500fee907e1b3b4e9c04d42d58fb1918c6758759b"}, - {file = "pyzstd-0.17.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:827327b35605265e1d05a2f6100244415e8f2728bb75c951736c9288415908d7"}, - {file = "pyzstd-0.17.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6a55008f80e3390e4f37bd9353830f1675f271d13d6368d2f1dc413b7c6022b3"}, - {file = "pyzstd-0.17.0-cp313-cp313-win32.whl", hash = "sha256:a4be186c0df86d4d95091c759a06582654f2b93690503b1c24d77f537d0cf5d0"}, - {file = "pyzstd-0.17.0-cp313-cp313-win_amd64.whl", hash = "sha256:251a0b599bd224ec66f39165ddb2f959d0a523938e3bbfa82d8188dc03a271a2"}, - {file = "pyzstd-0.17.0-cp313-cp313-win_arm64.whl", hash = "sha256:ce6d5fd908fd3ddec32d1c1a5a7a15b9d7737d0ef2ab20fe1e8261da61395017"}, - {file = "pyzstd-0.17.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d5cb23c3c4ba4105a518cfbe8a566f9482da26f4bc8c1c865fd66e8e266be071"}, - {file = "pyzstd-0.17.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:10b5d9215890a24f22505b68add26beeb2e3858bbe738a7ee339f0db8e29d033"}, - {file = "pyzstd-0.17.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:db1cff52fd24caf42a2cfb7e5d8dc822b93e9fac5dab505d0bd22e302061e2d2"}, - {file = "pyzstd-0.17.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3caad3106e0e80f76acbb19c15e1e834ba6fd44dd4c82719ef8e3374f7fafd3"}, - {file = "pyzstd-0.17.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b7e52e1de31b935e27568742145d8b4d0f204a1605e36f4e1e2846e0d39bed98"}, - {file = "pyzstd-0.17.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eaa046bc9e751c4083102f3624a52bbb66e20e7aa3e28673543b22e69d9b57cd"}, - {file = "pyzstd-0.17.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0cc9310bdb7cf2c70098aab40fb6bf68faaf0149110c6ef668996e7957e0147a"}, - {file = "pyzstd-0.17.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:3619075966456783818904f9d9e213c6fe2e583d5beb545fa1968b1848781e0f"}, - {file = "pyzstd-0.17.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3844f8c7d7850580423b1b33601b016b3b913d18deb6fe14a7641b9c2714275c"}, - {file = "pyzstd-0.17.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ab53f91280b7b639c47bb2048e01182230e7cf3f0f0980bdb405b4241cfb705e"}, - {file = "pyzstd-0.17.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:75252ee53e53a819ea7ac4271f66686018bc8b98ef12628269f099c10d881077"}, - {file = "pyzstd-0.17.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:0795afdaa34e1ed7f3d7552100cd57a1cef9d7310b386a893e0890e9a585b427"}, - {file = "pyzstd-0.17.0-cp39-cp39-win32.whl", hash = "sha256:f7316be5a5246b6bbdd807c7a4f10382b6b02c3afc5ae6acd2e266a84f715493"}, - {file = "pyzstd-0.17.0-cp39-cp39-win_amd64.whl", hash = "sha256:121e8fac3e24b881fed59d638100b80c34f6347c02d2f24580f633451939f2d7"}, - {file = "pyzstd-0.17.0-cp39-cp39-win_arm64.whl", hash = "sha256:fe36ccda67f73e909ac305984fe13b7b5a79296706d095a80472ada4413174c2"}, - {file = "pyzstd-0.17.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:1c56f99c697130f39702e07ab9fa0bb4c929c7bfe47c0a488dea732bd8a8752a"}, - {file = "pyzstd-0.17.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:152bae1b2197bcd41fc143f93acd23d474f590162547484ca04ce5874c4847de"}, - {file = "pyzstd-0.17.0-pp310-pypy310_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e2ddbbd7614922e52018ba3e7bb4cbe6f25b230096831d97916b8b89be8cd0cb"}, - {file = "pyzstd-0.17.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f6f3f152888825f71fd2cf2499f093fac252a5c1fa15ab8747110b3dc095f6b"}, - {file = "pyzstd-0.17.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d00a2d2bddf51c7bf32c17dc47f0f49f47ebae07c2528b9ee8abf1f318ac193"}, - {file = "pyzstd-0.17.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d79e3eff07217707a92c1a6a9841c2466bfcca4d00fea0bea968f4034c27a256"}, - {file = "pyzstd-0.17.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:3ce6bac0c4c032c5200647992a8efcb9801c918633ebe11cceba946afea152d9"}, - {file = "pyzstd-0.17.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:a00998144b35be7c485a383f739fe0843a784cd96c3f1f2f53f1a249545ce49a"}, - {file = "pyzstd-0.17.0-pp311-pypy311_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8521d7bbd00e0e1c1fd222c1369a7600fba94d24ba380618f9f75ee0c375c277"}, - {file = "pyzstd-0.17.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da65158c877eac78dcc108861d607c02fb3703195c3a177f2687e0bcdfd519d0"}, - {file = "pyzstd-0.17.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:226ca0430e2357abae1ade802585231a2959b010ec9865600e416652121ba80b"}, - {file = "pyzstd-0.17.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:e3a19e8521c145a0e2cd87ca464bf83604000c5454f7e0746092834fd7de84d1"}, - {file = "pyzstd-0.17.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:56ed2de4717844ffdebb5c312ec7e7b8eb2b69eb72883bbfe472ba2c872419e6"}, - {file = "pyzstd-0.17.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:fc61c47ca631241081c0c99895a1feb56dab4beab37cac7d1f9f18aff06962eb"}, - {file = "pyzstd-0.17.0-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd61757a4020590dad6c20fdbf37c054ed9f349591a0d308c3c03c0303ce221"}, - {file = "pyzstd-0.17.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2d6cce91a8ac8ae1aab06684a8bf0dee088405de7f451e1e89776ddc1f40074"}, - {file = "pyzstd-0.17.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc668b67a13bf6213d0a9c09edc1f4842ed680b92fc3c9361f55a904903bfd1f"}, - {file = "pyzstd-0.17.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a67d7ef18715875b31127eb90075c03ced722fd87902b34bca4b807a2ce1e4d9"}, - {file = "pyzstd-0.17.0.tar.gz", hash = "sha256:d84271f8baa66c419204c1dd115a4dec8b266f8a2921da21b81764fa208c1db6"}, + {file = "pyzstd-0.18.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:79bb84d866bf57ad2c4bc6b8247628b38e965c4f66288f887bf90f546a42ae04"}, + {file = "pyzstd-0.18.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c0576c48e2f7a2c457538414a6197397c343b1bf5bfe9332b049afd0366c0c92"}, + {file = "pyzstd-0.18.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ea7702484795ee3c16c48a03d990123e833f1e1d6baabbe9a53256238eb04cbc"}, + {file = "pyzstd-0.18.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9c412ac29a9ebb76c8c40f2df146327b460ce184bbbdaa5bc9257317dce4caa8"}, + {file = "pyzstd-0.18.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:36baae4201196c2ec6567faf4a3f19c68211efc2fca30836c885b848ed057f66"}, + {file = "pyzstd-0.18.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f6d9c8a535af243c5a19f2d66c3733595ab633e00b97237d877e70e8389edc5"}, + {file = "pyzstd-0.18.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a533550740ce8c721aae27b377fb1160df68a9f457f16015ec8e47547a033dfc"}, + {file = "pyzstd-0.18.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fdd76049c8ccbb98276cfa78d807b4a497ec6bad2603361eceae993c6130e5bf"}, + {file = "pyzstd-0.18.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:09b73fe07a8d81898ef1575cb3063816168abb3305c1a9f30110383b61a4ee92"}, + {file = "pyzstd-0.18.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:6baf9fd75d0af4f5d677b6e2d8dd3deb359c4ec2250c8536fe5ea48fd9305199"}, + {file = "pyzstd-0.18.0-cp310-cp310-win32.whl", hash = "sha256:c0634ab42226d2ad96c94d57fd242df2ca9417350c2969eb97c8c61d9574ba69"}, + {file = "pyzstd-0.18.0-cp310-cp310-win_amd64.whl", hash = "sha256:ec99569321a99b9868666c85a5846151f9a16b6a222b59b2570e2ddeefd4d80c"}, + {file = "pyzstd-0.18.0-cp310-cp310-win_arm64.whl", hash = "sha256:85371149cc1d8168461981084438b9f2f139c1699e989fef44562f7504ba0632"}, + {file = "pyzstd-0.18.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:848914835a8a984d4c5fad2355dc66f0aca979b35ec22753c9e694be8e98403c"}, + {file = "pyzstd-0.18.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3938fea87fe83113b5d8ec2925bb265b4c540e374bb0ec73e5528de58d68c393"}, + {file = "pyzstd-0.18.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9af4bcde7dde46ca7e82a4c6f5fda1760bcbfd15525dbea36fe625263ef06b5e"}, + {file = "pyzstd-0.18.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:15d9419d173d26de25342235256aba363190e48e3fd8a8988420a26221b45320"}, + {file = "pyzstd-0.18.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0b84f75f0494087afad31363e80a3463d1f32a0a6265f1a24660e6422b2b6fa6"}, + {file = "pyzstd-0.18.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2cfcdf0e46020bda2e98814464ca3ae830da83937c4c61776bf8835c7094214e"}, + {file = "pyzstd-0.18.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8551b6bc3690fb76e730967a628b6aab0d9331c38a41f5cddb546be994771191"}, + {file = "pyzstd-0.18.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:6883b47a4d5d5489890e24e74ef14c1f16dcd68bb326b86911ae0e254e33e4b7"}, + {file = "pyzstd-0.18.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:929dec930296362ce03fee81877fa93a68ca4de3af75fdfa96ecbe0e366b2ee3"}, + {file = "pyzstd-0.18.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:278c80fdeaf857b620295cc815a31f6478fcb217d476ac889985a43b2b67e9bd"}, + {file = "pyzstd-0.18.0-cp311-cp311-win32.whl", hash = "sha256:0d1b678644894e49b5a448f02eebe0ac31bde6f51813168f5ff223d7212e1974"}, + {file = "pyzstd-0.18.0-cp311-cp311-win_amd64.whl", hash = "sha256:8285a464aed201b166bb0d2f4667485b61b607cf89f12943b1f21f7e84cb4550"}, + {file = "pyzstd-0.18.0-cp311-cp311-win_arm64.whl", hash = "sha256:942badf996589e5ab6cbdd0f7dd33f5dc2cd7ed0b65441c96b9a12ffa7700d51"}, + {file = "pyzstd-0.18.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5eef13ee3e230e50c01b288d581664e8758f7b831271f6f32cfc29823a6ab365"}, + {file = "pyzstd-0.18.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f78d6ef80d2f355b5bc1a897e9aa58659e85170b3fa268f3211c4979c768264c"}, + {file = "pyzstd-0.18.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:394175aeeb4e2255ff5340b32f6db79375b3ffb25514fe4c1439015a7f335ec2"}, + {file = "pyzstd-0.18.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3250c551f526d3b966cf4a2199a8d9538dc5c7083b7a26a45f305f8f2ab20a06"}, + {file = "pyzstd-0.18.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a99ca80053ca37be21f05f6c4152c70777e0eface72b08277cb4b10b6d286e79"}, + {file = "pyzstd-0.18.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5dc4488536e87ff0aac698b9cd65f2913ac87417b3952d80be32463c8e95cc35"}, + {file = "pyzstd-0.18.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c12da158f6ec1180be0a3d6f531050dfc1357a25e5d0fd8dd99d4506d2a3f448"}, + {file = "pyzstd-0.18.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:f9a7d6bff36dfbe87dce1730e4b70d6ab49058a6f8ea22e85b33642491a2d053"}, + {file = "pyzstd-0.18.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:0f56086bf8019f7c809a406dcc182ce0fb0d3623a9edf351ed80dbb484514613"}, + {file = "pyzstd-0.18.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1eb69217ad9b760537e93f2d578c7927b788a9cac0e2104e536855a2797b5b09"}, + {file = "pyzstd-0.18.0-cp312-cp312-win32.whl", hash = "sha256:05ce49412c7aef970e0a6be8e9add4748bc474a7f13533a14555642022f871e9"}, + {file = "pyzstd-0.18.0-cp312-cp312-win_amd64.whl", hash = "sha256:e951c3013b9df479cff758d578b83837b2531d02fb6c3e59166a756795697e19"}, + {file = "pyzstd-0.18.0-cp312-cp312-win_arm64.whl", hash = "sha256:33b54781c66a86e33c93c89ae426811d0aa35a216a23116fc5d5162449284305"}, + {file = "pyzstd-0.18.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:65117997d1e10e9b41336c90c2c4877c8d27533f753272805ff39df15fd5298a"}, + {file = "pyzstd-0.18.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8550efbfb5944343666d0e79d6a3687adcbeb4dbf17aa743146a25e72d12d47f"}, + {file = "pyzstd-0.18.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ac61854c4a77df66695540549a89f4c67039e4181a9158b8646425f1d56d947a"}, + {file = "pyzstd-0.18.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4c453369483f67480f86d67a7b63ef22827db65e7f0d4bec7992bb81751a94b9"}, + {file = "pyzstd-0.18.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4ef4b757b2df808ac15058fc2aa41e07d93843ee5a95629ff51eb6e8f1950951"}, + {file = "pyzstd-0.18.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b42529770febd331e23c5e8a68e9899acb0cc0806ee4c970354806c0ceeec6c7"}, + {file = "pyzstd-0.18.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7f54d13c269cdc37d2f73c9b3e70c6d2bb168dec768a472d54c2ed830bb19fb9"}, + {file = "pyzstd-0.18.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:e6686460ca4be536dca1b6f2f80055f383a78e92e68e03a14806428572c4fdba"}, + {file = "pyzstd-0.18.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:8da3978d7de9095cacc5089bd0c435ab84ebd127e0979cd31fa1b216111644af"}, + {file = "pyzstd-0.18.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1ebc87e6e50547cff97e07c3fed9999d79b6327c9c4143c3049a7cfeacb2cdba"}, + {file = "pyzstd-0.18.0-cp313-cp313-win32.whl", hash = "sha256:2dd203f2534b16dea2761394fda4e0f3c465a5109ae6450bdaada67e6ac14a45"}, + {file = "pyzstd-0.18.0-cp313-cp313-win_amd64.whl", hash = "sha256:98f43488f88b859291d6bdc51cc7793d1eab17aa9382b17d762944bbb8567c98"}, + {file = "pyzstd-0.18.0-cp313-cp313-win_arm64.whl", hash = "sha256:cff8922e25e19d8fbd95b53f451e637bc80e826ab53c8777a885d4e99d1c0c2d"}, + {file = "pyzstd-0.18.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:67f795ec745cfd6930cdaf5118fcdd8d87ce02b07b254d37efe75afd33ce9917"}, + {file = "pyzstd-0.18.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a8a589673b9b417a084e393f18d09a16b67b87a80f80da6d3b4f84dd983c9b3d"}, + {file = "pyzstd-0.18.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fdaee8c33f96a6568225e821e6cc33045917628ae0bc7d8d3855332085c1aa7c"}, + {file = "pyzstd-0.18.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:42bf45d8e835d7c9c0bef98ff703143a5129edf09ef6c3b757037cbf79eabcaa"}, + {file = "pyzstd-0.18.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2f4dff2a15e2047baea9359d3a547dee80f61887f17e0f23190b4b932fd617e4"}, + {file = "pyzstd-0.18.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2ed87932d6c534fc8921f7d44a4dadb32881e10ebc68935175a2cba254f5cc83"}, + {file = "pyzstd-0.18.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7d08a372b2b7fa1fd24217424e13d3d794e01299c43c8bd55f50934ef0785779"}, + {file = "pyzstd-0.18.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:e8403108172e24622f51732a336a89fe32bf3842965e0dc677c65df3a562f3ad"}, + {file = "pyzstd-0.18.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:5604eeb7f00ec308b7e878dae92abfc4eee2e5d238765a62d4fadc0d57bbbff3"}, + {file = "pyzstd-0.18.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d6b300c5240409f1e7ab9972ab2a880a1949447d8414dbc11d89c10bfcb31aa5"}, + {file = "pyzstd-0.18.0-cp314-cp314-win32.whl", hash = "sha256:83f4fe1409a59c45a5e6fccb4d451e1e3dd03a5fabebd2dd6ba651468f54025e"}, + {file = "pyzstd-0.18.0-cp314-cp314-win_amd64.whl", hash = "sha256:73c3dcd9a16f1669ed6eef0dad1d840b7dd6070ab7d48719171ca691101e7975"}, + {file = "pyzstd-0.18.0-cp314-cp314-win_arm64.whl", hash = "sha256:61333bbb337b9746284624ed14f6238838dfae1e395691ba49f227015374f760"}, + {file = "pyzstd-0.18.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9bccd16621016b83c2d5d40408806a841bbca2860370dca5ef0e3db005417aca"}, + {file = "pyzstd-0.18.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c7ee6747541594a5851bae720d5ab070ba9ef644df779507f35819ea61fd83fd"}, + {file = "pyzstd-0.18.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3ea0d70b4ec72b9d5feae4ec665ef8a4cd48f442921f2100117229c900a5a713"}, + {file = "pyzstd-0.18.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d581aeeba9a3ed13e304b0efc27efdf310b58c1e69ebb99a08e0eeea3a392310"}, + {file = "pyzstd-0.18.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d582d2fab7cc3e7606c2b09093f914e6e8b942ec52aa992a3a25d9d3ed7ba295"}, + {file = "pyzstd-0.18.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a25a72afa7d66d47a881e475ffe88d9961b36052bf6a512af3b84de22b20d41f"}, + {file = "pyzstd-0.18.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5b4feed895f32b314f2b3aa3ba6a4e0ce903c6764f31ad78e68b6c3fa31415ac"}, + {file = "pyzstd-0.18.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:20d9524adbc4efc8a1680e59cc325bc73ff56bf70bb54d233c3540efcb7bf476"}, + {file = "pyzstd-0.18.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:72c25d14217854883b571f101253d39443ea2f226f85cf3223b4d4a4d644618d"}, + {file = "pyzstd-0.18.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c335605ac7d018ca2d4d68cc0bac10e3c4ccf8e9686972dfc569a4df53f7a8d3"}, + {file = "pyzstd-0.18.0-cp39-cp39-win32.whl", hash = "sha256:64ebf9bd8065388d778c4ab6d9c4e913c00633abcfbf55236202dd0398520cc0"}, + {file = "pyzstd-0.18.0-cp39-cp39-win_amd64.whl", hash = "sha256:4a32751ac634eb685bec42935b0f6e494f018843da09596da3f2a0072ae8273b"}, + {file = "pyzstd-0.18.0-cp39-cp39-win_arm64.whl", hash = "sha256:6b64efb254fdc3c90ed4c74185beee62c24e517288aacfb3abd95c127e6f8f52"}, + {file = "pyzstd-0.18.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:35934369fcdfde6fb932f88fa441337c8ddaf4b08e7b0b12952010f0ba2082f7"}, + {file = "pyzstd-0.18.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:55b8e12c9657359a697440e88a8535d1a771025e5d8f1c3087ad69ba11bee6d2"}, + {file = "pyzstd-0.18.0-pp310-pypy310_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:134d33d3e56b5083c8f827b63254c2abf85d6ace2b323e69d28e3954b5b71883"}, + {file = "pyzstd-0.18.0-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a6c4bffa0157ef9e5cfa32413a5a79448e5affadece4982df274f1b5aae3a680"}, + {file = "pyzstd-0.18.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:8c36824d94cf77997a899b60886cc2be3ac969083f1d74eb4dd4127234ba50a4"}, + {file = "pyzstd-0.18.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:788e0889db436cd6d16a3b490006ab80a913d8ce6f46db127f1888066ff4560b"}, + {file = "pyzstd-0.18.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:5e70b7c36a40d7f946bf6391a206374b057299735d366fad6524d3b9f392441f"}, + {file = "pyzstd-0.18.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:571c5f71622943387370f76de8cc0de3d5c6217ab0f38386cb127665e4e09275"}, + {file = "pyzstd-0.18.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de0b730f374b583894d58b79cff76569540baf1e84bc493be191d3128b58e559"}, + {file = "pyzstd-0.18.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:b32184013f33dba2fabcdda89f2a83289f5b717a0c2477cda764e53fdafec7ee"}, + {file = "pyzstd-0.18.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:27c281abfc2f13f19df92793f66e12cd0a19038ccbc02684af2a14bce664fdc4"}, + {file = "pyzstd-0.18.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:7313f3a9bd2cb11158e5eaab3d5d2cd6b4582702e383a08ebb8273d0d45c3e49"}, + {file = "pyzstd-0.18.0-pp39-pypy39_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3ec4ae014abf835bd9995ee1b318fdf4e955ffb8439838373bdc19c80d51a541"}, + {file = "pyzstd-0.18.0-pp39-pypy39_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:94c2f15f0e67acf89bec97ea276f7a5ad4e6d0267f62f12424bf044a0de280a0"}, + {file = "pyzstd-0.18.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:898e41170fde5aa73105a0262572c286bafc5f24c7b4cf131168d9b198e4c586"}, + {file = "pyzstd-0.18.0.tar.gz", hash = "sha256:81b6851ab1ca2e5f2c709e896a1362e3065a64f271f43db77fb7d5e4a78e9861"}, ] [package.dependencies] @@ -4403,136 +3846,38 @@ files = [ ] [[package]] -name = "referencing" -version = "0.36.2" -description = "JSON Referencing + Python" +name = "regipy" +version = "5.2.0" +description = "Python Registry Parser" +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "regipy-5.2.0-py3-none-any.whl", hash = "sha256:bd00d067bb3643eea25d2da1250d58e31101e8d013499673bfd3c00fa28f77ef"}, + {file = "regipy-5.2.0.tar.gz", hash = "sha256:cf1e977625e9dcf6fe4facb0cffa21ce6300bf585dee6d451e5f03b26fc61336"}, +] + +[package.dependencies] +attrs = ">=21" +construct = ">=2.10" +inflection = ">=0.5.1" +pytz = "*" + +[package.extras] +cli = ["click (>=7.0.0)", "tabulate"] +full = ["click (>=7.0.0)", "libfwps-python (>=20240310)", "libfwsi-python (>=20240315)", "tabulate"] +test = ["pytest", "pytest-flake8"] + +[[package]] +name = "requests" +version = "2.32.5" +description = "Python HTTP for Humans." optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "referencing-0.36.2-py3-none-any.whl", hash = "sha256:e8699adbbf8b5c7de96d8ffa0eb5c158b3beafce084968e2ea8bb08c6794dcd0"}, - {file = "referencing-0.36.2.tar.gz", hash = "sha256:df2e89862cd09deabbdba16944cc3f10feb6b3e6f18e902f7cc25609a34775aa"}, -] - -[package.dependencies] -attrs = ">=22.2.0" -rpds-py = ">=0.7.0" -typing-extensions = {version = ">=4.4.0", markers = "python_version < \"3.13\""} - -[[package]] -name = "regex" -version = "2024.11.6" -description = "Alternative regular expression module, to replace re." -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "regex-2024.11.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ff590880083d60acc0433f9c3f713c51f7ac6ebb9adf889c79a261ecf541aa91"}, - {file = "regex-2024.11.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:658f90550f38270639e83ce492f27d2c8d2cd63805c65a13a14d36ca126753f0"}, - {file = "regex-2024.11.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:164d8b7b3b4bcb2068b97428060b2a53be050085ef94eca7f240e7947f1b080e"}, - {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3660c82f209655a06b587d55e723f0b813d3a7db2e32e5e7dc64ac2a9e86fde"}, - {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d22326fcdef5e08c154280b71163ced384b428343ae16a5ab2b3354aed12436e"}, - {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f1ac758ef6aebfc8943560194e9fd0fa18bcb34d89fd8bd2af18183afd8da3a2"}, - {file = "regex-2024.11.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:997d6a487ff00807ba810e0f8332c18b4eb8d29463cfb7c820dc4b6e7562d0cf"}, - {file = "regex-2024.11.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:02a02d2bb04fec86ad61f3ea7f49c015a0681bf76abb9857f945d26159d2968c"}, - {file = "regex-2024.11.6-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f02f93b92358ee3f78660e43b4b0091229260c5d5c408d17d60bf26b6c900e86"}, - {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:06eb1be98df10e81ebaded73fcd51989dcf534e3c753466e4b60c4697a003b67"}, - {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:040df6fe1a5504eb0f04f048e6d09cd7c7110fef851d7c567a6b6e09942feb7d"}, - {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fdabbfc59f2c6edba2a6622c647b716e34e8e3867e0ab975412c5c2f79b82da2"}, - {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8447d2d39b5abe381419319f942de20b7ecd60ce86f16a23b0698f22e1b70008"}, - {file = "regex-2024.11.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:da8f5fc57d1933de22a9e23eec290a0d8a5927a5370d24bda9a6abe50683fe62"}, - {file = "regex-2024.11.6-cp310-cp310-win32.whl", hash = "sha256:b489578720afb782f6ccf2840920f3a32e31ba28a4b162e13900c3e6bd3f930e"}, - {file = "regex-2024.11.6-cp310-cp310-win_amd64.whl", hash = "sha256:5071b2093e793357c9d8b2929dfc13ac5f0a6c650559503bb81189d0a3814519"}, - {file = "regex-2024.11.6-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5478c6962ad548b54a591778e93cd7c456a7a29f8eca9c49e4f9a806dcc5d638"}, - {file = "regex-2024.11.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2c89a8cc122b25ce6945f0423dc1352cb9593c68abd19223eebbd4e56612c5b7"}, - {file = "regex-2024.11.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:94d87b689cdd831934fa3ce16cc15cd65748e6d689f5d2b8f4f4df2065c9fa20"}, - {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1062b39a0a2b75a9c694f7a08e7183a80c63c0d62b301418ffd9c35f55aaa114"}, - {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:167ed4852351d8a750da48712c3930b031f6efdaa0f22fa1933716bfcd6bf4a3"}, - {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d548dafee61f06ebdb584080621f3e0c23fff312f0de1afc776e2a2ba99a74f"}, - {file = "regex-2024.11.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2a19f302cd1ce5dd01a9099aaa19cae6173306d1302a43b627f62e21cf18ac0"}, - {file = "regex-2024.11.6-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bec9931dfb61ddd8ef2ebc05646293812cb6b16b60cf7c9511a832b6f1854b55"}, - {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9714398225f299aa85267fd222f7142fcb5c769e73d7733344efc46f2ef5cf89"}, - {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:202eb32e89f60fc147a41e55cb086db2a3f8cb82f9a9a88440dcfc5d37faae8d"}, - {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:4181b814e56078e9b00427ca358ec44333765f5ca1b45597ec7446d3a1ef6e34"}, - {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:068376da5a7e4da51968ce4c122a7cd31afaaec4fccc7856c92f63876e57b51d"}, - {file = "regex-2024.11.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ac10f2c4184420d881a3475fb2c6f4d95d53a8d50209a2500723d831036f7c45"}, - {file = "regex-2024.11.6-cp311-cp311-win32.whl", hash = "sha256:c36f9b6f5f8649bb251a5f3f66564438977b7ef8386a52460ae77e6070d309d9"}, - {file = "regex-2024.11.6-cp311-cp311-win_amd64.whl", hash = "sha256:02e28184be537f0e75c1f9b2f8847dc51e08e6e171c6bde130b2687e0c33cf60"}, - {file = "regex-2024.11.6-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:52fb28f528778f184f870b7cf8f225f5eef0a8f6e3778529bdd40c7b3920796a"}, - {file = "regex-2024.11.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fdd6028445d2460f33136c55eeb1f601ab06d74cb3347132e1c24250187500d9"}, - {file = "regex-2024.11.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:805e6b60c54bf766b251e94526ebad60b7de0c70f70a4e6210ee2891acb70bf2"}, - {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b85c2530be953a890eaffde05485238f07029600e8f098cdf1848d414a8b45e4"}, - {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bb26437975da7dc36b7efad18aa9dd4ea569d2357ae6b783bf1118dabd9ea577"}, - {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:abfa5080c374a76a251ba60683242bc17eeb2c9818d0d30117b4486be10c59d3"}, - {file = "regex-2024.11.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b7fa6606c2881c1db9479b0eaa11ed5dfa11c8d60a474ff0e095099f39d98e"}, - {file = "regex-2024.11.6-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0c32f75920cf99fe6b6c539c399a4a128452eaf1af27f39bce8909c9a3fd8cbe"}, - {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:982e6d21414e78e1f51cf595d7f321dcd14de1f2881c5dc6a6e23bbbbd68435e"}, - {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a7c2155f790e2fb448faed6dd241386719802296ec588a8b9051c1f5c481bc29"}, - {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:149f5008d286636e48cd0b1dd65018548944e495b0265b45e1bffecce1ef7f39"}, - {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:e5364a4502efca094731680e80009632ad6624084aff9a23ce8c8c6820de3e51"}, - {file = "regex-2024.11.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0a86e7eeca091c09e021db8eb72d54751e527fa47b8d5787caf96d9831bd02ad"}, - {file = "regex-2024.11.6-cp312-cp312-win32.whl", hash = "sha256:32f9a4c643baad4efa81d549c2aadefaeba12249b2adc5af541759237eee1c54"}, - {file = "regex-2024.11.6-cp312-cp312-win_amd64.whl", hash = "sha256:a93c194e2df18f7d264092dc8539b8ffb86b45b899ab976aa15d48214138e81b"}, - {file = "regex-2024.11.6-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a6ba92c0bcdf96cbf43a12c717eae4bc98325ca3730f6b130ffa2e3c3c723d84"}, - {file = "regex-2024.11.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:525eab0b789891ac3be914d36893bdf972d483fe66551f79d3e27146191a37d4"}, - {file = "regex-2024.11.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:086a27a0b4ca227941700e0b31425e7a28ef1ae8e5e05a33826e17e47fbfdba0"}, - {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bde01f35767c4a7899b7eb6e823b125a64de314a8ee9791367c9a34d56af18d0"}, - {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b583904576650166b3d920d2bcce13971f6f9e9a396c673187f49811b2769dc7"}, - {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c4de13f06a0d54fa0d5ab1b7138bfa0d883220965a29616e3ea61b35d5f5fc7"}, - {file = "regex-2024.11.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3cde6e9f2580eb1665965ce9bf17ff4952f34f5b126beb509fee8f4e994f143c"}, - {file = "regex-2024.11.6-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0d7f453dca13f40a02b79636a339c5b62b670141e63efd511d3f8f73fba162b3"}, - {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:59dfe1ed21aea057a65c6b586afd2a945de04fc7db3de0a6e3ed5397ad491b07"}, - {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b97c1e0bd37c5cd7902e65f410779d39eeda155800b65fc4d04cc432efa9bc6e"}, - {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f9d1e379028e0fc2ae3654bac3cbbef81bf3fd571272a42d56c24007979bafb6"}, - {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:13291b39131e2d002a7940fb176e120bec5145f3aeb7621be6534e46251912c4"}, - {file = "regex-2024.11.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4f51f88c126370dcec4908576c5a627220da6c09d0bff31cfa89f2523843316d"}, - {file = "regex-2024.11.6-cp313-cp313-win32.whl", hash = "sha256:63b13cfd72e9601125027202cad74995ab26921d8cd935c25f09c630436348ff"}, - {file = "regex-2024.11.6-cp313-cp313-win_amd64.whl", hash = "sha256:2b3361af3198667e99927da8b84c1b010752fa4b1115ee30beaa332cabc3ef1a"}, - {file = "regex-2024.11.6-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:3a51ccc315653ba012774efca4f23d1d2a8a8f278a6072e29c7147eee7da446b"}, - {file = "regex-2024.11.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ad182d02e40de7459b73155deb8996bbd8e96852267879396fb274e8700190e3"}, - {file = "regex-2024.11.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ba9b72e5643641b7d41fa1f6d5abda2c9a263ae835b917348fc3c928182ad467"}, - {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40291b1b89ca6ad8d3f2b82782cc33807f1406cf68c8d440861da6304d8ffbbd"}, - {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cdf58d0e516ee426a48f7b2c03a332a4114420716d55769ff7108c37a09951bf"}, - {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a36fdf2af13c2b14738f6e973aba563623cb77d753bbbd8d414d18bfaa3105dd"}, - {file = "regex-2024.11.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1cee317bfc014c2419a76bcc87f071405e3966da434e03e13beb45f8aced1a6"}, - {file = "regex-2024.11.6-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50153825ee016b91549962f970d6a4442fa106832e14c918acd1c8e479916c4f"}, - {file = "regex-2024.11.6-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ea1bfda2f7162605f6e8178223576856b3d791109f15ea99a9f95c16a7636fb5"}, - {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:df951c5f4a1b1910f1a99ff42c473ff60f8225baa1cdd3539fe2819d9543e9df"}, - {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:072623554418a9911446278f16ecb398fb3b540147a7828c06e2011fa531e773"}, - {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:f654882311409afb1d780b940234208a252322c24a93b442ca714d119e68086c"}, - {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:89d75e7293d2b3e674db7d4d9b1bee7f8f3d1609428e293771d1a962617150cc"}, - {file = "regex-2024.11.6-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:f65557897fc977a44ab205ea871b690adaef6b9da6afda4790a2484b04293a5f"}, - {file = "regex-2024.11.6-cp38-cp38-win32.whl", hash = "sha256:6f44ec28b1f858c98d3036ad5d7d0bfc568bdd7a74f9c24e25f41ef1ebfd81a4"}, - {file = "regex-2024.11.6-cp38-cp38-win_amd64.whl", hash = "sha256:bb8f74f2f10dbf13a0be8de623ba4f9491faf58c24064f32b65679b021ed0001"}, - {file = "regex-2024.11.6-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5704e174f8ccab2026bd2f1ab6c510345ae8eac818b613d7d73e785f1310f839"}, - {file = "regex-2024.11.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:220902c3c5cc6af55d4fe19ead504de80eb91f786dc102fbd74894b1551f095e"}, - {file = "regex-2024.11.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5e7e351589da0850c125f1600a4c4ba3c722efefe16b297de54300f08d734fbf"}, - {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5056b185ca113c88e18223183aa1a50e66507769c9640a6ff75859619d73957b"}, - {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2e34b51b650b23ed3354b5a07aab37034d9f923db2a40519139af34f485f77d0"}, - {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5670bce7b200273eee1840ef307bfa07cda90b38ae56e9a6ebcc9f50da9c469b"}, - {file = "regex-2024.11.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:08986dce1339bc932923e7d1232ce9881499a0e02925f7402fb7c982515419ef"}, - {file = "regex-2024.11.6-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:93c0b12d3d3bc25af4ebbf38f9ee780a487e8bf6954c115b9f015822d3bb8e48"}, - {file = "regex-2024.11.6-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:764e71f22ab3b305e7f4c21f1a97e1526a25ebdd22513e251cf376760213da13"}, - {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:f056bf21105c2515c32372bbc057f43eb02aae2fda61052e2f7622c801f0b4e2"}, - {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:69ab78f848845569401469da20df3e081e6b5a11cb086de3eed1d48f5ed57c95"}, - {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:86fddba590aad9208e2fa8b43b4c098bb0ec74f15718bb6a704e3c63e2cef3e9"}, - {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:684d7a212682996d21ca12ef3c17353c021fe9de6049e19ac8481ec35574a70f"}, - {file = "regex-2024.11.6-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a03e02f48cd1abbd9f3b7e3586d97c8f7a9721c436f51a5245b3b9483044480b"}, - {file = "regex-2024.11.6-cp39-cp39-win32.whl", hash = "sha256:41758407fc32d5c3c5de163888068cfee69cb4c2be844e7ac517a52770f9af57"}, - {file = "regex-2024.11.6-cp39-cp39-win_amd64.whl", hash = "sha256:b2837718570f95dd41675328e111345f9b7095d821bac435aac173ac80b19983"}, - {file = "regex-2024.11.6.tar.gz", hash = "sha256:7ab159b063c52a0333c884e4679f8d7a85112ee3078fe3d9004b2dd875585519"}, -] - -[[package]] -name = "requests" -version = "2.32.4" -description = "Python HTTP for Humans." -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "requests-2.32.4-py3-none-any.whl", hash = "sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c"}, - {file = "requests-2.32.4.tar.gz", hash = "sha256:27d0316682c8a29834d3264820024b62a36942083d52caf2f14c0591336d3422"}, + {file = "requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6"}, + {file = "requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf"}, ] [package.dependencies] @@ -4545,242 +3890,6 @@ urllib3 = ">=1.21.1,<3" socks = ["PySocks (>=1.5.6,!=1.5.7)"] use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] -[[package]] -name = "rigging" -version = "2.3.0" -description = "LLM Interaction Framework" -optional = false -python-versions = "<4.0,>=3.9" -groups = ["main"] -files = [ - {file = "rigging-2.3.0-py3-none-any.whl", hash = "sha256:e17a78acb4c36651fc30eb55c8166858402d2f51b359bcbf717833883a6dad8f"}, - {file = "rigging-2.3.0.tar.gz", hash = "sha256:2c021cbfeaf6c6cd80762ba6bba310ef77443bf792eaadadef54795a877a8883"}, -] - -[package.dependencies] -boto3 = ">=1.35.0,<2.0.0" -boto3-stubs = {version = ">=1.35.0,<2.0.0", extras = ["s3"]} -colorama = ">=0.4.6,<0.5.0" -elasticsearch = ">=8.13.2,<9.0.0" -eval-type-backport = ">=0.2.0,<0.3.0" -jsonpath-ng = ">=1.7.0,<2.0.0" -jsonref = ">=1.1.0,<2.0.0" -litellm = ">=1.60.0,<2.0.0" -logfire-api = ">=3.1.1,<4.0.0" -loguru = ">=0.7.2,<0.8.0" -pandas = ">=2.2.2,<3.0.0" -pydantic = ">=2.7.3,<3.0.0" -pydantic-xml = ">=2.11.0,<3.0.0" -ruamel-yaml = ">=0.18.10,<0.19.0" -xmltodict = ">=0.13.0,<0.14.0" - -[package.extras] -all = ["accelerate (>=0.30.1,<0.31.0)", "aiodocker (>=0.22.2,<0.23.0)", "asyncssh (>=2.14.2,<3.0.0)", "click (>=8.1.7,<9.0.0)", "httpx (>=0.27.0,<0.28.0)", "transformers (>=4.41.0,<5.0.0)", "vllm (>=0.5.0,<0.6.0)", "websockets (>=13.0,<14.0)"] -examples = ["aiodocker (>=0.22.2,<0.23.0)", "asyncssh (>=2.14.2,<3.0.0)", "click (>=8.1.7,<9.0.0)", "httpx (>=0.27.0,<0.28.0)", "websockets (>=13.0,<14.0)"] - -[[package]] -name = "rpds-py" -version = "0.25.1" -description = "Python bindings to Rust's persistent data structures (rpds)" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "rpds_py-0.25.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:f4ad628b5174d5315761b67f212774a32f5bad5e61396d38108bd801c0a8f5d9"}, - {file = "rpds_py-0.25.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8c742af695f7525e559c16f1562cf2323db0e3f0fbdcabdf6865b095256b2d40"}, - {file = "rpds_py-0.25.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:605ffe7769e24b1800b4d024d24034405d9404f0bc2f55b6db3362cd34145a6f"}, - {file = "rpds_py-0.25.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ccc6f3ddef93243538be76f8e47045b4aad7a66a212cd3a0f23e34469473d36b"}, - {file = "rpds_py-0.25.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f70316f760174ca04492b5ab01be631a8ae30cadab1d1081035136ba12738cfa"}, - {file = "rpds_py-0.25.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e1dafef8df605fdb46edcc0bf1573dea0d6d7b01ba87f85cd04dc855b2b4479e"}, - {file = "rpds_py-0.25.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0701942049095741a8aeb298a31b203e735d1c61f4423511d2b1a41dcd8a16da"}, - {file = "rpds_py-0.25.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e87798852ae0b37c88babb7f7bbbb3e3fecc562a1c340195b44c7e24d403e380"}, - {file = "rpds_py-0.25.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3bcce0edc1488906c2d4c75c94c70a0417e83920dd4c88fec1078c94843a6ce9"}, - {file = "rpds_py-0.25.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e2f6a2347d3440ae789505693a02836383426249d5293541cd712e07e7aecf54"}, - {file = "rpds_py-0.25.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:4fd52d3455a0aa997734f3835cbc4c9f32571345143960e7d7ebfe7b5fbfa3b2"}, - {file = "rpds_py-0.25.1-cp310-cp310-win32.whl", hash = "sha256:3f0b1798cae2bbbc9b9db44ee068c556d4737911ad53a4e5093d09d04b3bbc24"}, - {file = "rpds_py-0.25.1-cp310-cp310-win_amd64.whl", hash = "sha256:3ebd879ab996537fc510a2be58c59915b5dd63bccb06d1ef514fee787e05984a"}, - {file = "rpds_py-0.25.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:5f048bbf18b1f9120685c6d6bb70cc1a52c8cc11bdd04e643d28d3be0baf666d"}, - {file = "rpds_py-0.25.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4fbb0dbba559959fcb5d0735a0f87cdbca9e95dac87982e9b95c0f8f7ad10255"}, - {file = "rpds_py-0.25.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4ca54b9cf9d80b4016a67a0193ebe0bcf29f6b0a96f09db942087e294d3d4c2"}, - {file = "rpds_py-0.25.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1ee3e26eb83d39b886d2cb6e06ea701bba82ef30a0de044d34626ede51ec98b0"}, - {file = "rpds_py-0.25.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:89706d0683c73a26f76a5315d893c051324d771196ae8b13e6ffa1ffaf5e574f"}, - {file = "rpds_py-0.25.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c2013ee878c76269c7b557a9a9c042335d732e89d482606990b70a839635feb7"}, - {file = "rpds_py-0.25.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45e484db65e5380804afbec784522de84fa95e6bb92ef1bd3325d33d13efaebd"}, - {file = "rpds_py-0.25.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:48d64155d02127c249695abb87d39f0faf410733428d499867606be138161d65"}, - {file = "rpds_py-0.25.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:048893e902132fd6548a2e661fb38bf4896a89eea95ac5816cf443524a85556f"}, - {file = "rpds_py-0.25.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:0317177b1e8691ab5879f4f33f4b6dc55ad3b344399e23df2e499de7b10a548d"}, - {file = "rpds_py-0.25.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bffcf57826d77a4151962bf1701374e0fc87f536e56ec46f1abdd6a903354042"}, - {file = "rpds_py-0.25.1-cp311-cp311-win32.whl", hash = "sha256:cda776f1967cb304816173b30994faaf2fd5bcb37e73118a47964a02c348e1bc"}, - {file = "rpds_py-0.25.1-cp311-cp311-win_amd64.whl", hash = "sha256:dc3c1ff0abc91444cd20ec643d0f805df9a3661fcacf9c95000329f3ddf268a4"}, - {file = "rpds_py-0.25.1-cp311-cp311-win_arm64.whl", hash = "sha256:5a3ddb74b0985c4387719fc536faced33cadf2172769540c62e2a94b7b9be1c4"}, - {file = "rpds_py-0.25.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:b5ffe453cde61f73fea9430223c81d29e2fbf412a6073951102146c84e19e34c"}, - {file = "rpds_py-0.25.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:115874ae5e2fdcfc16b2aedc95b5eef4aebe91b28e7e21951eda8a5dc0d3461b"}, - {file = "rpds_py-0.25.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a714bf6e5e81b0e570d01f56e0c89c6375101b8463999ead3a93a5d2a4af91fa"}, - {file = "rpds_py-0.25.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:35634369325906bcd01577da4c19e3b9541a15e99f31e91a02d010816b49bfda"}, - {file = "rpds_py-0.25.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d4cb2b3ddc16710548801c6fcc0cfcdeeff9dafbc983f77265877793f2660309"}, - {file = "rpds_py-0.25.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9ceca1cf097ed77e1a51f1dbc8d174d10cb5931c188a4505ff9f3e119dfe519b"}, - {file = "rpds_py-0.25.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c2cd1a4b0c2b8c5e31ffff50d09f39906fe351389ba143c195566056c13a7ea"}, - {file = "rpds_py-0.25.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1de336a4b164c9188cb23f3703adb74a7623ab32d20090d0e9bf499a2203ad65"}, - {file = "rpds_py-0.25.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9fca84a15333e925dd59ce01da0ffe2ffe0d6e5d29a9eeba2148916d1824948c"}, - {file = "rpds_py-0.25.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:88ec04afe0c59fa64e2f6ea0dd9657e04fc83e38de90f6de201954b4d4eb59bd"}, - {file = "rpds_py-0.25.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a8bd2f19e312ce3e1d2c635618e8a8d8132892bb746a7cf74780a489f0f6cdcb"}, - {file = "rpds_py-0.25.1-cp312-cp312-win32.whl", hash = "sha256:e5e2f7280d8d0d3ef06f3ec1b4fd598d386cc6f0721e54f09109a8132182fbfe"}, - {file = "rpds_py-0.25.1-cp312-cp312-win_amd64.whl", hash = "sha256:db58483f71c5db67d643857404da360dce3573031586034b7d59f245144cc192"}, - {file = "rpds_py-0.25.1-cp312-cp312-win_arm64.whl", hash = "sha256:6d50841c425d16faf3206ddbba44c21aa3310a0cebc3c1cdfc3e3f4f9f6f5728"}, - {file = "rpds_py-0.25.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:659d87430a8c8c704d52d094f5ba6fa72ef13b4d385b7e542a08fc240cb4a559"}, - {file = "rpds_py-0.25.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:68f6f060f0bbdfb0245267da014d3a6da9be127fe3e8cc4a68c6f833f8a23bb1"}, - {file = "rpds_py-0.25.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:083a9513a33e0b92cf6e7a6366036c6bb43ea595332c1ab5c8ae329e4bcc0a9c"}, - {file = "rpds_py-0.25.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:816568614ecb22b18a010c7a12559c19f6fe993526af88e95a76d5a60b8b75fb"}, - {file = "rpds_py-0.25.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3c6564c0947a7f52e4792983f8e6cf9bac140438ebf81f527a21d944f2fd0a40"}, - {file = "rpds_py-0.25.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c4a128527fe415d73cf1f70a9a688d06130d5810be69f3b553bf7b45e8acf79"}, - {file = "rpds_py-0.25.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a49e1d7a4978ed554f095430b89ecc23f42014a50ac385eb0c4d163ce213c325"}, - {file = "rpds_py-0.25.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d74ec9bc0e2feb81d3f16946b005748119c0f52a153f6db6a29e8cd68636f295"}, - {file = "rpds_py-0.25.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3af5b4cc10fa41e5bc64e5c198a1b2d2864337f8fcbb9a67e747e34002ce812b"}, - {file = "rpds_py-0.25.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:79dc317a5f1c51fd9c6a0c4f48209c6b8526d0524a6904fc1076476e79b00f98"}, - {file = "rpds_py-0.25.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1521031351865e0181bc585147624d66b3b00a84109b57fcb7a779c3ec3772cd"}, - {file = "rpds_py-0.25.1-cp313-cp313-win32.whl", hash = "sha256:5d473be2b13600b93a5675d78f59e63b51b1ba2d0476893415dfbb5477e65b31"}, - {file = "rpds_py-0.25.1-cp313-cp313-win_amd64.whl", hash = "sha256:a7b74e92a3b212390bdce1d93da9f6488c3878c1d434c5e751cbc202c5e09500"}, - {file = "rpds_py-0.25.1-cp313-cp313-win_arm64.whl", hash = "sha256:dd326a81afe332ede08eb39ab75b301d5676802cdffd3a8f287a5f0b694dc3f5"}, - {file = "rpds_py-0.25.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:a58d1ed49a94d4183483a3ce0af22f20318d4a1434acee255d683ad90bf78129"}, - {file = "rpds_py-0.25.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f251bf23deb8332823aef1da169d5d89fa84c89f67bdfb566c49dea1fccfd50d"}, - {file = "rpds_py-0.25.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8dbd586bfa270c1103ece2109314dd423df1fa3d9719928b5d09e4840cec0d72"}, - {file = "rpds_py-0.25.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6d273f136e912aa101a9274c3145dcbddbe4bac560e77e6d5b3c9f6e0ed06d34"}, - {file = "rpds_py-0.25.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:666fa7b1bd0a3810a7f18f6d3a25ccd8866291fbbc3c9b912b917a6715874bb9"}, - {file = "rpds_py-0.25.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:921954d7fbf3fccc7de8f717799304b14b6d9a45bbeec5a8d7408ccbf531faf5"}, - {file = "rpds_py-0.25.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3d86373ff19ca0441ebeb696ef64cb58b8b5cbacffcda5a0ec2f3911732a194"}, - {file = "rpds_py-0.25.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c8980cde3bb8575e7c956a530f2c217c1d6aac453474bf3ea0f9c89868b531b6"}, - {file = "rpds_py-0.25.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:8eb8c84ecea987a2523e057c0d950bcb3f789696c0499290b8d7b3107a719d78"}, - {file = "rpds_py-0.25.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:e43a005671a9ed5a650f3bc39e4dbccd6d4326b24fb5ea8be5f3a43a6f576c72"}, - {file = "rpds_py-0.25.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:58f77c60956501a4a627749a6dcb78dac522f249dd96b5c9f1c6af29bfacfb66"}, - {file = "rpds_py-0.25.1-cp313-cp313t-win32.whl", hash = "sha256:2cb9e5b5e26fc02c8a4345048cd9998c2aca7c2712bd1b36da0c72ee969a3523"}, - {file = "rpds_py-0.25.1-cp313-cp313t-win_amd64.whl", hash = "sha256:401ca1c4a20cc0510d3435d89c069fe0a9ae2ee6495135ac46bdd49ec0495763"}, - {file = "rpds_py-0.25.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:ce4c8e485a3c59593f1a6f683cf0ea5ab1c1dc94d11eea5619e4fb5228b40fbd"}, - {file = "rpds_py-0.25.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d8222acdb51a22929c3b2ddb236b69c59c72af4019d2cba961e2f9add9b6e634"}, - {file = "rpds_py-0.25.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4593c4eae9b27d22df41cde518b4b9e4464d139e4322e2127daa9b5b981b76be"}, - {file = "rpds_py-0.25.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bd035756830c712b64725a76327ce80e82ed12ebab361d3a1cdc0f51ea21acb0"}, - {file = "rpds_py-0.25.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:114a07e85f32b125404f28f2ed0ba431685151c037a26032b213c882f26eb908"}, - {file = "rpds_py-0.25.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dec21e02e6cc932538b5203d3a8bd6aa1480c98c4914cb88eea064ecdbc6396a"}, - {file = "rpds_py-0.25.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:09eab132f41bf792c7a0ea1578e55df3f3e7f61888e340779b06050a9a3f16e9"}, - {file = "rpds_py-0.25.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c98f126c4fc697b84c423e387337d5b07e4a61e9feac494362a59fd7a2d9ed80"}, - {file = "rpds_py-0.25.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0e6a327af8ebf6baba1c10fadd04964c1965d375d318f4435d5f3f9651550f4a"}, - {file = "rpds_py-0.25.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:bc120d1132cff853ff617754196d0ac0ae63befe7c8498bd67731ba368abe451"}, - {file = "rpds_py-0.25.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:140f61d9bed7839446bdd44852e30195c8e520f81329b4201ceead4d64eb3a9f"}, - {file = "rpds_py-0.25.1-cp39-cp39-win32.whl", hash = "sha256:9c006f3aadeda131b438c3092124bd196b66312f0caa5823ef09585a669cf449"}, - {file = "rpds_py-0.25.1-cp39-cp39-win_amd64.whl", hash = "sha256:a61d0b2c7c9a0ae45732a77844917b427ff16ad5464b4d4f5e4adb955f582890"}, - {file = "rpds_py-0.25.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b24bf3cd93d5b6ecfbedec73b15f143596c88ee249fa98cefa9a9dc9d92c6f28"}, - {file = "rpds_py-0.25.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:0eb90e94f43e5085623932b68840b6f379f26db7b5c2e6bcef3179bd83c9330f"}, - {file = "rpds_py-0.25.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d50e4864498a9ab639d6d8854b25e80642bd362ff104312d9770b05d66e5fb13"}, - {file = "rpds_py-0.25.1-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7c9409b47ba0650544b0bb3c188243b83654dfe55dcc173a86832314e1a6a35d"}, - {file = "rpds_py-0.25.1-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:796ad874c89127c91970652a4ee8b00d56368b7e00d3477f4415fe78164c8000"}, - {file = "rpds_py-0.25.1-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:85608eb70a659bf4c1142b2781083d4b7c0c4e2c90eff11856a9754e965b2540"}, - {file = "rpds_py-0.25.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4feb9211d15d9160bc85fa72fed46432cdc143eb9cf6d5ca377335a921ac37b"}, - {file = "rpds_py-0.25.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ccfa689b9246c48947d31dd9d8b16d89a0ecc8e0e26ea5253068efb6c542b76e"}, - {file = "rpds_py-0.25.1-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:3c5b317ecbd8226887994852e85de562f7177add602514d4ac40f87de3ae45a8"}, - {file = "rpds_py-0.25.1-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:454601988aab2c6e8fd49e7634c65476b2b919647626208e376afcd22019eeb8"}, - {file = "rpds_py-0.25.1-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:1c0c434a53714358532d13539272db75a5ed9df75a4a090a753ac7173ec14e11"}, - {file = "rpds_py-0.25.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:f73ce1512e04fbe2bc97836e89830d6b4314c171587a99688082d090f934d20a"}, - {file = "rpds_py-0.25.1-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:ee86d81551ec68a5c25373c5643d343150cc54672b5e9a0cafc93c1870a53954"}, - {file = "rpds_py-0.25.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:89c24300cd4a8e4a51e55c31a8ff3918e6651b241ee8876a42cc2b2a078533ba"}, - {file = "rpds_py-0.25.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:771c16060ff4e79584dc48902a91ba79fd93eade3aa3a12d6d2a4aadaf7d542b"}, - {file = "rpds_py-0.25.1-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:785ffacd0ee61c3e60bdfde93baa6d7c10d86f15655bd706c89da08068dc5038"}, - {file = "rpds_py-0.25.1-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2a40046a529cc15cef88ac5ab589f83f739e2d332cb4d7399072242400ed68c9"}, - {file = "rpds_py-0.25.1-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:85fc223d9c76cabe5d0bff82214459189720dc135db45f9f66aa7cffbf9ff6c1"}, - {file = "rpds_py-0.25.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b0be9965f93c222fb9b4cc254235b3b2b215796c03ef5ee64f995b1b69af0762"}, - {file = "rpds_py-0.25.1-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8378fa4a940f3fb509c081e06cb7f7f2adae8cf46ef258b0e0ed7519facd573e"}, - {file = "rpds_py-0.25.1-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:33358883a4490287e67a2c391dfaea4d9359860281db3292b6886bf0be3d8692"}, - {file = "rpds_py-0.25.1-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:1d1fadd539298e70cac2f2cb36f5b8a65f742b9b9f1014dd4ea1f7785e2470bf"}, - {file = "rpds_py-0.25.1-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:9a46c2fb2545e21181445515960006e85d22025bd2fe6db23e76daec6eb689fe"}, - {file = "rpds_py-0.25.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:50f2c501a89c9a5f4e454b126193c5495b9fb441a75b298c60591d8a2eb92e1b"}, - {file = "rpds_py-0.25.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:7d779b325cc8238227c47fbc53964c8cc9a941d5dbae87aa007a1f08f2f77b23"}, - {file = "rpds_py-0.25.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:036ded36bedb727beeabc16dc1dad7cb154b3fa444e936a03b67a86dc6a5066e"}, - {file = "rpds_py-0.25.1-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:245550f5a1ac98504147cba96ffec8fabc22b610742e9150138e5d60774686d7"}, - {file = "rpds_py-0.25.1-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ff7c23ba0a88cb7b104281a99476cccadf29de2a0ef5ce864959a52675b1ca83"}, - {file = "rpds_py-0.25.1-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e37caa8cdb3b7cf24786451a0bdb853f6347b8b92005eeb64225ae1db54d1c2b"}, - {file = "rpds_py-0.25.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f2f48ab00181600ee266a095fe815134eb456163f7d6699f525dee471f312cf"}, - {file = "rpds_py-0.25.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9e5fc7484fa7dce57e25063b0ec9638ff02a908304f861d81ea49273e43838c1"}, - {file = "rpds_py-0.25.1-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:d3c10228d6cf6fe2b63d2e7985e94f6916fa46940df46b70449e9ff9297bd3d1"}, - {file = "rpds_py-0.25.1-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:5d9e40f32745db28c1ef7aad23f6fc458dc1e29945bd6781060f0d15628b8ddf"}, - {file = "rpds_py-0.25.1-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:35a8d1a24b5936b35c5003313bc177403d8bdef0f8b24f28b1c4a255f94ea992"}, - {file = "rpds_py-0.25.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:6099263f526efff9cf3883dfef505518730f7a7a93049b1d90d42e50a22b4793"}, - {file = "rpds_py-0.25.1.tar.gz", hash = "sha256:8960b6dac09b62dac26e75d7e2c4a22efb835d827a7278c34f72b2b84fa160e3"}, -] - -[[package]] -name = "ruamel-yaml" -version = "0.18.14" -description = "ruamel.yaml is a YAML parser/emitter that supports roundtrip preservation of comments, seq/map flow style, and map key order" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "ruamel.yaml-0.18.14-py3-none-any.whl", hash = "sha256:710ff198bb53da66718c7db27eec4fbcc9aa6ca7204e4c1df2f282b6fe5eb6b2"}, - {file = "ruamel.yaml-0.18.14.tar.gz", hash = "sha256:7227b76aaec364df15936730efbf7d72b30c0b79b1d578bbb8e3dcb2d81f52b7"}, -] - -[package.dependencies] -"ruamel.yaml.clib" = {version = ">=0.2.7", markers = "platform_python_implementation == \"CPython\" and python_version < \"3.14\""} - -[package.extras] -docs = ["mercurial (>5.7)", "ryd"] -jinja2 = ["ruamel.yaml.jinja2 (>=0.2)"] - -[[package]] -name = "ruamel-yaml-clib" -version = "0.2.12" -description = "C version of reader, parser and emitter for ruamel.yaml derived from libyaml" -optional = false -python-versions = ">=3.9" -groups = ["main"] -markers = "platform_python_implementation == \"CPython\" and python_version < \"3.14\"" -files = [ - {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-macosx_13_0_arm64.whl", hash = "sha256:11f891336688faf5156a36293a9c362bdc7c88f03a8a027c2c1d8e0bcde998e5"}, - {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:a606ef75a60ecf3d924613892cc603b154178ee25abb3055db5062da811fd969"}, - {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd5415dded15c3822597455bc02bcd66e81ef8b7a48cb71a33628fc9fdde39df"}, - {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f66efbc1caa63c088dead1c4170d148eabc9b80d95fb75b6c92ac0aad2437d76"}, - {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:22353049ba4181685023b25b5b51a574bce33e7f51c759371a7422dcae5402a6"}, - {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:932205970b9f9991b34f55136be327501903f7c66830e9760a8ffb15b07f05cd"}, - {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a52d48f4e7bf9005e8f0a89209bf9a73f7190ddf0489eee5eb51377385f59f2a"}, - {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-win32.whl", hash = "sha256:3eac5a91891ceb88138c113f9db04f3cebdae277f5d44eaa3651a4f573e6a5da"}, - {file = "ruamel.yaml.clib-0.2.12-cp310-cp310-win_amd64.whl", hash = "sha256:ab007f2f5a87bd08ab1499bdf96f3d5c6ad4dcfa364884cb4549aa0154b13a28"}, - {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:4a6679521a58256a90b0d89e03992c15144c5f3858f40d7c18886023d7943db6"}, - {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:d84318609196d6bd6da0edfa25cedfbabd8dbde5140a0a23af29ad4b8f91fb1e"}, - {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb43a269eb827806502c7c8efb7ae7e9e9d0573257a46e8e952f4d4caba4f31e"}, - {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:811ea1594b8a0fb466172c384267a4e5e367298af6b228931f273b111f17ef52"}, - {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:cf12567a7b565cbf65d438dec6cfbe2917d3c1bdddfce84a9930b7d35ea59642"}, - {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7dd5adc8b930b12c8fc5b99e2d535a09889941aa0d0bd06f4749e9a9397c71d2"}, - {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1492a6051dab8d912fc2adeef0e8c72216b24d57bd896ea607cb90bb0c4981d3"}, - {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-win32.whl", hash = "sha256:bd0a08f0bab19093c54e18a14a10b4322e1eacc5217056f3c063bd2f59853ce4"}, - {file = "ruamel.yaml.clib-0.2.12-cp311-cp311-win_amd64.whl", hash = "sha256:a274fb2cb086c7a3dea4322ec27f4cb5cc4b6298adb583ab0e211a4682f241eb"}, - {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:20b0f8dc160ba83b6dcc0e256846e1a02d044e13f7ea74a3d1d56ede4e48c632"}, - {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:943f32bc9dedb3abff9879edc134901df92cfce2c3d5c9348f172f62eb2d771d"}, - {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95c3829bb364fdb8e0332c9931ecf57d9be3519241323c5274bd82f709cebc0c"}, - {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:749c16fcc4a2b09f28843cda5a193e0283e47454b63ec4b81eaa2242f50e4ccd"}, - {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bf165fef1f223beae7333275156ab2022cffe255dcc51c27f066b4370da81e31"}, - {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:32621c177bbf782ca5a18ba4d7af0f1082a3f6e517ac2a18b3974d4edf349680"}, - {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b82a7c94a498853aa0b272fd5bc67f29008da798d4f93a2f9f289feb8426a58d"}, - {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-win32.whl", hash = "sha256:e8c4ebfcfd57177b572e2040777b8abc537cdef58a2120e830124946aa9b42c5"}, - {file = "ruamel.yaml.clib-0.2.12-cp312-cp312-win_amd64.whl", hash = "sha256:0467c5965282c62203273b838ae77c0d29d7638c8a4e3a1c8bdd3602c10904e4"}, - {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:4c8c5d82f50bb53986a5e02d1b3092b03622c02c2eb78e29bec33fd9593bae1a"}, - {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-manylinux2014_aarch64.whl", hash = "sha256:e7e3736715fbf53e9be2a79eb4db68e4ed857017344d697e8b9749444ae57475"}, - {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b7e75b4965e1d4690e93021adfcecccbca7d61c7bddd8e22406ef2ff20d74ef"}, - {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96777d473c05ee3e5e3c3e999f5d23c6f4ec5b0c38c098b3a5229085f74236c6"}, - {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:3bc2a80e6420ca8b7d3590791e2dfc709c88ab9152c00eeb511c9875ce5778bf"}, - {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:e188d2699864c11c36cdfdada94d781fd5d6b0071cd9c427bceb08ad3d7c70e1"}, - {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4f6f3eac23941b32afccc23081e1f50612bdbe4e982012ef4f5797986828cd01"}, - {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-win32.whl", hash = "sha256:6442cb36270b3afb1b4951f060eccca1ce49f3d087ca1ca4563a6eb479cb3de6"}, - {file = "ruamel.yaml.clib-0.2.12-cp313-cp313-win_amd64.whl", hash = "sha256:e5b8daf27af0b90da7bb903a876477a9e6d7270be6146906b276605997c7e9a3"}, - {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:fc4b630cd3fa2cf7fce38afa91d7cfe844a9f75d7f0f36393fa98815e911d987"}, - {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:bc5f1e1c28e966d61d2519f2a3d451ba989f9ea0f2307de7bc45baa526de9e45"}, - {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a0e060aace4c24dcaf71023bbd7d42674e3b230f7e7b97317baf1e953e5b519"}, - {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e2f1c3765db32be59d18ab3953f43ab62a761327aafc1594a2a1fbe038b8b8a7"}, - {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:d85252669dc32f98ebcd5d36768f5d4faeaeaa2d655ac0473be490ecdae3c285"}, - {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e143ada795c341b56de9418c58d028989093ee611aa27ffb9b7f609c00d813ed"}, - {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2c59aa6170b990d8d2719323e628aaf36f3bfbc1c26279c0eeeb24d05d2d11c7"}, - {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-win32.whl", hash = "sha256:beffaed67936fbbeffd10966a4eb53c402fafd3d6833770516bf7314bc6ffa12"}, - {file = "ruamel.yaml.clib-0.2.12-cp39-cp39-win_amd64.whl", hash = "sha256:040ae85536960525ea62868b642bdb0c2cc6021c9f9d507810c0c604e66f5a7b"}, - {file = "ruamel.yaml.clib-0.2.12.tar.gz", hash = "sha256:6c8fbb13ec503f99a91901ab46e0b07ae7941cd527393187039aec586fdfd36f"}, -] - [[package]] name = "ruff" version = "0.9.10" @@ -4809,24 +3918,6 @@ files = [ {file = "ruff-0.9.10.tar.gz", hash = "sha256:9bacb735d7bada9cfb0f2c227d3658fc443d90a727b47f206fb33f52f3c0eac7"}, ] -[[package]] -name = "s3transfer" -version = "0.13.0" -description = "An Amazon S3 Transfer Manager" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "s3transfer-0.13.0-py3-none-any.whl", hash = "sha256:0148ef34d6dd964d0d8cf4311b2b21c474693e57c2e069ec708ce043d2b527be"}, - {file = "s3transfer-0.13.0.tar.gz", hash = "sha256:f5e6db74eb7776a37208001113ea7aa97695368242b364d73e91c981ac522177"}, -] - -[package.dependencies] -botocore = ">=1.37.4,<2.0a.0" - -[package.extras] -crt = ["botocore[crt] (>=1.37.4,<2.0a.0)"] - [[package]] name = "setuptools" version = "80.9.0" @@ -4929,87 +4020,6 @@ files = [ {file = "texttable-1.7.0.tar.gz", hash = "sha256:2d2068fb55115807d3ac77a4ca68fa48803e84ebb0ee2340f858107a36522638"}, ] -[[package]] -name = "tiktoken" -version = "0.9.0" -description = "tiktoken is a fast BPE tokeniser for use with OpenAI's models" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "tiktoken-0.9.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:586c16358138b96ea804c034b8acf3f5d3f0258bd2bc3b0227af4af5d622e382"}, - {file = "tiktoken-0.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d9c59ccc528c6c5dd51820b3474402f69d9a9e1d656226848ad68a8d5b2e5108"}, - {file = "tiktoken-0.9.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f0968d5beeafbca2a72c595e8385a1a1f8af58feaebb02b227229b69ca5357fd"}, - {file = "tiktoken-0.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:92a5fb085a6a3b7350b8fc838baf493317ca0e17bd95e8642f95fc69ecfed1de"}, - {file = "tiktoken-0.9.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:15a2752dea63d93b0332fb0ddb05dd909371ededa145fe6a3242f46724fa7990"}, - {file = "tiktoken-0.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:26113fec3bd7a352e4b33dbaf1bd8948de2507e30bd95a44e2b1156647bc01b4"}, - {file = "tiktoken-0.9.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:f32cc56168eac4851109e9b5d327637f15fd662aa30dd79f964b7c39fbadd26e"}, - {file = "tiktoken-0.9.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:45556bc41241e5294063508caf901bf92ba52d8ef9222023f83d2483a3055348"}, - {file = "tiktoken-0.9.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:03935988a91d6d3216e2ec7c645afbb3d870b37bcb67ada1943ec48678e7ee33"}, - {file = "tiktoken-0.9.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b3d80aad8d2c6b9238fc1a5524542087c52b860b10cbf952429ffb714bc1136"}, - {file = "tiktoken-0.9.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b2a21133be05dc116b1d0372af051cd2c6aa1d2188250c9b553f9fa49301b336"}, - {file = "tiktoken-0.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:11a20e67fdf58b0e2dea7b8654a288e481bb4fc0289d3ad21291f8d0849915fb"}, - {file = "tiktoken-0.9.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e88f121c1c22b726649ce67c089b90ddda8b9662545a8aeb03cfef15967ddd03"}, - {file = "tiktoken-0.9.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a6600660f2f72369acb13a57fb3e212434ed38b045fd8cc6cdd74947b4b5d210"}, - {file = "tiktoken-0.9.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:95e811743b5dfa74f4b227927ed86cbc57cad4df859cb3b643be797914e41794"}, - {file = "tiktoken-0.9.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99376e1370d59bcf6935c933cb9ba64adc29033b7e73f5f7569f3aad86552b22"}, - {file = "tiktoken-0.9.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:badb947c32739fb6ddde173e14885fb3de4d32ab9d8c591cbd013c22b4c31dd2"}, - {file = "tiktoken-0.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:5a62d7a25225bafed786a524c1b9f0910a1128f4232615bf3f8257a73aaa3b16"}, - {file = "tiktoken-0.9.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2b0e8e05a26eda1249e824156d537015480af7ae222ccb798e5234ae0285dbdb"}, - {file = "tiktoken-0.9.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:27d457f096f87685195eea0165a1807fae87b97b2161fe8c9b1df5bd74ca6f63"}, - {file = "tiktoken-0.9.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cf8ded49cddf825390e36dd1ad35cd49589e8161fdcb52aa25f0583e90a3e01"}, - {file = "tiktoken-0.9.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc156cb314119a8bb9748257a2eaebd5cc0753b6cb491d26694ed42fc7cb3139"}, - {file = "tiktoken-0.9.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:cd69372e8c9dd761f0ab873112aba55a0e3e506332dd9f7522ca466e817b1b7a"}, - {file = "tiktoken-0.9.0-cp313-cp313-win_amd64.whl", hash = "sha256:5ea0edb6f83dc56d794723286215918c1cde03712cbbafa0348b33448faf5b95"}, - {file = "tiktoken-0.9.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:c6386ca815e7d96ef5b4ac61e0048cd32ca5a92d5781255e13b31381d28667dc"}, - {file = "tiktoken-0.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:75f6d5db5bc2c6274b674ceab1615c1778e6416b14705827d19b40e6355f03e0"}, - {file = "tiktoken-0.9.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e15b16f61e6f4625a57a36496d28dd182a8a60ec20a534c5343ba3cafa156ac7"}, - {file = "tiktoken-0.9.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ebcec91babf21297022882344c3f7d9eed855931466c3311b1ad6b64befb3df"}, - {file = "tiktoken-0.9.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:e5fd49e7799579240f03913447c0cdfa1129625ebd5ac440787afc4345990427"}, - {file = "tiktoken-0.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:26242ca9dc8b58e875ff4ca078b9a94d2f0813e6a535dcd2205df5d49d927cc7"}, - {file = "tiktoken-0.9.0.tar.gz", hash = "sha256:d02a5ca6a938e0490e1ff957bc48c8b078c88cb83977be1625b1fd8aac792c5d"}, -] - -[package.dependencies] -regex = ">=2022.1.18" -requests = ">=2.26.0" - -[package.extras] -blobfile = ["blobfile (>=2)"] - -[[package]] -name = "tokenizers" -version = "0.21.1" -description = "" -optional = false -python-versions = ">=3.9" -groups = ["main"] -files = [ - {file = "tokenizers-0.21.1-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:e78e413e9e668ad790a29456e677d9d3aa50a9ad311a40905d6861ba7692cf41"}, - {file = "tokenizers-0.21.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:cd51cd0a91ecc801633829fcd1fda9cf8682ed3477c6243b9a095539de4aecf3"}, - {file = "tokenizers-0.21.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28da6b72d4fb14ee200a1bd386ff74ade8992d7f725f2bde2c495a9a98cf4d9f"}, - {file = "tokenizers-0.21.1-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:34d8cfde551c9916cb92014e040806122295a6800914bab5865deb85623931cf"}, - {file = "tokenizers-0.21.1-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aaa852d23e125b73d283c98f007e06d4595732104b65402f46e8ef24b588d9f8"}, - {file = "tokenizers-0.21.1-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a21a15d5c8e603331b8a59548bbe113564136dc0f5ad8306dd5033459a226da0"}, - {file = "tokenizers-0.21.1-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2fdbd4c067c60a0ac7eca14b6bd18a5bebace54eb757c706b47ea93204f7a37c"}, - {file = "tokenizers-0.21.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2dd9a0061e403546f7377df940e866c3e678d7d4e9643d0461ea442b4f89e61a"}, - {file = "tokenizers-0.21.1-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:db9484aeb2e200c43b915a1a0150ea885e35f357a5a8fabf7373af333dcc8dbf"}, - {file = "tokenizers-0.21.1-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:ed248ab5279e601a30a4d67bdb897ecbe955a50f1e7bb62bd99f07dd11c2f5b6"}, - {file = "tokenizers-0.21.1-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:9ac78b12e541d4ce67b4dfd970e44c060a2147b9b2a21f509566d556a509c67d"}, - {file = "tokenizers-0.21.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:e5a69c1a4496b81a5ee5d2c1f3f7fbdf95e90a0196101b0ee89ed9956b8a168f"}, - {file = "tokenizers-0.21.1-cp39-abi3-win32.whl", hash = "sha256:1039a3a5734944e09de1d48761ade94e00d0fa760c0e0551151d4dd851ba63e3"}, - {file = "tokenizers-0.21.1-cp39-abi3-win_amd64.whl", hash = "sha256:0f0dcbcc9f6e13e675a66d7a5f2f225a736745ce484c1a4e07476a89ccdad382"}, - {file = "tokenizers-0.21.1.tar.gz", hash = "sha256:a1bb04dc5b448985f86ecd4b05407f5a8d97cb2c0532199b2a302a604a0165ab"}, -] - -[package.dependencies] -huggingface-hub = ">=0.16.4,<1.0" - -[package.extras] -dev = ["tokenizers[testing]"] -docs = ["setuptools-rust", "sphinx", "sphinx-rtd-theme"] -testing = ["black (==22.3)", "datasets", "numpy", "pytest", "requests", "ruff"] - [[package]] name = "tqdm" version = "4.67.1" @@ -5032,52 +4042,29 @@ notebook = ["ipywidgets (>=6)"] slack = ["slack-sdk"] telegram = ["requests"] -[[package]] -name = "types-awscrt" -version = "0.27.2" -description = "Type annotations and code completion for awscrt" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "types_awscrt-0.27.2-py3-none-any.whl", hash = "sha256:49a045f25bbd5ad2865f314512afced933aed35ddbafc252e2268efa8a787e4e"}, - {file = "types_awscrt-0.27.2.tar.gz", hash = "sha256:acd04f57119eb15626ab0ba9157fc24672421de56e7bd7b9f61681fedee44e91"}, -] - -[[package]] -name = "types-s3transfer" -version = "0.13.0" -description = "Type annotations and code completion for s3transfer" -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "types_s3transfer-0.13.0-py3-none-any.whl", hash = "sha256:79c8375cbf48a64bff7654c02df1ec4b20d74f8c5672fc13e382f593ca5565b3"}, - {file = "types_s3transfer-0.13.0.tar.gz", hash = "sha256:203dadcb9865c2f68fb44bc0440e1dc05b79197ba4a641c0976c26c9af75ef52"}, -] - [[package]] name = "typing-extensions" -version = "4.14.0" +version = "4.15.0" description = "Backported and Experimental Type Hints for Python 3.9+" optional = false python-versions = ">=3.9" -groups = ["main"] +groups = ["main", "dev"] files = [ - {file = "typing_extensions-4.14.0-py3-none-any.whl", hash = "sha256:a1514509136dd0b477638fc68d6a91497af5076466ad0fa6c338e44e359944af"}, - {file = "typing_extensions-4.14.0.tar.gz", hash = "sha256:8676b788e32f02ab42d9e7c61324048ae4c6d844a399eebace3d4979d75ceef4"}, + {file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"}, + {file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"}, ] +markers = {dev = "python_version < \"3.13\""} [[package]] name = "typing-inspection" -version = "0.4.1" +version = "0.4.2" description = "Runtime typing introspection tools" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "typing_inspection-0.4.1-py3-none-any.whl", hash = "sha256:389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51"}, - {file = "typing_inspection-0.4.1.tar.gz", hash = "sha256:6ae134cc0203c33377d43188d4064e9b357dba58cff3185f22924610e70a9d28"}, + {file = "typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7"}, + {file = "typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464"}, ] [package.dependencies] @@ -5090,20 +4077,33 @@ description = "Provider of IANA time zone data" optional = false python-versions = ">=2" groups = ["main"] +markers = "sys_platform == \"win32\"" files = [ {file = "tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8"}, {file = "tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9"}, ] +[[package]] +name = "unicodecsv" +version = "0.14.1" +description = "Python2's stdlib csv module is nice, but it doesn't support unicode. This module is a drop-in replacement which *does*." +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "unicodecsv-0.14.1.tar.gz", hash = "sha256:018c08037d48649a0412063ff4eda26eaa81eff1546dbffa51fa5293276ff7fc"}, +] + [[package]] name = "unicrypto" -version = "0.0.10" +version = "0.0.11" description = "Unified interface for cryptographic libraries" optional = false python-versions = ">=3.6" groups = ["main"] files = [ - {file = "unicrypto-0.0.10-py3-none-any.whl", hash = "sha256:77322c68cb6a7ef8ee762dcb0a824a491429f8939793e8a9d64f615baaf595b9"}, + {file = "unicrypto-0.0.11-py3-none-any.whl", hash = "sha256:6eca25e58797ba0965aba9d7a8cded15001dfaa424a622111d90a1f4f7afe733"}, + {file = "unicrypto-0.0.11.tar.gz", hash = "sha256:44ab77bbf0e9ea6a4e957bc03d015cf837b9533c5319129e44d950be273d40a5"}, ] [package.dependencies] @@ -5111,14 +4111,14 @@ pycryptodomex = "*" [[package]] name = "urllib3" -version = "2.4.0" +version = "2.5.0" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "urllib3-2.4.0-py3-none-any.whl", hash = "sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813"}, - {file = "urllib3-2.4.0.tar.gz", hash = "sha256:414bc6535b787febd7567804cc015fee39daab8ad86268f1310a9250697de466"}, + {file = "urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc"}, + {file = "urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760"}, ] [package.extras] @@ -5129,14 +4129,14 @@ zstd = ["zstandard (>=0.18.0)"] [[package]] name = "uvicorn" -version = "0.34.3" +version = "0.37.0" description = "The lightning-fast ASGI server." optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "uvicorn-0.34.3-py3-none-any.whl", hash = "sha256:16246631db62bdfbf069b0645177d6e8a77ba950cfedbfd093acef9444e4d885"}, - {file = "uvicorn-0.34.3.tar.gz", hash = "sha256:35919a9a979d7a59334b6b10e05d77c1d0d574c50e0fc98b8b1a0f165708b55a"}, + {file = "uvicorn-0.37.0-py3-none-any.whl", hash = "sha256:913b2b88672343739927ce381ff9e2ad62541f9f8289664fa1d1d3803fa2ce6c"}, + {file = "uvicorn-0.37.0.tar.gz", hash = "sha256:4115c8add6d3fd536c8ee77f0e14a7fd2ebba939fed9b02583a97f80648f9e13"}, ] [package.dependencies] @@ -5148,14 +4148,14 @@ standard = ["colorama (>=0.4)", "httptools (>=0.6.3)", "python-dotenv (>=0.13)", [[package]] name = "wcwidth" -version = "0.2.13" +version = "0.2.14" description = "Measures the displayed width of unicode strings in a terminal" optional = false -python-versions = "*" +python-versions = ">=3.6" groups = ["main"] files = [ - {file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"}, - {file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"}, + {file = "wcwidth-0.2.14-py2.py3-none-any.whl", hash = "sha256:a7bb560c8aee30f9957e5f9895805edd20602f2d7f720186dfd906e82b4982e1"}, + {file = "wcwidth-0.2.14.tar.gz", hash = "sha256:4d478375d31bc5395a3c55c40ccdf3354688364cd61c4f6adacaa9215d0b3605"}, ] [[package]] @@ -5188,22 +4188,6 @@ files = [ {file = "win_unicode_console-0.5.zip", hash = "sha256:d4142d4d56d46f449d6f00536a73625a871cba040f0bc1a2e305a04578f07d1e"}, ] -[[package]] -name = "win32-setctime" -version = "1.2.0" -description = "A small Python utility to set file creation time on Windows" -optional = false -python-versions = ">=3.5" -groups = ["main"] -markers = "sys_platform == \"win32\"" -files = [ - {file = "win32_setctime-1.2.0-py3-none-any.whl", hash = "sha256:95d644c4e708aba81dc3704a116d8cbc974d70b3bdb8be1d150e36be6e9d1390"}, - {file = "win32_setctime-1.2.0.tar.gz", hash = "sha256:ae1fdf948f5640aae05c511ade119313fb6a30d7eabe25fef9764dca5873c4c0"}, -] - -[package.extras] -dev = ["black (>=19.3b0)", "pytest (>=4.6.2)"] - [[package]] name = "winacl" version = "0.1.9" @@ -5219,239 +4203,164 @@ files = [ [package.dependencies] cryptography = ">=38.0.1" -[[package]] -name = "wrapt" -version = "1.17.2" -description = "Module for decorators, wrappers and monkey patching." -optional = false -python-versions = ">=3.8" -groups = ["main"] -files = [ - {file = "wrapt-1.17.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3d57c572081fed831ad2d26fd430d565b76aa277ed1d30ff4d40670b1c0dd984"}, - {file = "wrapt-1.17.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b5e251054542ae57ac7f3fba5d10bfff615b6c2fb09abeb37d2f1463f841ae22"}, - {file = "wrapt-1.17.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:80dd7db6a7cb57ffbc279c4394246414ec99537ae81ffd702443335a61dbf3a7"}, - {file = "wrapt-1.17.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a6e821770cf99cc586d33833b2ff32faebdbe886bd6322395606cf55153246c"}, - {file = "wrapt-1.17.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b60fb58b90c6d63779cb0c0c54eeb38941bae3ecf7a73c764c52c88c2dcb9d72"}, - {file = "wrapt-1.17.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b870b5df5b71d8c3359d21be8f0d6c485fa0ebdb6477dda51a1ea54a9b558061"}, - {file = "wrapt-1.17.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4011d137b9955791f9084749cba9a367c68d50ab8d11d64c50ba1688c9b457f2"}, - {file = "wrapt-1.17.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:1473400e5b2733e58b396a04eb7f35f541e1fb976d0c0724d0223dd607e0f74c"}, - {file = "wrapt-1.17.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3cedbfa9c940fdad3e6e941db7138e26ce8aad38ab5fe9dcfadfed9db7a54e62"}, - {file = "wrapt-1.17.2-cp310-cp310-win32.whl", hash = "sha256:582530701bff1dec6779efa00c516496968edd851fba224fbd86e46cc6b73563"}, - {file = "wrapt-1.17.2-cp310-cp310-win_amd64.whl", hash = "sha256:58705da316756681ad3c9c73fd15499aa4d8c69f9fd38dc8a35e06c12468582f"}, - {file = "wrapt-1.17.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ff04ef6eec3eee8a5efef2401495967a916feaa353643defcc03fc74fe213b58"}, - {file = "wrapt-1.17.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4db983e7bca53819efdbd64590ee96c9213894272c776966ca6306b73e4affda"}, - {file = "wrapt-1.17.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9abc77a4ce4c6f2a3168ff34b1da9b0f311a8f1cfd694ec96b0603dff1c79438"}, - {file = "wrapt-1.17.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b929ac182f5ace000d459c59c2c9c33047e20e935f8e39371fa6e3b85d56f4a"}, - {file = "wrapt-1.17.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f09b286faeff3c750a879d336fb6d8713206fc97af3adc14def0cdd349df6000"}, - {file = "wrapt-1.17.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a7ed2d9d039bd41e889f6fb9364554052ca21ce823580f6a07c4ec245c1f5d6"}, - {file = "wrapt-1.17.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:129a150f5c445165ff941fc02ee27df65940fcb8a22a61828b1853c98763a64b"}, - {file = "wrapt-1.17.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1fb5699e4464afe5c7e65fa51d4f99e0b2eadcc176e4aa33600a3df7801d6662"}, - {file = "wrapt-1.17.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9a2bce789a5ea90e51a02dfcc39e31b7f1e662bc3317979aa7e5538e3a034f72"}, - {file = "wrapt-1.17.2-cp311-cp311-win32.whl", hash = "sha256:4afd5814270fdf6380616b321fd31435a462019d834f83c8611a0ce7484c7317"}, - {file = "wrapt-1.17.2-cp311-cp311-win_amd64.whl", hash = "sha256:acc130bc0375999da18e3d19e5a86403667ac0c4042a094fefb7eec8ebac7cf3"}, - {file = "wrapt-1.17.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d5e2439eecc762cd85e7bd37161d4714aa03a33c5ba884e26c81559817ca0925"}, - {file = "wrapt-1.17.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3fc7cb4c1c744f8c05cd5f9438a3caa6ab94ce8344e952d7c45a8ed59dd88392"}, - {file = "wrapt-1.17.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8fdbdb757d5390f7c675e558fd3186d590973244fab0c5fe63d373ade3e99d40"}, - {file = "wrapt-1.17.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5bb1d0dbf99411f3d871deb6faa9aabb9d4e744d67dcaaa05399af89d847a91d"}, - {file = "wrapt-1.17.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d18a4865f46b8579d44e4fe1e2bcbc6472ad83d98e22a26c963d46e4c125ef0b"}, - {file = "wrapt-1.17.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc570b5f14a79734437cb7b0500376b6b791153314986074486e0b0fa8d71d98"}, - {file = "wrapt-1.17.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6d9187b01bebc3875bac9b087948a2bccefe464a7d8f627cf6e48b1bbae30f82"}, - {file = "wrapt-1.17.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:9e8659775f1adf02eb1e6f109751268e493c73716ca5761f8acb695e52a756ae"}, - {file = "wrapt-1.17.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e8b2816ebef96d83657b56306152a93909a83f23994f4b30ad4573b00bd11bb9"}, - {file = "wrapt-1.17.2-cp312-cp312-win32.whl", hash = "sha256:468090021f391fe0056ad3e807e3d9034e0fd01adcd3bdfba977b6fdf4213ea9"}, - {file = "wrapt-1.17.2-cp312-cp312-win_amd64.whl", hash = "sha256:ec89ed91f2fa8e3f52ae53cd3cf640d6feff92ba90d62236a81e4e563ac0e991"}, - {file = "wrapt-1.17.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6ed6ffac43aecfe6d86ec5b74b06a5be33d5bb9243d055141e8cabb12aa08125"}, - {file = "wrapt-1.17.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:35621ae4c00e056adb0009f8e86e28eb4a41a4bfa8f9bfa9fca7d343fe94f998"}, - {file = "wrapt-1.17.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a604bf7a053f8362d27eb9fefd2097f82600b856d5abe996d623babd067b1ab5"}, - {file = "wrapt-1.17.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5cbabee4f083b6b4cd282f5b817a867cf0b1028c54d445b7ec7cfe6505057cf8"}, - {file = "wrapt-1.17.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49703ce2ddc220df165bd2962f8e03b84c89fee2d65e1c24a7defff6f988f4d6"}, - {file = "wrapt-1.17.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8112e52c5822fc4253f3901b676c55ddf288614dc7011634e2719718eaa187dc"}, - {file = "wrapt-1.17.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9fee687dce376205d9a494e9c121e27183b2a3df18037f89d69bd7b35bcf59e2"}, - {file = "wrapt-1.17.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:18983c537e04d11cf027fbb60a1e8dfd5190e2b60cc27bc0808e653e7b218d1b"}, - {file = "wrapt-1.17.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:703919b1633412ab54bcf920ab388735832fdcb9f9a00ae49387f0fe67dad504"}, - {file = "wrapt-1.17.2-cp313-cp313-win32.whl", hash = "sha256:abbb9e76177c35d4e8568e58650aa6926040d6a9f6f03435b7a522bf1c487f9a"}, - {file = "wrapt-1.17.2-cp313-cp313-win_amd64.whl", hash = "sha256:69606d7bb691b50a4240ce6b22ebb319c1cfb164e5f6569835058196e0f3a845"}, - {file = "wrapt-1.17.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:4a721d3c943dae44f8e243b380cb645a709ba5bd35d3ad27bc2ed947e9c68192"}, - {file = "wrapt-1.17.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:766d8bbefcb9e00c3ac3b000d9acc51f1b399513f44d77dfe0eb026ad7c9a19b"}, - {file = "wrapt-1.17.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e496a8ce2c256da1eb98bd15803a79bee00fc351f5dfb9ea82594a3f058309e0"}, - {file = "wrapt-1.17.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40d615e4fe22f4ad3528448c193b218e077656ca9ccb22ce2cb20db730f8d306"}, - {file = "wrapt-1.17.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a5aaeff38654462bc4b09023918b7f21790efb807f54c000a39d41d69cf552cb"}, - {file = "wrapt-1.17.2-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a7d15bbd2bc99e92e39f49a04653062ee6085c0e18b3b7512a4f2fe91f2d681"}, - {file = "wrapt-1.17.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:e3890b508a23299083e065f435a492b5435eba6e304a7114d2f919d400888cc6"}, - {file = "wrapt-1.17.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:8c8b293cd65ad716d13d8dd3624e42e5a19cc2a2f1acc74b30c2c13f15cb61a6"}, - {file = "wrapt-1.17.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c82b8785d98cdd9fed4cac84d765d234ed3251bd6afe34cb7ac523cb93e8b4f"}, - {file = "wrapt-1.17.2-cp313-cp313t-win32.whl", hash = "sha256:13e6afb7fe71fe7485a4550a8844cc9ffbe263c0f1a1eea569bc7091d4898555"}, - {file = "wrapt-1.17.2-cp313-cp313t-win_amd64.whl", hash = "sha256:eaf675418ed6b3b31c7a989fd007fa7c3be66ce14e5c3b27336383604c9da85c"}, - {file = "wrapt-1.17.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5c803c401ea1c1c18de70a06a6f79fcc9c5acfc79133e9869e730ad7f8ad8ef9"}, - {file = "wrapt-1.17.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f917c1180fdb8623c2b75a99192f4025e412597c50b2ac870f156de8fb101119"}, - {file = "wrapt-1.17.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ecc840861360ba9d176d413a5489b9a0aff6d6303d7e733e2c4623cfa26904a6"}, - {file = "wrapt-1.17.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb87745b2e6dc56361bfde481d5a378dc314b252a98d7dd19a651a3fa58f24a9"}, - {file = "wrapt-1.17.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:58455b79ec2661c3600e65c0a716955adc2410f7383755d537584b0de41b1d8a"}, - {file = "wrapt-1.17.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b4e42a40a5e164cbfdb7b386c966a588b1047558a990981ace551ed7e12ca9c2"}, - {file = "wrapt-1.17.2-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:91bd7d1773e64019f9288b7a5101f3ae50d3d8e6b1de7edee9c2ccc1d32f0c0a"}, - {file = "wrapt-1.17.2-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:bb90fb8bda722a1b9d48ac1e6c38f923ea757b3baf8ebd0c82e09c5c1a0e7a04"}, - {file = "wrapt-1.17.2-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:08e7ce672e35efa54c5024936e559469436f8b8096253404faeb54d2a878416f"}, - {file = "wrapt-1.17.2-cp38-cp38-win32.whl", hash = "sha256:410a92fefd2e0e10d26210e1dfb4a876ddaf8439ef60d6434f21ef8d87efc5b7"}, - {file = "wrapt-1.17.2-cp38-cp38-win_amd64.whl", hash = "sha256:95c658736ec15602da0ed73f312d410117723914a5c91a14ee4cdd72f1d790b3"}, - {file = "wrapt-1.17.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:99039fa9e6306880572915728d7f6c24a86ec57b0a83f6b2491e1d8ab0235b9a"}, - {file = "wrapt-1.17.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2696993ee1eebd20b8e4ee4356483c4cb696066ddc24bd70bcbb80fa56ff9061"}, - {file = "wrapt-1.17.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:612dff5db80beef9e649c6d803a8d50c409082f1fedc9dbcdfde2983b2025b82"}, - {file = "wrapt-1.17.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62c2caa1585c82b3f7a7ab56afef7b3602021d6da34fbc1cf234ff139fed3cd9"}, - {file = "wrapt-1.17.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c958bcfd59bacc2d0249dcfe575e71da54f9dcf4a8bdf89c4cb9a68a1170d73f"}, - {file = "wrapt-1.17.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc78a84e2dfbc27afe4b2bd7c80c8db9bca75cc5b85df52bfe634596a1da846b"}, - {file = "wrapt-1.17.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:ba0f0eb61ef00ea10e00eb53a9129501f52385c44853dbd6c4ad3f403603083f"}, - {file = "wrapt-1.17.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:1e1fe0e6ab7775fd842bc39e86f6dcfc4507ab0ffe206093e76d61cde37225c8"}, - {file = "wrapt-1.17.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c86563182421896d73858e08e1db93afdd2b947a70064b813d515d66549e15f9"}, - {file = "wrapt-1.17.2-cp39-cp39-win32.whl", hash = "sha256:f393cda562f79828f38a819f4788641ac7c4085f30f1ce1a68672baa686482bb"}, - {file = "wrapt-1.17.2-cp39-cp39-win_amd64.whl", hash = "sha256:36ccae62f64235cf8ddb682073a60519426fdd4725524ae38874adf72b5f2aeb"}, - {file = "wrapt-1.17.2-py3-none-any.whl", hash = "sha256:b18f2d1533a71f069c7f82d524a52599053d4c7166e9dd374ae2136b7f40f7c8"}, - {file = "wrapt-1.17.2.tar.gz", hash = "sha256:41388e9d4d1522446fe79d3213196bd9e3b301a336965b9e27ca2788ebd122f3"}, -] - -[[package]] -name = "xmltodict" -version = "0.13.0" -description = "Makes working with XML feel like you are working with JSON" -optional = false -python-versions = ">=3.4" -groups = ["main"] -files = [ - {file = "xmltodict-0.13.0-py2.py3-none-any.whl", hash = "sha256:aa89e8fd76320154a40d19a0df04a4695fb9dc5ba977cbb68ab3e4eb225e7852"}, - {file = "xmltodict-0.13.0.tar.gz", hash = "sha256:341595a488e3e01a85a9d8911d8912fd922ede5fecc4dce437eb4b6c8d037e56"}, -] - [[package]] name = "yara-x" -version = "1.1.0" +version = "1.8.1" description = "Python bindings for YARA-X" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "yara_x-1.1.0-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:d7befedea56d70d9a2426b9bed8dfa464ca0d78bfffafa89063163724448420b"}, - {file = "yara_x-1.1.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:7470c4568378b3d16cb5c06c1971b8dbf945b862f3490ba51952267b39ac1b4b"}, - {file = "yara_x-1.1.0-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:df3ba8ab69440897e4f3d2c9d428ca1a7b32a5ac145237ec5c0a172b3392d617"}, - {file = "yara_x-1.1.0-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:4fc11c2c26cc825e1c44a17d206fbd27bf778e114cc9a7e9dc3353d338dbf927"}, - {file = "yara_x-1.1.0-cp38-abi3-win_amd64.whl", hash = "sha256:27ba2c6a6b9527921affeb6c8c5c4640c3e9b05793f7e5772b31dbc7141857ca"}, - {file = "yara_x-1.1.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:8dbc9615b85d4825e6e9d7a2af7c5ccf926bd3cd6809aaab2526cb0e2a1e6469"}, - {file = "yara_x-1.1.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:491db9c26733d09aa4230c3836e5431022d6e270a278a23fdf7b62d37fbe9ca6"}, - {file = "yara_x-1.1.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:8ab08d813feeb6e285225e601757fb60015e62529d4301e5c14a85e74dd9a1c3"}, - {file = "yara_x-1.1.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:6ddc741fa190c9412976dd14e0ed39d0f5d5a4f2ebb186bc72b11c07a4830237"}, - {file = "yara_x-1.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:bb332f5a26554d2777628b4b30f873abaae2f1e503b66541f4ae2e89c5926a74"}, + {file = "yara_x-1.8.1-cp38-abi3-macosx_14_0_arm64.whl", hash = "sha256:dbb1fd289f24a05c113b8f0713d4750331cb9c39722d212fea108b8aa69c8594"}, + {file = "yara_x-1.8.1-cp38-abi3-macosx_14_0_x86_64.whl", hash = "sha256:b41f4c4b9326905d584b38cf84d52037eb6864e70154844db4f59672107a5a1a"}, + {file = "yara_x-1.8.1-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:bcee1686b937fd8d75df3faae354e45c64256561b384bdf97ff79be32770c7a8"}, + {file = "yara_x-1.8.1-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:c6936ac7a316ce86e78e570bca73724319c2daa33db147974adbe3c8e502f27a"}, + {file = "yara_x-1.8.1-cp38-abi3-win_amd64.whl", hash = "sha256:25c3e4554ba3428968f1749004efaaf47a111f2ac7db51d08915f66140c7d53d"}, + {file = "yara_x-1.8.1-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:3565c776476c71a15cd997463b52578c38b4c20aae538157baf070b83339d7b4"}, + {file = "yara_x-1.8.1-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:b0fb608e107e92fb240cac8229160637c6219fe06811e113aca0cc7a85190e80"}, + {file = "yara_x-1.8.1-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:4d3ceaf7571b8d6ce6a2e644e3083c4f5656e4726885d98d12b75bbbb23262f7"}, + {file = "yara_x-1.8.1-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:7f2e42b980e0b4388e633d11c3ac4a7d70d10e3099e008ba3dadf8c6f9a33fb9"}, + {file = "yara_x-1.8.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:6a31f5f7e17b0231e9b0ef00ca0c91fb6c2a7b21c1973a42c0a459288ed7fd71"}, ] [[package]] name = "yarl" -version = "1.20.1" +version = "1.22.0" description = "Yet another URL library" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "yarl-1.20.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6032e6da6abd41e4acda34d75a816012717000fa6839f37124a47fcefc49bec4"}, - {file = "yarl-1.20.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2c7b34d804b8cf9b214f05015c4fee2ebe7ed05cf581e7192c06555c71f4446a"}, - {file = "yarl-1.20.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0c869f2651cc77465f6cd01d938d91a11d9ea5d798738c1dc077f3de0b5e5fed"}, - {file = "yarl-1.20.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62915e6688eb4d180d93840cda4110995ad50c459bf931b8b3775b37c264af1e"}, - {file = "yarl-1.20.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:41ebd28167bc6af8abb97fec1a399f412eec5fd61a3ccbe2305a18b84fb4ca73"}, - {file = "yarl-1.20.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:21242b4288a6d56f04ea193adde174b7e347ac46ce6bc84989ff7c1b1ecea84e"}, - {file = "yarl-1.20.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bea21cdae6c7eb02ba02a475f37463abfe0a01f5d7200121b03e605d6a0439f8"}, - {file = "yarl-1.20.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f8a891e4a22a89f5dde7862994485e19db246b70bb288d3ce73a34422e55b23"}, - {file = "yarl-1.20.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dd803820d44c8853a109a34e3660e5a61beae12970da479cf44aa2954019bf70"}, - {file = "yarl-1.20.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b982fa7f74c80d5c0c7b5b38f908971e513380a10fecea528091405f519b9ebb"}, - {file = "yarl-1.20.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:33f29ecfe0330c570d997bcf1afd304377f2e48f61447f37e846a6058a4d33b2"}, - {file = "yarl-1.20.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:835ab2cfc74d5eb4a6a528c57f05688099da41cf4957cf08cad38647e4a83b30"}, - {file = "yarl-1.20.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:46b5e0ccf1943a9a6e766b2c2b8c732c55b34e28be57d8daa2b3c1d1d4009309"}, - {file = "yarl-1.20.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:df47c55f7d74127d1b11251fe6397d84afdde0d53b90bedb46a23c0e534f9d24"}, - {file = "yarl-1.20.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:76d12524d05841276b0e22573f28d5fbcb67589836772ae9244d90dd7d66aa13"}, - {file = "yarl-1.20.1-cp310-cp310-win32.whl", hash = "sha256:6c4fbf6b02d70e512d7ade4b1f998f237137f1417ab07ec06358ea04f69134f8"}, - {file = "yarl-1.20.1-cp310-cp310-win_amd64.whl", hash = "sha256:aef6c4d69554d44b7f9d923245f8ad9a707d971e6209d51279196d8e8fe1ae16"}, - {file = "yarl-1.20.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:47ee6188fea634bdfaeb2cc420f5b3b17332e6225ce88149a17c413c77ff269e"}, - {file = "yarl-1.20.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d0f6500f69e8402d513e5eedb77a4e1818691e8f45e6b687147963514d84b44b"}, - {file = "yarl-1.20.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7a8900a42fcdaad568de58887c7b2f602962356908eedb7628eaf6021a6e435b"}, - {file = "yarl-1.20.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bad6d131fda8ef508b36be3ece16d0902e80b88ea7200f030a0f6c11d9e508d4"}, - {file = "yarl-1.20.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:df018d92fe22aaebb679a7f89fe0c0f368ec497e3dda6cb81a567610f04501f1"}, - {file = "yarl-1.20.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8f969afbb0a9b63c18d0feecf0db09d164b7a44a053e78a7d05f5df163e43833"}, - {file = "yarl-1.20.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:812303eb4aa98e302886ccda58d6b099e3576b1b9276161469c25803a8db277d"}, - {file = "yarl-1.20.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98c4a7d166635147924aa0bf9bfe8d8abad6fffa6102de9c99ea04a1376f91e8"}, - {file = "yarl-1.20.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:12e768f966538e81e6e7550f9086a6236b16e26cd964cf4df35349970f3551cf"}, - {file = "yarl-1.20.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fe41919b9d899661c5c28a8b4b0acf704510b88f27f0934ac7a7bebdd8938d5e"}, - {file = "yarl-1.20.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:8601bc010d1d7780592f3fc1bdc6c72e2b6466ea34569778422943e1a1f3c389"}, - {file = "yarl-1.20.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:daadbdc1f2a9033a2399c42646fbd46da7992e868a5fe9513860122d7fe7a73f"}, - {file = "yarl-1.20.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:03aa1e041727cb438ca762628109ef1333498b122e4c76dd858d186a37cec845"}, - {file = "yarl-1.20.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:642980ef5e0fa1de5fa96d905c7e00cb2c47cb468bfcac5a18c58e27dbf8d8d1"}, - {file = "yarl-1.20.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:86971e2795584fe8c002356d3b97ef6c61862720eeff03db2a7c86b678d85b3e"}, - {file = "yarl-1.20.1-cp311-cp311-win32.whl", hash = "sha256:597f40615b8d25812f14562699e287f0dcc035d25eb74da72cae043bb884d773"}, - {file = "yarl-1.20.1-cp311-cp311-win_amd64.whl", hash = "sha256:26ef53a9e726e61e9cd1cda6b478f17e350fb5800b4bd1cd9fe81c4d91cfeb2e"}, - {file = "yarl-1.20.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:bdcc4cd244e58593a4379fe60fdee5ac0331f8eb70320a24d591a3be197b94a9"}, - {file = "yarl-1.20.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b29a2c385a5f5b9c7d9347e5812b6f7ab267193c62d282a540b4fc528c8a9d2a"}, - {file = "yarl-1.20.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1112ae8154186dfe2de4732197f59c05a83dc814849a5ced892b708033f40dc2"}, - {file = "yarl-1.20.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:90bbd29c4fe234233f7fa2b9b121fb63c321830e5d05b45153a2ca68f7d310ee"}, - {file = "yarl-1.20.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:680e19c7ce3710ac4cd964e90dad99bf9b5029372ba0c7cbfcd55e54d90ea819"}, - {file = "yarl-1.20.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4a979218c1fdb4246a05efc2cc23859d47c89af463a90b99b7c56094daf25a16"}, - {file = "yarl-1.20.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:255b468adf57b4a7b65d8aad5b5138dce6a0752c139965711bdcb81bc370e1b6"}, - {file = "yarl-1.20.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a97d67108e79cfe22e2b430d80d7571ae57d19f17cda8bb967057ca8a7bf5bfd"}, - {file = "yarl-1.20.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8570d998db4ddbfb9a590b185a0a33dbf8aafb831d07a5257b4ec9948df9cb0a"}, - {file = "yarl-1.20.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:97c75596019baae7c71ccf1d8cc4738bc08134060d0adfcbe5642f778d1dca38"}, - {file = "yarl-1.20.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:1c48912653e63aef91ff988c5432832692ac5a1d8f0fb8a33091520b5bbe19ef"}, - {file = "yarl-1.20.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4c3ae28f3ae1563c50f3d37f064ddb1511ecc1d5584e88c6b7c63cf7702a6d5f"}, - {file = "yarl-1.20.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c5e9642f27036283550f5f57dc6156c51084b458570b9d0d96100c8bebb186a8"}, - {file = "yarl-1.20.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:2c26b0c49220d5799f7b22c6838409ee9bc58ee5c95361a4d7831f03cc225b5a"}, - {file = "yarl-1.20.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:564ab3d517e3d01c408c67f2e5247aad4019dcf1969982aba3974b4093279004"}, - {file = "yarl-1.20.1-cp312-cp312-win32.whl", hash = "sha256:daea0d313868da1cf2fac6b2d3a25c6e3a9e879483244be38c8e6a41f1d876a5"}, - {file = "yarl-1.20.1-cp312-cp312-win_amd64.whl", hash = "sha256:48ea7d7f9be0487339828a4de0360d7ce0efc06524a48e1810f945c45b813698"}, - {file = "yarl-1.20.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:0b5ff0fbb7c9f1b1b5ab53330acbfc5247893069e7716840c8e7d5bb7355038a"}, - {file = "yarl-1.20.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:14f326acd845c2b2e2eb38fb1346c94f7f3b01a4f5c788f8144f9b630bfff9a3"}, - {file = "yarl-1.20.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f60e4ad5db23f0b96e49c018596707c3ae89f5d0bd97f0ad3684bcbad899f1e7"}, - {file = "yarl-1.20.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:49bdd1b8e00ce57e68ba51916e4bb04461746e794e7c4d4bbc42ba2f18297691"}, - {file = "yarl-1.20.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:66252d780b45189975abfed839616e8fd2dbacbdc262105ad7742c6ae58f3e31"}, - {file = "yarl-1.20.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59174e7332f5d153d8f7452a102b103e2e74035ad085f404df2e40e663a22b28"}, - {file = "yarl-1.20.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e3968ec7d92a0c0f9ac34d5ecfd03869ec0cab0697c91a45db3fbbd95fe1b653"}, - {file = "yarl-1.20.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1a4fbb50e14396ba3d375f68bfe02215d8e7bc3ec49da8341fe3157f59d2ff5"}, - {file = "yarl-1.20.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:11a62c839c3a8eac2410e951301309426f368388ff2f33799052787035793b02"}, - {file = "yarl-1.20.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:041eaa14f73ff5a8986b4388ac6bb43a77f2ea09bf1913df7a35d4646db69e53"}, - {file = "yarl-1.20.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:377fae2fef158e8fd9d60b4c8751387b8d1fb121d3d0b8e9b0be07d1b41e83dc"}, - {file = "yarl-1.20.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:1c92f4390e407513f619d49319023664643d3339bd5e5a56a3bebe01bc67ec04"}, - {file = "yarl-1.20.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d25ddcf954df1754ab0f86bb696af765c5bfaba39b74095f27eececa049ef9a4"}, - {file = "yarl-1.20.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:909313577e9619dcff8c31a0ea2aa0a2a828341d92673015456b3ae492e7317b"}, - {file = "yarl-1.20.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:793fd0580cb9664548c6b83c63b43c477212c0260891ddf86809e1c06c8b08f1"}, - {file = "yarl-1.20.1-cp313-cp313-win32.whl", hash = "sha256:468f6e40285de5a5b3c44981ca3a319a4b208ccc07d526b20b12aeedcfa654b7"}, - {file = "yarl-1.20.1-cp313-cp313-win_amd64.whl", hash = "sha256:495b4ef2fea40596bfc0affe3837411d6aa3371abcf31aac0ccc4bdd64d4ef5c"}, - {file = "yarl-1.20.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:f60233b98423aab21d249a30eb27c389c14929f47be8430efa7dbd91493a729d"}, - {file = "yarl-1.20.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:6f3eff4cc3f03d650d8755c6eefc844edde99d641d0dcf4da3ab27141a5f8ddf"}, - {file = "yarl-1.20.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:69ff8439d8ba832d6bed88af2c2b3445977eba9a4588b787b32945871c2444e3"}, - {file = "yarl-1.20.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cf34efa60eb81dd2645a2e13e00bb98b76c35ab5061a3989c7a70f78c85006d"}, - {file = "yarl-1.20.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8e0fe9364ad0fddab2688ce72cb7a8e61ea42eff3c7caeeb83874a5d479c896c"}, - {file = "yarl-1.20.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8f64fbf81878ba914562c672024089e3401974a39767747691c65080a67b18c1"}, - {file = "yarl-1.20.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f6342d643bf9a1de97e512e45e4b9560a043347e779a173250824f8b254bd5ce"}, - {file = "yarl-1.20.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56dac5f452ed25eef0f6e3c6a066c6ab68971d96a9fb441791cad0efba6140d3"}, - {file = "yarl-1.20.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7d7f497126d65e2cad8dc5f97d34c27b19199b6414a40cb36b52f41b79014be"}, - {file = "yarl-1.20.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:67e708dfb8e78d8a19169818eeb5c7a80717562de9051bf2413aca8e3696bf16"}, - {file = "yarl-1.20.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:595c07bc79af2494365cc96ddeb772f76272364ef7c80fb892ef9d0649586513"}, - {file = "yarl-1.20.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:7bdd2f80f4a7df852ab9ab49484a4dee8030023aa536df41f2d922fd57bf023f"}, - {file = "yarl-1.20.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:c03bfebc4ae8d862f853a9757199677ab74ec25424d0ebd68a0027e9c639a390"}, - {file = "yarl-1.20.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:344d1103e9c1523f32a5ed704d576172d2cabed3122ea90b1d4e11fe17c66458"}, - {file = "yarl-1.20.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:88cab98aa4e13e1ade8c141daeedd300a4603b7132819c484841bb7af3edce9e"}, - {file = "yarl-1.20.1-cp313-cp313t-win32.whl", hash = "sha256:b121ff6a7cbd4abc28985b6028235491941b9fe8fe226e6fdc539c977ea1739d"}, - {file = "yarl-1.20.1-cp313-cp313t-win_amd64.whl", hash = "sha256:541d050a355bbbc27e55d906bc91cb6fe42f96c01413dd0f4ed5a5240513874f"}, - {file = "yarl-1.20.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e42ba79e2efb6845ebab49c7bf20306c4edf74a0b20fc6b2ccdd1a219d12fad3"}, - {file = "yarl-1.20.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:41493b9b7c312ac448b7f0a42a089dffe1d6e6e981a2d76205801a023ed26a2b"}, - {file = "yarl-1.20.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f5a5928ff5eb13408c62a968ac90d43f8322fd56d87008b8f9dabf3c0f6ee983"}, - {file = "yarl-1.20.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:30c41ad5d717b3961b2dd785593b67d386b73feca30522048d37298fee981805"}, - {file = "yarl-1.20.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:59febc3969b0781682b469d4aca1a5cab7505a4f7b85acf6db01fa500fa3f6ba"}, - {file = "yarl-1.20.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d2b6fb3622b7e5bf7a6e5b679a69326b4279e805ed1699d749739a61d242449e"}, - {file = "yarl-1.20.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:749d73611db8d26a6281086f859ea7ec08f9c4c56cec864e52028c8b328db723"}, - {file = "yarl-1.20.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9427925776096e664c39e131447aa20ec738bdd77c049c48ea5200db2237e000"}, - {file = "yarl-1.20.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff70f32aa316393eaf8222d518ce9118148eddb8a53073c2403863b41033eed5"}, - {file = "yarl-1.20.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:c7ddf7a09f38667aea38801da8b8d6bfe81df767d9dfc8c88eb45827b195cd1c"}, - {file = "yarl-1.20.1-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:57edc88517d7fc62b174fcfb2e939fbc486a68315d648d7e74d07fac42cec240"}, - {file = "yarl-1.20.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:dab096ce479d5894d62c26ff4f699ec9072269d514b4edd630a393223f45a0ee"}, - {file = "yarl-1.20.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:14a85f3bd2d7bb255be7183e5d7d6e70add151a98edf56a770d6140f5d5f4010"}, - {file = "yarl-1.20.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:2c89b5c792685dd9cd3fa9761c1b9f46fc240c2a3265483acc1565769996a3f8"}, - {file = "yarl-1.20.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:69e9b141de5511021942a6866990aea6d111c9042235de90e08f94cf972ca03d"}, - {file = "yarl-1.20.1-cp39-cp39-win32.whl", hash = "sha256:b5f307337819cdfdbb40193cad84978a029f847b0a357fbe49f712063cfc4f06"}, - {file = "yarl-1.20.1-cp39-cp39-win_amd64.whl", hash = "sha256:eae7bfe2069f9c1c5b05fc7fe5d612e5bbc089a39309904ee8b829e322dcad00"}, - {file = "yarl-1.20.1-py3-none-any.whl", hash = "sha256:83b8eb083fe4683c6115795d9fc1cfaf2cbbefb19b3a1cb68f6527460f483a77"}, - {file = "yarl-1.20.1.tar.gz", hash = "sha256:d017a4997ee50c91fd5466cef416231bb82177b93b029906cefc542ce14c35ac"}, + {file = "yarl-1.22.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c7bd6683587567e5a49ee6e336e0612bec8329be1b7d4c8af5687dcdeb67ee1e"}, + {file = "yarl-1.22.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5cdac20da754f3a723cceea5b3448e1a2074866406adeb4ef35b469d089adb8f"}, + {file = "yarl-1.22.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:07a524d84df0c10f41e3ee918846e1974aba4ec017f990dc735aad487a0bdfdf"}, + {file = "yarl-1.22.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e1b329cb8146d7b736677a2440e422eadd775d1806a81db2d4cded80a48efc1a"}, + {file = "yarl-1.22.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:75976c6945d85dbb9ee6308cd7ff7b1fb9409380c82d6119bd778d8fcfe2931c"}, + {file = "yarl-1.22.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:80ddf7a5f8c86cb3eb4bc9028b07bbbf1f08a96c5c0bc1244be5e8fefcb94147"}, + {file = "yarl-1.22.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d332fc2e3c94dad927f2112395772a4e4fedbcf8f80efc21ed7cdfae4d574fdb"}, + {file = "yarl-1.22.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0cf71bf877efeac18b38d3930594c0948c82b64547c1cf420ba48722fe5509f6"}, + {file = "yarl-1.22.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:663e1cadaddae26be034a6ab6072449a8426ddb03d500f43daf952b74553bba0"}, + {file = "yarl-1.22.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:6dcbb0829c671f305be48a7227918cfcd11276c2d637a8033a99a02b67bf9eda"}, + {file = "yarl-1.22.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f0d97c18dfd9a9af4490631905a3f131a8e4c9e80a39353919e2cfed8f00aedc"}, + {file = "yarl-1.22.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:437840083abe022c978470b942ff832c3940b2ad3734d424b7eaffcd07f76737"}, + {file = "yarl-1.22.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a899cbd98dce6f5d8de1aad31cb712ec0a530abc0a86bd6edaa47c1090138467"}, + {file = "yarl-1.22.0-cp310-cp310-win32.whl", hash = "sha256:595697f68bd1f0c1c159fcb97b661fc9c3f5db46498043555d04805430e79bea"}, + {file = "yarl-1.22.0-cp310-cp310-win_amd64.whl", hash = "sha256:cb95a9b1adaa48e41815a55ae740cfda005758104049a640a398120bf02515ca"}, + {file = "yarl-1.22.0-cp310-cp310-win_arm64.whl", hash = "sha256:b85b982afde6df99ecc996990d4ad7ccbdbb70e2a4ba4de0aecde5922ba98a0b"}, + {file = "yarl-1.22.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1ab72135b1f2db3fed3997d7e7dc1b80573c67138023852b6efb336a5eae6511"}, + {file = "yarl-1.22.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:669930400e375570189492dc8d8341301578e8493aec04aebc20d4717f899dd6"}, + {file = "yarl-1.22.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:792a2af6d58177ef7c19cbf0097aba92ca1b9cb3ffdd9c7470e156c8f9b5e028"}, + {file = "yarl-1.22.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3ea66b1c11c9150f1372f69afb6b8116f2dd7286f38e14ea71a44eee9ec51b9d"}, + {file = "yarl-1.22.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3e2daa88dc91870215961e96a039ec73e4937da13cf77ce17f9cad0c18df3503"}, + {file = "yarl-1.22.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ba440ae430c00eee41509353628600212112cd5018d5def7e9b05ea7ac34eb65"}, + {file = "yarl-1.22.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e6438cc8f23a9c1478633d216b16104a586b9761db62bfacb6425bac0a36679e"}, + {file = "yarl-1.22.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c52a6e78aef5cf47a98ef8e934755abf53953379b7d53e68b15ff4420e6683d"}, + {file = "yarl-1.22.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3b06bcadaac49c70f4c88af4ffcfbe3dc155aab3163e75777818092478bcbbe7"}, + {file = "yarl-1.22.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:6944b2dc72c4d7f7052683487e3677456050ff77fcf5e6204e98caf785ad1967"}, + {file = "yarl-1.22.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:d5372ca1df0f91a86b047d1277c2aaf1edb32d78bbcefffc81b40ffd18f027ed"}, + {file = "yarl-1.22.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:51af598701f5299012b8416486b40fceef8c26fc87dc6d7d1f6fc30609ea0aa6"}, + {file = "yarl-1.22.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b266bd01fedeffeeac01a79ae181719ff848a5a13ce10075adbefc8f1daee70e"}, + {file = "yarl-1.22.0-cp311-cp311-win32.whl", hash = "sha256:a9b1ba5610a4e20f655258d5a1fdc7ebe3d837bb0e45b581398b99eb98b1f5ca"}, + {file = "yarl-1.22.0-cp311-cp311-win_amd64.whl", hash = "sha256:078278b9b0b11568937d9509b589ee83ef98ed6d561dfe2020e24a9fd08eaa2b"}, + {file = "yarl-1.22.0-cp311-cp311-win_arm64.whl", hash = "sha256:b6a6f620cfe13ccec221fa312139135166e47ae169f8253f72a0abc0dae94376"}, + {file = "yarl-1.22.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e340382d1afa5d32b892b3ff062436d592ec3d692aeea3bef3a5cfe11bbf8c6f"}, + {file = "yarl-1.22.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f1e09112a2c31ffe8d80be1b0988fa6a18c5d5cad92a9ffbb1c04c91bfe52ad2"}, + {file = "yarl-1.22.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:939fe60db294c786f6b7c2d2e121576628468f65453d86b0fe36cb52f987bd74"}, + {file = "yarl-1.22.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e1651bf8e0398574646744c1885a41198eba53dc8a9312b954073f845c90a8df"}, + {file = "yarl-1.22.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b8a0588521a26bf92a57a1705b77b8b59044cdceccac7151bd8d229e66b8dedb"}, + {file = "yarl-1.22.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:42188e6a615c1a75bcaa6e150c3fe8f3e8680471a6b10150c5f7e83f47cc34d2"}, + {file = "yarl-1.22.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f6d2cb59377d99718913ad9a151030d6f83ef420a2b8f521d94609ecc106ee82"}, + {file = "yarl-1.22.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50678a3b71c751d58d7908edc96d332af328839eea883bb554a43f539101277a"}, + {file = "yarl-1.22.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1e8fbaa7cec507aa24ea27a01456e8dd4b6fab829059b69844bd348f2d467124"}, + {file = "yarl-1.22.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:433885ab5431bc3d3d4f2f9bd15bfa1614c522b0f1405d62c4f926ccd69d04fa"}, + {file = "yarl-1.22.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:b790b39c7e9a4192dc2e201a282109ed2985a1ddbd5ac08dc56d0e121400a8f7"}, + {file = "yarl-1.22.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:31f0b53913220599446872d757257be5898019c85e7971599065bc55065dc99d"}, + {file = "yarl-1.22.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a49370e8f711daec68d09b821a34e1167792ee2d24d405cbc2387be4f158b520"}, + {file = "yarl-1.22.0-cp312-cp312-win32.whl", hash = "sha256:70dfd4f241c04bd9239d53b17f11e6ab672b9f1420364af63e8531198e3f5fe8"}, + {file = "yarl-1.22.0-cp312-cp312-win_amd64.whl", hash = "sha256:8884d8b332a5e9b88e23f60bb166890009429391864c685e17bd73a9eda9105c"}, + {file = "yarl-1.22.0-cp312-cp312-win_arm64.whl", hash = "sha256:ea70f61a47f3cc93bdf8b2f368ed359ef02a01ca6393916bc8ff877427181e74"}, + {file = "yarl-1.22.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8dee9c25c74997f6a750cd317b8ca63545169c098faee42c84aa5e506c819b53"}, + {file = "yarl-1.22.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:01e73b85a5434f89fc4fe27dcda2aff08ddf35e4d47bbbea3bdcd25321af538a"}, + {file = "yarl-1.22.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:22965c2af250d20c873cdbee8ff958fb809940aeb2e74ba5f20aaf6b7ac8c70c"}, + {file = "yarl-1.22.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b4f15793aa49793ec8d1c708ab7f9eded1aa72edc5174cae703651555ed1b601"}, + {file = "yarl-1.22.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5542339dcf2747135c5c85f68680353d5cb9ffd741c0f2e8d832d054d41f35a"}, + {file = "yarl-1.22.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5c401e05ad47a75869c3ab3e35137f8468b846770587e70d71e11de797d113df"}, + {file = "yarl-1.22.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:243dda95d901c733f5b59214d28b0120893d91777cb8aa043e6ef059d3cddfe2"}, + {file = "yarl-1.22.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bec03d0d388060058f5d291a813f21c011041938a441c593374da6077fe21b1b"}, + {file = "yarl-1.22.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b0748275abb8c1e1e09301ee3cf90c8a99678a4e92e4373705f2a2570d581273"}, + {file = "yarl-1.22.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:47fdb18187e2a4e18fda2c25c05d8251a9e4a521edaed757fef033e7d8498d9a"}, + {file = "yarl-1.22.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c7044802eec4524fde550afc28edda0dd5784c4c45f0be151a2d3ba017daca7d"}, + {file = "yarl-1.22.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:139718f35149ff544caba20fce6e8a2f71f1e39b92c700d8438a0b1d2a631a02"}, + {file = "yarl-1.22.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e1b51bebd221006d3d2f95fbe124b22b247136647ae5dcc8c7acafba66e5ee67"}, + {file = "yarl-1.22.0-cp313-cp313-win32.whl", hash = "sha256:d3e32536234a95f513bd374e93d717cf6b2231a791758de6c509e3653f234c95"}, + {file = "yarl-1.22.0-cp313-cp313-win_amd64.whl", hash = "sha256:47743b82b76d89a1d20b83e60d5c20314cbd5ba2befc9cda8f28300c4a08ed4d"}, + {file = "yarl-1.22.0-cp313-cp313-win_arm64.whl", hash = "sha256:5d0fcda9608875f7d052eff120c7a5da474a6796fe4d83e152e0e4d42f6d1a9b"}, + {file = "yarl-1.22.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:719ae08b6972befcba4310e49edb1161a88cdd331e3a694b84466bd938a6ab10"}, + {file = "yarl-1.22.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:47d8a5c446df1c4db9d21b49619ffdba90e77c89ec6e283f453856c74b50b9e3"}, + {file = "yarl-1.22.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:cfebc0ac8333520d2d0423cbbe43ae43c8838862ddb898f5ca68565e395516e9"}, + {file = "yarl-1.22.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4398557cbf484207df000309235979c79c4356518fd5c99158c7d38203c4da4f"}, + {file = "yarl-1.22.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2ca6fd72a8cd803be290d42f2dec5cdcd5299eeb93c2d929bf060ad9efaf5de0"}, + {file = "yarl-1.22.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ca1f59c4e1ab6e72f0a23c13fca5430f889634166be85dbf1013683e49e3278e"}, + {file = "yarl-1.22.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6c5010a52015e7c70f86eb967db0f37f3c8bd503a695a49f8d45700144667708"}, + {file = "yarl-1.22.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d7672ecf7557476642c88497c2f8d8542f8e36596e928e9bcba0e42e1e7d71f"}, + {file = "yarl-1.22.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:3b7c88eeef021579d600e50363e0b6ee4f7f6f728cd3486b9d0f3ee7b946398d"}, + {file = "yarl-1.22.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:f4afb5c34f2c6fecdcc182dfcfc6af6cccf1aa923eed4d6a12e9d96904e1a0d8"}, + {file = "yarl-1.22.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:59c189e3e99a59cf8d83cbb31d4db02d66cda5a1a4374e8a012b51255341abf5"}, + {file = "yarl-1.22.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:5a3bf7f62a289fa90f1990422dc8dff5a458469ea71d1624585ec3a4c8d6960f"}, + {file = "yarl-1.22.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:de6b9a04c606978fdfe72666fa216ffcf2d1a9f6a381058d4378f8d7b1e5de62"}, + {file = "yarl-1.22.0-cp313-cp313t-win32.whl", hash = "sha256:1834bb90991cc2999f10f97f5f01317f99b143284766d197e43cd5b45eb18d03"}, + {file = "yarl-1.22.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ff86011bd159a9d2dfc89c34cfd8aff12875980e3bd6a39ff097887520e60249"}, + {file = "yarl-1.22.0-cp313-cp313t-win_arm64.whl", hash = "sha256:7861058d0582b847bc4e3a4a4c46828a410bca738673f35a29ba3ca5db0b473b"}, + {file = "yarl-1.22.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:34b36c2c57124530884d89d50ed2c1478697ad7473efd59cfd479945c95650e4"}, + {file = "yarl-1.22.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:0dd9a702591ca2e543631c2a017e4a547e38a5c0f29eece37d9097e04a7ac683"}, + {file = "yarl-1.22.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:594fcab1032e2d2cc3321bb2e51271e7cd2b516c7d9aee780ece81b07ff8244b"}, + {file = "yarl-1.22.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f3d7a87a78d46a2e3d5b72587ac14b4c16952dd0887dbb051451eceac774411e"}, + {file = "yarl-1.22.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:852863707010316c973162e703bddabec35e8757e67fcb8ad58829de1ebc8590"}, + {file = "yarl-1.22.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:131a085a53bfe839a477c0845acf21efc77457ba2bcf5899618136d64f3303a2"}, + {file = "yarl-1.22.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:078a8aefd263f4d4f923a9677b942b445a2be970ca24548a8102689a3a8ab8da"}, + {file = "yarl-1.22.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bca03b91c323036913993ff5c738d0842fc9c60c4648e5c8d98331526df89784"}, + {file = "yarl-1.22.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:68986a61557d37bb90d3051a45b91fa3d5c516d177dfc6dd6f2f436a07ff2b6b"}, + {file = "yarl-1.22.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:4792b262d585ff0dff6bcb787f8492e40698443ec982a3568c2096433660c694"}, + {file = "yarl-1.22.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:ebd4549b108d732dba1d4ace67614b9545b21ece30937a63a65dd34efa19732d"}, + {file = "yarl-1.22.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f87ac53513d22240c7d59203f25cc3beac1e574c6cd681bbfd321987b69f95fd"}, + {file = "yarl-1.22.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:22b029f2881599e2f1b06f8f1db2ee63bd309e2293ba2d566e008ba12778b8da"}, + {file = "yarl-1.22.0-cp314-cp314-win32.whl", hash = "sha256:6a635ea45ba4ea8238463b4f7d0e721bad669f80878b7bfd1f89266e2ae63da2"}, + {file = "yarl-1.22.0-cp314-cp314-win_amd64.whl", hash = "sha256:0d6e6885777af0f110b0e5d7e5dda8b704efed3894da26220b7f3d887b839a79"}, + {file = "yarl-1.22.0-cp314-cp314-win_arm64.whl", hash = "sha256:8218f4e98d3c10d683584cb40f0424f4b9fd6e95610232dd75e13743b070ee33"}, + {file = "yarl-1.22.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:45c2842ff0e0d1b35a6bf1cd6c690939dacb617a70827f715232b2e0494d55d1"}, + {file = "yarl-1.22.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:d947071e6ebcf2e2bee8fce76e10faca8f7a14808ca36a910263acaacef08eca"}, + {file = "yarl-1.22.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:334b8721303e61b00019474cc103bdac3d7b1f65e91f0bfedeec2d56dfe74b53"}, + {file = "yarl-1.22.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e7ce67c34138a058fd092f67d07a72b8e31ff0c9236e751957465a24b28910c"}, + {file = "yarl-1.22.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d77e1b2c6d04711478cb1c4ab90db07f1609ccf06a287d5607fcd90dc9863acf"}, + {file = "yarl-1.22.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4647674b6150d2cae088fc07de2738a84b8bcedebef29802cf0b0a82ab6face"}, + {file = "yarl-1.22.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:efb07073be061c8f79d03d04139a80ba33cbd390ca8f0297aae9cce6411e4c6b"}, + {file = "yarl-1.22.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e51ac5435758ba97ad69617e13233da53908beccc6cfcd6c34bbed8dcbede486"}, + {file = "yarl-1.22.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:33e32a0dd0c8205efa8e83d04fc9f19313772b78522d1bdc7d9aed706bfd6138"}, + {file = "yarl-1.22.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:bf4a21e58b9cde0e401e683ebd00f6ed30a06d14e93f7c8fd059f8b6e8f87b6a"}, + {file = "yarl-1.22.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:e4b582bab49ac33c8deb97e058cd67c2c50dac0dd134874106d9c774fd272529"}, + {file = "yarl-1.22.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:0b5bcc1a9c4839e7e30b7b30dd47fe5e7e44fb7054ec29b5bb8d526aa1041093"}, + {file = "yarl-1.22.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:c0232bce2170103ec23c454e54a57008a9a72b5d1c3105dc2496750da8cfa47c"}, + {file = "yarl-1.22.0-cp314-cp314t-win32.whl", hash = "sha256:8009b3173bcd637be650922ac455946197d858b3630b6d8787aa9e5c4564533e"}, + {file = "yarl-1.22.0-cp314-cp314t-win_amd64.whl", hash = "sha256:9fb17ea16e972c63d25d4a97f016d235c78dd2344820eb35bc034bc32012ee27"}, + {file = "yarl-1.22.0-cp314-cp314t-win_arm64.whl", hash = "sha256:9f6d73c1436b934e3f01df1e1b21ff765cd1d28c77dfb9ace207f746d4610ee1"}, + {file = "yarl-1.22.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3aa27acb6de7a23785d81557577491f6c38a5209a254d1191519d07d8fe51748"}, + {file = "yarl-1.22.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:af74f05666a5e531289cb1cc9c883d1de2088b8e5b4de48004e5ca8a830ac859"}, + {file = "yarl-1.22.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:62441e55958977b8167b2709c164c91a6363e25da322d87ae6dd9c6019ceecf9"}, + {file = "yarl-1.22.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b580e71cac3f8113d3135888770903eaf2f507e9421e5697d6ee6d8cd1c7f054"}, + {file = "yarl-1.22.0-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e81fda2fb4a07eda1a2252b216aa0df23ebcd4d584894e9612e80999a78fd95b"}, + {file = "yarl-1.22.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:99b6fc1d55782461b78221e95fc357b47ad98b041e8e20f47c1411d0aacddc60"}, + {file = "yarl-1.22.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:088e4e08f033db4be2ccd1f34cf29fe994772fb54cfe004bbf54db320af56890"}, + {file = "yarl-1.22.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e4e1f6f0b4da23e61188676e3ed027ef0baa833a2e633c29ff8530800edccba"}, + {file = "yarl-1.22.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:84fc3ec96fce86ce5aa305eb4aa9358279d1aa644b71fab7b8ed33fe3ba1a7ca"}, + {file = "yarl-1.22.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:5dbeefd6ca588b33576a01b0ad58aa934bc1b41ef89dee505bf2932b22ddffba"}, + {file = "yarl-1.22.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:14291620375b1060613f4aab9ebf21850058b6b1b438f386cc814813d901c60b"}, + {file = "yarl-1.22.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:a4fcfc8eb2c34148c118dfa02e6427ca278bfd0f3df7c5f99e33d2c0e81eae3e"}, + {file = "yarl-1.22.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:029866bde8d7b0878b9c160e72305bbf0a7342bcd20b9999381704ae03308dc8"}, + {file = "yarl-1.22.0-cp39-cp39-win32.whl", hash = "sha256:4dcc74149ccc8bba31ce1944acee24813e93cfdee2acda3c172df844948ddf7b"}, + {file = "yarl-1.22.0-cp39-cp39-win_amd64.whl", hash = "sha256:10619d9fdee46d20edc49d3479e2f8269d0779f1b031e6f7c2aa1c76be04b7ed"}, + {file = "yarl-1.22.0-cp39-cp39-win_arm64.whl", hash = "sha256:dd7afd3f8b0bfb4e0d9fc3c31bfe8a4ec7debe124cfd90619305def3c8ca8cd2"}, + {file = "yarl-1.22.0-py3-none-any.whl", hash = "sha256:1380560bdba02b6b6c90de54133c81c9f2a453dee9912fe58c1dcced1edb7cff"}, + {file = "yarl-1.22.0.tar.gz", hash = "sha256:bebf8557577d4401ba8bd9ff33906f1376c877aa78d1fe216ad01b4d6745af71"}, ] [package.dependencies] @@ -5482,4 +4391,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.1" python-versions = "^3.12" -content-hash = "3ebf19a118a8cfdf86f87b676e064b22892c92f010cb3ff36a9e2df706c0bda6" +content-hash = "3fabf80b239db81e20d419ce9e3da20d5f6e5ea9139afffdb8ca38659cfbcfa3" diff --git a/projects/file_enrichment/pyproject.toml b/projects/file_enrichment/pyproject.toml index 147afe8..d87d833 100644 --- a/projects/file_enrichment/pyproject.toml +++ b/projects/file_enrichment/pyproject.toml @@ -7,9 +7,6 @@ readme = "README.md" [tool.poetry.dependencies] python = "^3.12" fastapi = "^0.115.6" -dapr = "^1.15.0" -dapr-ext-fastapi = "^1.15.0" -dapr-ext-workflow = "^1.15.0" pydantic = "^2.10.4" python-magic = "^0.4.27" binaryornot = "^0.4.4" @@ -23,96 +20,35 @@ yara-x = "^1.1.0" pypdf = "^5.4.0" plyara = "^2.2.7" common = { path = "../../libs/common", develop = true } +chromium = { path = "../../libs/chromium", develop = true } +nemesis_dpapi = { path = "../../libs/nemesis_dpapi", develop = true } +file_linking = { path = "../../libs/file_linking", develop = true } file_enrichment_modules = { path = "../../libs/file_enrichment_modules", develop = true } structlog = "^25.1.0" colorlog = "^6.9.0" psycopg = {extras = ["pool"], version = "^3.2.9"} -opentelemetry-instrumentation-fastapi = "^0.51b0" -opentelemetry-api = "^1.30.0" -opentelemetry-sdk = "^1.30.0" -opentelemetry-instrumentation = "^0.51b0" -opentelemetry-exporter-zipkin-json = "^1.30.0" -opentelemetry-exporter-otlp-proto-grpc = "^1.30.0" -rigging = "^2.2.4" pyarrow = "^19.0.1" -impacket = "^0.12.0" msoffcrypto-tool = "^5.4.2" oletools = "^0.60.2" pypykatz = "^0.6.11" - +cryptography = "^42.0.8" +uvicorn = "^0.37.0" +regipy = "^5.2.0" +dapr-ext-fastapi = "1.16.0" +dapr-ext-workflow = "1.16.0" +dapr = "1.16.0" +durabletask-dapr = "0.2.0a8" +protobuf = "6.31.1" +pillow = "^11.3.0" +opentelemetry-api = "^1.38.0" +opentelemetry-sdk = "^1.38.0" +opentelemetry-exporter-otlp-proto-grpc = "^1.38.0" [tool.poetry.group.dev.dependencies] ruff = "^0.9.2" +pytest = "^8.4.1" +pytest-asyncio = "^1.1.0" [build-system] requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" - -[tool.ruff] -select = [ - "E", # pycodestyle errors - "W", # pycodestyle warnings - "F", # pyflakes - "I", # isort - "C", # flake8-comprehensions - "B", # flake8-bugbear - "UP", # pyupgrade - "NPY", # numpydoc - "A", # flake8-annotations - "TCH001", # Move application-only imports into TYPE_CHECKING block - "TCH002", # Move third-party imports into TYPE_CHECKING block - "TCH003", # Move standard library imports into TYPE_CHECKING block -] -ignore = [ - "E501", # line too long, handled by black - "B008", # do not perform function calls in argument defaults - "C901", # too complex - "W191", # indentation contains tabs - "F722", # syntax error in forward annotation - "UP007", # X | Y syntax while we're still supporting 3.9 - "UP038", # isinstance() X | Y instance ^ - "B905", # zip() without strict (isn't supported in 3.9) -] - -exclude = [ - ".bzr", - ".direnv", - ".eggs", - ".git", - ".git-rewrite", - ".hg", - ".mypy_cache", - ".nox", - ".pants.d", - ".pytype", - ".ruff_cache", - ".svn", - ".tox", - ".venv", - "__pypackages__", - "_build", - "buck-out", - "build", - "dist", - "node_modules", - "venv", -] - -line-length = 120 -indent-width = 4 -target-version = "py312" - -[tool.ruff.lint] -fixable = ["ALL"] -unfixable = ["B"] - -dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" -extend-select = [ - "I", # isort, added per instructions in the VS code extension (https://marketplace.visualstudio.com/items?itemName=charliermarsh.ruff) -] - -[tool.ruff.format] -quote-style = "double" -indent-style = "space" -skip-magic-trailing-comma = false -line-ending = "auto" diff --git a/projects/file_enrichment/tests/test_example.py b/projects/file_enrichment/tests/test_example.py new file mode 100644 index 0000000..b497045 --- /dev/null +++ b/projects/file_enrichment/tests/test_example.py @@ -0,0 +1,3 @@ +def test_example(): + """Simple example test to verify pytest is working.""" + assert True diff --git a/projects/frontend/.vscode/settings.json b/projects/frontend/.vscode/settings.json index 4b20349..313408d 100644 --- a/projects/frontend/.vscode/settings.json +++ b/projects/frontend/.vscode/settings.json @@ -7,5 +7,13 @@ "prettier.requireConfig": true, "files.associations": { "*.jsx": "javascriptreact" + }, + "files.exclude": { + "**/node_modules": true, + "**/dist": true + }, + "search.exclude": { + "**/node_modules": true, + "**/dist": true } } \ No newline at end of file diff --git a/projects/frontend/Dockerfile b/projects/frontend/Dockerfile index 148d392..2be5553 100644 --- a/projects/frontend/Dockerfile +++ b/projects/frontend/Dockerfile @@ -11,6 +11,8 @@ COPY package.json ./ RUN npm install COPY . . +# Note: version.json will be mounted as a volume in dev or copied during CI build + ######################## # Development ######################## diff --git a/projects/frontend/index.html b/projects/frontend/index.html index e1b375a..436a017 100644 --- a/projects/frontend/index.html +++ b/projects/frontend/index.html @@ -8,11 +8,7 @@
- + \ No newline at end of file diff --git a/projects/frontend/src/App.jsx b/projects/frontend/src/App.jsx index 9cac623..4b82aa5 100644 --- a/projects/frontend/src/App.jsx +++ b/projects/frontend/src/App.jsx @@ -1,16 +1,22 @@ // src/App.jsx import { createClient } from 'graphql-ws'; import { + BarChart2, + Bot, ChevronLeft, ChevronRight, + FileArchive, FileSearch, FileText, + FolderTree, HelpCircle, + Key, LayoutDashboard, Search, Settings, Siren, - Upload + Upload, + Globe } from 'lucide-react'; import React, { useEffect, useState } from 'react'; import { Route, BrowserRouter as Router, Routes, useLocation, useNavigate } from 'react-router-dom'; @@ -33,6 +39,14 @@ import DocumentSearch from './components/Search/DocumentSearch'; import SettingsPage from './components/Settings/SettingsPage'; import ThemeToggle from './components/ThemeToggle'; import YaraRulesManager from './components/Yara/YaraManager'; +import Containers from './components/Containers/Containers'; +import AgentsPage from './components/Agents/AgentsPage'; +import FileBrowser from './components/FileBrowser/FileBrowser'; +import Chromium from './components/Chromium/Chromium'; +import Dpapi from './components/Dpapi/Dpapi'; +import ReportingPage from './components/Reporting/ReportingPage'; +import SourceReportPage from './components/Reporting/SourceReportPage'; +import SystemReportPage from './components/Reporting/SystemReportPage'; // Assets import logoDark from './img/nemesis_logo_dark.png'; @@ -44,6 +58,7 @@ const Sidebar = ({ onCollapse }) => { return savedState ? JSON.parse(savedState) : false; }); const [findingsCount, setFindingsCount] = useState(0); + const [litellmAvailable, setLitellmAvailable] = useState(false); const navigate = useNavigate(); const location = useLocation(); @@ -53,6 +68,22 @@ const Sidebar = ({ onCollapse }) => { }, [isCollapsed]); useEffect(() => { + // Check LiteLLM availability + const checkLitellmAvailability = async () => { + try { + const response = await fetch('/api/system/available-services'); + if (response.ok) { + const data = await response.json(); + const availableServices = data.services || []; + setLitellmAvailable(availableServices.includes('/llm')); + } + } catch (err) { + console.error('Error checking LiteLLM availability:', err); + } + }; + + checkLitellmAvailability(); + // Initial count fetch const fetchFindingsCount = async () => { const query = { @@ -152,15 +183,25 @@ const Sidebar = ({ onCollapse }) => { onCollapse?.(newState); }; - const navigationItems = [ + const baseNavigationItems = [ { id: 'dashboard', label: 'Dashboard', icon: LayoutDashboard, path: '/' }, - { id: 'files', label: 'Files', icon: FileText, path: '/files' }, { id: 'upload', label: 'File Upload', icon: Upload, path: '/upload' }, - { id: 'findings', label: `Findings - ${findingsCount} Untriaged`, icon: Siren, path: '/findings?triage_state=untriaged', count: findingsCount }, + { id: 'files', label: 'Files', icon: FileText, path: '/files' }, + { id: 'findings', label: `Findings - ${findingsCount} Untriaged`, icon: Siren, path: '/findings', count: findingsCount }, + { id: 'file-browser', label: 'File Browser', icon: FolderTree, path: '/file-browser' }, + { id: 'chromium', label: 'Chromium', icon: Globe, path: '/chromium' }, + { id: 'dpapi', label: 'Dpapi', icon: Key, path: '/dpapi' }, { id: 'search', label: 'Document Search', icon: Search, path: '/search' }, - { id: 'yara', label: 'Yara Rules', icon: FileSearch, path: '/yara-rules' } + { id: 'yara', label: 'Yara Rules', icon: FileSearch, path: '/yara-rules' }, + { id: 'containers', label: 'Containers', icon: FileArchive, path: '/containers' }, + { id: 'reporting', label: 'Reporting', icon: BarChart2, path: '/reporting' } ]; + // Add Agents tab if LiteLLM is available + const navigationItems = litellmAvailable + ? [...baseNavigationItems, { id: 'agents', label: 'Agents', icon: Bot, path: '/agents' }] + : baseNavigationItems; + const utilityItems = [ { id: 'settings', label: 'Settings', icon: Settings, path: '/settings' }, { id: 'help', label: 'Help', icon: HelpCircle, path: '/help' } @@ -320,10 +361,18 @@ const App = () => { } /> } /> } /> + } /> } /> } /> } /> + } /> + } /> } /> + } /> + } /> + } /> + } /> + } /> } /> } /> diff --git a/projects/frontend/src/components/Agents/AgentsPage.jsx b/projects/frontend/src/components/Agents/AgentsPage.jsx new file mode 100644 index 0000000..41c69ab --- /dev/null +++ b/projects/frontend/src/components/Agents/AgentsPage.jsx @@ -0,0 +1,458 @@ +import React, { useState, useEffect } from 'react'; +import { Bot, Edit3, Save, X, AlertCircle, Clock } from 'lucide-react'; + +// Status badge component +const StatusBadge = ({ enabled, hasPrompt }) => { + if (!enabled) { + return ( + + + Disabled + + ); + } + + const isRuleBased = !hasPrompt; + const badgeClasses = isRuleBased + ? "inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-purple-100 text-purple-800 dark:bg-purple-900 dark:text-purple-200" + : "inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200"; + + return ( + + + {hasPrompt ? 'LLM-based' : 'Rule-based'} + + ); +}; + +// Agent card component +const AgentCard = ({ agent, onEditClick, isEditing, editedPrompt, onPromptChange, onSaveClick, onCancelClick, saving }) => { + return ( +
+ {/* Header */} +
+
+ +
+

+ {agent.name} +

+

+ {agent.type ? agent.type.replace('_', ' ') : 'Agent'} +

+
+
+
+ + {agent.has_prompt && ( + + )} +
+
+ + {/* Description */} +
+

+ {agent.description} +

+
+ + {/* Edit Section */} + {isEditing && ( +
+
+ +