├── .python-version ├── tests ├── __init__.py └── test_cli_mcp_server.py ├── glama.json ├── .gitignore ├── src └── cli_mcp_server │ ├── __init__.py │ └── server.py ├── .github └── workflows │ └── python-tests.yml ├── pyproject.toml ├── LICENSE ├── README.md └── uv.lock /.python-version: -------------------------------------------------------------------------------- 1 | 3.13 2 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | # tests package 2 | -------------------------------------------------------------------------------- /glama.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://glama.ai/mcp/schemas/server.json", 3 | "maintainers": [ 4 | "MladenSU" 5 | ] 6 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Python-generated files 2 | __pycache__/ 3 | *.py[oc] 4 | build/ 5 | dist/ 6 | wheels/ 7 | *.egg-info 8 | 9 | # Virtual environments 10 | .venv 11 | 12 | # PyCharm 13 | .idea/ -------------------------------------------------------------------------------- /src/cli_mcp_server/__init__.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | 3 | from . import server 4 | 5 | 6 | def main(): 7 | """Main entry point for the package.""" 8 | asyncio.run(server.main()) 9 | 10 | 11 | # Optionally expose other important items at package level 12 | __all__ = ["main", "server"] 13 | -------------------------------------------------------------------------------- /.github/workflows/python-tests.yml: -------------------------------------------------------------------------------- 1 | name: Python Tests 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | test: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v3 13 | - uses: actions/setup-python@v4 14 | with: 15 | python-version: '3.10' 16 | - run: | 17 | python -m pip install --upgrade pip 18 | python -m pip install . 19 | - run: python -m unittest discover -v -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "cli-mcp-server" 3 | version = "0.2.5" 4 | description = "Command line interface for MCP clients with secure execution and customizable security policies" 5 | readme = "README.md" 6 | requires-python = ">=3.10" 7 | dependencies = ["mcp>=1.10.1"] 8 | authors = [ 9 | { name = "Mladen", email = "fangs-lever6n@icloud.com" }, 10 | ] 11 | 12 | [build-system] 13 | requires = ["hatchling"] 14 | build-backend = "hatchling.build" 15 | 16 | [project.scripts] 17 | cli-mcp-server = "cli_mcp_server:main" 18 | 19 | [project.urls] 20 | Homepage = "https://github.com/MladenSU/cli-mcp-server" 21 | Documentation = "https://github.com/MladenSU/cli-mcp-server#readme" 22 | Repository = "https://github.com/MladenSU/cli-mcp-server.git" 23 | "Bug Tracker" = "https://github.com/MladenSU/cli-mcp-server/issues" -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Mladen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CLI MCP Server 2 | 3 | --- 4 | 5 | A secure Model Context Protocol (MCP) server implementation for executing controlled command-line operations with 6 | comprehensive security features. 7 | 8 | ![License](https://img.shields.io/badge/license-MIT-blue.svg) 9 | ![Python Version](https://img.shields.io/badge/python-3.10%2B-blue) 10 | ![MCP Protocol](https://img.shields.io/badge/MCP-Compatible-green) 11 | [![smithery badge](https://smithery.ai/badge/cli-mcp-server)](https://smithery.ai/protocol/cli-mcp-server) 12 | [![Python Tests](https://github.com/MladenSU/cli-mcp-server/actions/workflows/python-tests.yml/badge.svg)](https://github.com/MladenSU/cli-mcp-server/actions/workflows/python-tests.yml) 13 | 14 | 15 | 16 | --- 17 | 18 | # Table of Contents 19 | 20 | 1. [Overview](#overview) 21 | 2. [Features](#features) 22 | 3. [Configuration](#configuration) 23 | 4. [Available Tools](#available-tools) 24 | - [run_command](#run_command) 25 | - [show_security_rules](#show_security_rules) 26 | 5. [Usage with Claude Desktop](#usage-with-claude-desktop) 27 | - [Development/Unpublished Servers Configuration](#developmentunpublished-servers-configuration) 28 | - [Published Servers Configuration](#published-servers-configuration) 29 | 6. [Security Features](#security-features) 30 | 7. [Error Handling](#error-handling) 31 | 8. [Development](#development) 32 | - [Prerequisites](#prerequisites) 33 | - [Building and Publishing](#building-and-publishing) 34 | - [Debugging](#debugging) 35 | 9. [License](#license) 36 | 37 | --- 38 | 39 | ## Overview 40 | 41 | This MCP server enables secure command-line execution with robust security measures including command whitelisting, path 42 | validation, and execution controls. Perfect for providing controlled CLI access to LLM applications while maintaining security. 43 | 44 | ## Features 45 | 46 | - 🔒 Secure command execution with strict validation 47 | - ⚙️ Configurable command and flag whitelisting with 'all' option 48 | - 🛡️ Path traversal prevention and validation 49 | - 🚫 Shell operator injection protection 50 | - ⏱️ Execution timeouts and length limits 51 | - 📝 Detailed error reporting 52 | - 🔄 Async operation support 53 | - 🎯 Working directory restriction and validation 54 | 55 | ## Configuration 56 | 57 | Configure the server using environment variables: 58 | 59 | | Variable | Description | Default | 60 | |---------------------|------------------------------------------------------|-------------------| 61 | | `ALLOWED_DIR` | Base directory for command execution (Required) | None (Required) | 62 | | `ALLOWED_COMMANDS` | Comma-separated list of allowed commands or 'all' | `ls,cat,pwd` | 63 | | `ALLOWED_FLAGS` | Comma-separated list of allowed flags or 'all' | `-l,-a,--help` | 64 | | `MAX_COMMAND_LENGTH`| Maximum command string length | `1024` | 65 | | `COMMAND_TIMEOUT` | Command execution timeout (seconds) | `30` | 66 | | `ALLOW_SHELL_OPERATORS` | Allow shell operators (&&, \|\|, \|, >, etc.) | `false` | 67 | 68 | Note: Setting `ALLOWED_COMMANDS` or `ALLOWED_FLAGS` to 'all' will allow any command or flag respectively. 69 | 70 | ## Installation 71 | 72 | To install CLI MCP Server for Claude Desktop automatically via [Smithery](https://smithery.ai/protocol/cli-mcp-server): 73 | 74 | ```bash 75 | npx @smithery/cli install cli-mcp-server --client claude 76 | ``` 77 | 78 | ## Available Tools 79 | 80 | ### run_command 81 | 82 | Executes whitelisted CLI commands within allowed directories. 83 | 84 | **Input Schema:** 85 | ```json 86 | { 87 | "command": { 88 | "type": "string", 89 | "description": "Single command to execute (e.g., 'ls -l' or 'cat file.txt')" 90 | } 91 | } 92 | ``` 93 | 94 | **Security Notes:** 95 | - Shell operators (&&, |, >, >>) are not supported by default, but can be enabled with `ALLOW_SHELL_OPERATORS=true` 96 | - Commands must be whitelisted unless ALLOWED_COMMANDS='all' 97 | - Flags must be whitelisted unless ALLOWED_FLAGS='all' 98 | - All paths are validated to be within ALLOWED_DIR 99 | 100 | ### show_security_rules 101 | 102 | Displays current security configuration and restrictions, including: 103 | - Working directory 104 | - Allowed commands 105 | - Allowed flags 106 | - Security limits (max command length and timeout) 107 | 108 | ## Usage with Claude Desktop 109 | 110 | Add to your `~/Library/Application\ Support/Claude/claude_desktop_config.json`: 111 | 112 | > Development/Unpublished Servers Configuration 113 | 114 | ```json 115 | { 116 | "mcpServers": { 117 | "cli-mcp-server": { 118 | "command": "uv", 119 | "args": [ 120 | "--directory", 121 | "/cli-mcp-server", 122 | "run", 123 | "cli-mcp-server" 124 | ], 125 | "env": { 126 | "ALLOWED_DIR": "", 127 | "ALLOWED_COMMANDS": "ls,cat,pwd,echo", 128 | "ALLOWED_FLAGS": "-l,-a,--help,--version", 129 | "MAX_COMMAND_LENGTH": "1024", 130 | "COMMAND_TIMEOUT": "30", 131 | "ALLOW_SHELL_OPERATORS": "false" 132 | } 133 | } 134 | } 135 | } 136 | ``` 137 | 138 | > Published Servers Configuration 139 | 140 | ```json 141 | { 142 | "mcpServers": { 143 | "cli-mcp-server": { 144 | "command": "uvx", 145 | "args": [ 146 | "cli-mcp-server" 147 | ], 148 | "env": { 149 | "ALLOWED_DIR": "", 150 | "ALLOWED_COMMANDS": "ls,cat,pwd,echo", 151 | "ALLOWED_FLAGS": "-l,-a,--help,--version", 152 | "MAX_COMMAND_LENGTH": "1024", 153 | "COMMAND_TIMEOUT": "30", 154 | "ALLOW_SHELL_OPERATORS": "false" 155 | } 156 | } 157 | } 158 | } 159 | ``` 160 | > In case it's not working or showing in the UI, clear your cache via `uv clean`. 161 | 162 | ## Security Features 163 | 164 | - ✅ Command whitelist enforcement with 'all' option 165 | - ✅ Flag validation with 'all' option 166 | - ✅ Path traversal prevention and normalization 167 | - ✅ Shell operator blocking (with opt-in support via `ALLOW_SHELL_OPERATORS=true`) 168 | - ✅ Command length limits 169 | - ✅ Execution timeouts 170 | - ✅ Working directory restrictions 171 | - ✅ Symlink resolution and validation 172 | 173 | ## Error Handling 174 | 175 | The server provides detailed error messages for: 176 | 177 | - Security violations (CommandSecurityError) 178 | - Command timeouts (CommandTimeoutError) 179 | - Invalid command formats 180 | - Path security violations 181 | - Execution failures (CommandExecutionError) 182 | - General command errors (CommandError) 183 | 184 | ## Development 185 | 186 | ### Prerequisites 187 | 188 | - Python 3.10+ 189 | - MCP protocol library 190 | 191 | ### Building and Publishing 192 | 193 | To prepare the package for distribution: 194 | 195 | 1. Sync dependencies and update lockfile: 196 | ```bash 197 | uv sync 198 | ``` 199 | 200 | 2. Build package distributions: 201 | ```bash 202 | uv build 203 | ``` 204 | 205 | > This will create source and wheel distributions in the `dist/` directory. 206 | 207 | 3. Publish to PyPI: 208 | ```bash 209 | uv publish --token {{YOUR_PYPI_API_TOKEN}} 210 | ``` 211 | 212 | ### Debugging 213 | 214 | Since MCP servers run over stdio, debugging can be challenging. For the best debugging 215 | experience, we strongly recommend using the [MCP Inspector](https://github.com/modelcontextprotocol/inspector). 216 | 217 | You can launch the MCP Inspector via [`npm`](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) with 218 | this command: 219 | 220 | ```bash 221 | npx @modelcontextprotocol/inspector uv --directory {{your source code local directory}}/cli-mcp-server run cli-mcp-server 222 | ``` 223 | 224 | Upon launching, the Inspector will display a URL that you can access in your browser to begin debugging. 225 | 226 | ## License 227 | 228 | This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. 229 | 230 | --- 231 | 232 | For more information or support, please open an issue on the project repository. -------------------------------------------------------------------------------- /tests/test_cli_mcp_server.py: -------------------------------------------------------------------------------- 1 | import os 2 | import importlib 3 | import asyncio 4 | import shutil 5 | import tempfile 6 | import unittest 7 | 8 | 9 | # Helper to print results in a simple table format 10 | def print_results_table(name: str, results: list) -> None: 11 | print(f"\n[{name}] Results Table:") 12 | print("Idx | Type | Error | Text") 13 | print("-----|--------|-------|-----") 14 | for idx, tc in enumerate(results): 15 | error_flag = getattr(tc, "error", False) 16 | # Replace newlines in text for single-line display 17 | text = tc.text.strip().replace("\n", "\\n") 18 | print(f"{idx:<3} | {tc.type:<6} | {error_flag!s:<5} | {text}") 19 | 20 | 21 | class TestCLIMCPServer(unittest.TestCase): 22 | def setUp(self): 23 | # Create a temporary directory for allowed_dir 24 | self.tempdir = tempfile.TemporaryDirectory() 25 | os.environ["ALLOWED_DIR"] = self.tempdir.name 26 | # Remove custom allowed commands/flags to use defaults 27 | os.environ.pop("ALLOWED_COMMANDS", None) 28 | os.environ.pop("ALLOWED_FLAGS", None) 29 | # Ensure shell operators are disabled by default 30 | os.environ.pop("ALLOW_SHELL_OPERATORS", None) 31 | # Reload server module to pick up env changes 32 | try: 33 | import cli_mcp_server.server as server_module 34 | 35 | self.server = importlib.reload(server_module) 36 | except ImportError: 37 | import cli_mcp_server.server as server_module 38 | 39 | self.server = server_module 40 | 41 | def tearDown(self): 42 | self.tempdir.cleanup() 43 | 44 | def test_run_pwd(self): 45 | # Run 'pwd' command 46 | result = asyncio.run( 47 | self.server.handle_call_tool("run_command", {"command": "pwd"}) 48 | ) 49 | texts = [tc.text for tc in result] 50 | # Debug print: show results in table form 51 | print_results_table("test_run_pwd", result) 52 | self.assertTrue(texts, "No output returned") 53 | self.assertEqual(texts[0].strip(), self.tempdir.name) 54 | self.assertTrue(any("return code: 0" in text for text in texts)) 55 | 56 | def test_run_ls(self): 57 | # Create a file in the allowed directory 58 | file_path = os.path.join(self.tempdir.name, "foo.txt") 59 | with open(file_path, "w") as f: 60 | f.write("test") 61 | result = asyncio.run( 62 | self.server.handle_call_tool("run_command", {"command": "ls"}) 63 | ) 64 | texts = [tc.text for tc in result] 65 | # Debug print: show results in table form 66 | print_results_table("test_run_ls", result) 67 | self.assertTrue( 68 | any("foo.txt" in text for text in texts), 69 | f"Output did not contain 'foo.txt': {texts}", 70 | ) 71 | self.assertTrue(any("return code: 0" in text for text in texts)) 72 | 73 | def test_run_curl_ifconfig(self): 74 | # Skip test if curl is not available 75 | if not shutil.which("curl"): 76 | self.skipTest("curl is not available on PATH") 77 | # Allow all commands and flags 78 | os.environ["ALLOWED_COMMANDS"] = "all" 79 | os.environ["ALLOWED_FLAGS"] = "all" 80 | # Reload server to pick up new settings 81 | import cli_mcp_server.server as server_module 82 | 83 | self.server = importlib.reload(server_module) 84 | result = asyncio.run( 85 | self.server.handle_call_tool( 86 | "run_command", {"command": "curl -sG ifconfig.me"} 87 | ) 88 | ) 89 | texts = [tc.text for tc in result] 90 | # Debug print: show results in table form 91 | print_results_table("test_run_curl_ifconfig", result) 92 | output_texts = [t for t in texts if "return code" not in t] 93 | self.assertTrue( 94 | any(t.strip() for t in output_texts), f"No IP address retrieved: {texts}" 95 | ) 96 | self.assertTrue(any("return code: 0" in text for text in texts)) 97 | 98 | def test_shell_operator_disallowed(self): 99 | # Ensure shell operators are disabled by default 100 | result = asyncio.run( 101 | self.server.handle_call_tool("run_command", {"command": "echo 1 && echo 2"}) 102 | ) 103 | texts = [tc.text for tc in result] 104 | print_results_table("test_shell_operator_disallowed", result) 105 | self.assertTrue( 106 | any("Security violation" in text for text in texts), 107 | f"Expected security violation for shell operators, got: {texts}", 108 | ) 109 | self.assertTrue( 110 | any("Shell operator '&&' is not supported" in text for text in texts), 111 | f"Expected '&&' not supported message, got: {texts}", 112 | ) 113 | 114 | def test_shell_operator_allowed_and_executes_commands(self): 115 | # Enable shell operators and allow all commands/flags 116 | os.environ["ALLOW_SHELL_OPERATORS"] = "true" 117 | os.environ["ALLOWED_COMMANDS"] = "all" 118 | os.environ["ALLOWED_FLAGS"] = "all" 119 | # Reload server to pick up new settings 120 | import cli_mcp_server.server as server_module 121 | 122 | server = importlib.reload(server_module) 123 | # Execute a compound command with '&&' 124 | result = asyncio.run( 125 | server.handle_call_tool("run_command", {"command": "echo 3 && echo 4"}) 126 | ) 127 | texts = [tc.text for tc in result] 128 | print_results_table("test_shell_operator_allowed", result) 129 | # The first element should contain the combined stdout from both commands 130 | self.assertEqual( 131 | texts[0].strip(), 132 | "3\n4", 133 | f"Unexpected combined output, got: {texts[0]!r}", 134 | ) 135 | self.assertTrue(any("return code: 0" in text for text in texts)) 136 | 137 | def test_shell_operator_semicolon(self): 138 | # Enable shell operators and allow all commands/flags 139 | os.environ["ALLOW_SHELL_OPERATORS"] = "true" 140 | os.environ["ALLOWED_COMMANDS"] = "all" 141 | os.environ["ALLOWED_FLAGS"] = "all" 142 | # Reload server to pick up new settings 143 | import cli_mcp_server.server as server_module 144 | 145 | server = importlib.reload(server_module) 146 | # Execute a compound command with ';' 147 | result = asyncio.run( 148 | server.handle_call_tool("run_command", {"command": "echo 5; echo 6"}) 149 | ) 150 | texts = [tc.text for tc in result] 151 | print_results_table("test_shell_operator_semicolon", result) 152 | self.assertEqual( 153 | texts[0].strip(), 154 | "5\n6", 155 | f"Unexpected combined output, got: {texts[0]!r}", 156 | ) 157 | self.assertTrue(any("return code: 0" in text for text in texts)) 158 | 159 | def test_shell_operator_append_redirection(self): 160 | # Enable shell operators and allow all commands/flags 161 | os.environ["ALLOW_SHELL_OPERATORS"] = "true" 162 | os.environ["ALLOWED_COMMANDS"] = "all" 163 | os.environ["ALLOWED_FLAGS"] = "all" 164 | # Reload server to pick up new settings 165 | import cli_mcp_server.server as server_module 166 | 167 | server = importlib.reload(server_module) 168 | # Create an output file and append text using '>>' 169 | file_name = "append.txt" 170 | file_path = os.path.join(self.tempdir.name, file_name) 171 | # Ensure the file exists 172 | open(file_path, "w").close() 173 | result = asyncio.run( 174 | server.handle_call_tool("run_command", {"command": f"echo hello >> {file_name}"}) 175 | ) 176 | texts = [tc.text for tc in result] 177 | print_results_table("test_shell_operator_append_redirection", result) 178 | # After redirection, file should contain 'hello' 179 | with open(file_path, "r") as f: 180 | content = f.read().strip() 181 | self.assertEqual(content, "hello", f"Unexpected file content: {content!r}") 182 | self.assertTrue(any("return code: 0" in text for text in texts)) 183 | 184 | def test_shell_operator_pipe(self): 185 | # Enable shell operators and allow all commands/flags 186 | os.environ["ALLOW_SHELL_OPERATORS"] = "true" 187 | os.environ["ALLOWED_COMMANDS"] = "all" 188 | os.environ["ALLOWED_FLAGS"] = "all" 189 | # Reload server to pick up new settings 190 | import cli_mcp_server.server as server_module 191 | 192 | server = importlib.reload(server_module) 193 | # Execute a simple pipeline to filter output 194 | result = asyncio.run( 195 | server.handle_call_tool("run_command", {"command": "echo 123 | grep 123"}) 196 | ) 197 | texts = [tc.text for tc in result] 198 | print_results_table("test_shell_operator_pipe", result) 199 | # The pipeline should output '123' 200 | self.assertEqual(texts[0].strip(), "123", f"Unexpected pipeline output: {texts[0]!r}") 201 | self.assertTrue(any("return code: 0" in text for text in texts)) 202 | 203 | def test_shell_operator_or(self): 204 | # Enable shell operators and allow all commands/flags 205 | os.environ["ALLOW_SHELL_OPERATORS"] = "true" 206 | os.environ["ALLOWED_COMMANDS"] = "all" 207 | os.environ["ALLOWED_FLAGS"] = "all" 208 | # Reload server to pick up new settings 209 | import cli_mcp_server.server as server_module 210 | 211 | server = importlib.reload(server_module) 212 | # Use '||' to fallback on failure 213 | result = asyncio.run( 214 | server.handle_call_tool("run_command", {"command": "false || echo OR_OK"}) 215 | ) 216 | texts = [tc.text for tc in result] 217 | print_results_table("test_shell_operator_or", result) 218 | # The OR operation should output 'OR_OK' 219 | self.assertEqual(texts[0].strip(), "OR_OK", f"Unexpected OR output: {texts[0]!r}") 220 | self.assertTrue(any("return code: 0" in text for text in texts)) 221 | 222 | 223 | if __name__ == "__main__": 224 | unittest.main() 225 | -------------------------------------------------------------------------------- /src/cli_mcp_server/server.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | import shlex 4 | import subprocess 5 | from dataclasses import dataclass 6 | from typing import List, Dict, Any, Optional 7 | 8 | import mcp.server.stdio 9 | import mcp.types as types 10 | from mcp.server import NotificationOptions, Server 11 | from mcp.server.models import InitializationOptions 12 | 13 | server = Server("cli-mcp-server") 14 | 15 | 16 | class CommandError(Exception): 17 | """Base exception for command-related errors""" 18 | 19 | pass 20 | 21 | 22 | class CommandSecurityError(CommandError): 23 | """Security violation errors""" 24 | 25 | pass 26 | 27 | 28 | class CommandExecutionError(CommandError): 29 | """Command execution errors""" 30 | 31 | pass 32 | 33 | 34 | class CommandTimeoutError(CommandError): 35 | """Command timeout errors""" 36 | 37 | pass 38 | 39 | 40 | @dataclass 41 | class SecurityConfig: 42 | """ 43 | Security configuration for command execution 44 | """ 45 | 46 | allowed_commands: set[str] 47 | allowed_flags: set[str] 48 | max_command_length: int 49 | command_timeout: int 50 | allow_all_commands: bool = False 51 | allow_all_flags: bool = False 52 | allow_shell_operators: bool = False 53 | 54 | 55 | class CommandExecutor: 56 | def __init__(self, allowed_dir: str, security_config: SecurityConfig): 57 | if not allowed_dir or not os.path.exists(allowed_dir): 58 | raise ValueError("Valid ALLOWED_DIR is required") 59 | self.allowed_dir = os.path.abspath(os.path.realpath(allowed_dir)) 60 | self.security_config = security_config 61 | 62 | def _normalize_path(self, path: str) -> str: 63 | """ 64 | Normalizes a path and ensures it's within allowed directory. 65 | """ 66 | try: 67 | if os.path.isabs(path): 68 | # If absolute path, check directly 69 | real_path = os.path.abspath(os.path.realpath(path)) 70 | else: 71 | # If relative path, combine with allowed_dir first 72 | real_path = os.path.abspath( 73 | os.path.realpath(os.path.join(self.allowed_dir, path)) 74 | ) 75 | 76 | if not self._is_path_safe(real_path): 77 | raise CommandSecurityError( 78 | f"Path '{path}' is outside of allowed directory: {self.allowed_dir}" 79 | ) 80 | 81 | return real_path 82 | except CommandSecurityError: 83 | raise 84 | except Exception as e: 85 | raise CommandSecurityError(f"Invalid path '{path}': {str(e)}") 86 | 87 | def validate_command(self, command_string: str) -> tuple[str, List[str]]: 88 | """ 89 | Validates and parses a command string for security and formatting. 90 | 91 | Checks if the command string contains shell operators. If it does, splits the command 92 | by operators and validates each part individually. If all parts are valid, returns 93 | the original command string to be executed with shell=True. 94 | 95 | For commands without shell operators, splits into command and arguments and validates 96 | each part according to security rules. 97 | 98 | Args: 99 | command_string (str): The command string to validate and parse. 100 | 101 | Returns: 102 | tuple[str, List[str]]: A tuple containing: 103 | - For regular commands: The command name (str) and list of arguments (List[str]) 104 | - For commands with shell operators: The full command string and empty args list 105 | 106 | Raises: 107 | CommandSecurityError: If any part of the command fails security validation. 108 | """ 109 | 110 | # Define shell operators 111 | shell_operators = ["&&", "||", "|", ">", ">>", "<", "<<", ";"] 112 | 113 | # Check if command contains shell operators 114 | contains_shell_operator = any( 115 | operator in command_string for operator in shell_operators 116 | ) 117 | 118 | if contains_shell_operator: 119 | # Check if shell operators are allowed 120 | if not self.security_config.allow_shell_operators: 121 | # If shell operators are not allowed, raise an error 122 | for operator in shell_operators: 123 | if operator in command_string: 124 | raise CommandSecurityError( 125 | f"Shell operator '{operator}' is not supported. Set ALLOW_SHELL_OPERATORS=true to enable." 126 | ) 127 | 128 | # Split the command by shell operators and validate each part 129 | return self._validate_command_with_operators( 130 | command_string, shell_operators 131 | ) 132 | 133 | # Process single command without shell operators 134 | return self._validate_single_command(command_string) 135 | 136 | def _is_url_path(self, path: str) -> bool: 137 | """ 138 | Checks if a given path is a URL of type http or https. 139 | 140 | Args: 141 | path (str): The path to check. 142 | 143 | Returns: 144 | bool: True if the path is a URL, False otherwise. 145 | """ 146 | url_pattern = re.compile(r"^https?://") 147 | return bool(url_pattern.match(path)) 148 | 149 | def _is_path_safe(self, path: str) -> bool: 150 | """ 151 | Checks if a given path is safe to access within allowed directory boundaries. 152 | 153 | Validates that the absolute resolved path is within the allowed directory 154 | to prevent directory traversal attacks. 155 | 156 | Args: 157 | path (str): The path to validate. 158 | 159 | Returns: 160 | bool: True if path is within allowed directory, False otherwise. 161 | Returns False if path resolution fails for any reason. 162 | 163 | Private method intended for internal use only. 164 | """ 165 | try: 166 | # Resolve any symlinks and get absolute path 167 | real_path = os.path.abspath(os.path.realpath(path)) 168 | allowed_dir_real = os.path.abspath(os.path.realpath(self.allowed_dir)) 169 | 170 | # Check if the path starts with allowed_dir 171 | return real_path.startswith(allowed_dir_real) 172 | except Exception: 173 | return False 174 | 175 | def _validate_single_command(self, command_string: str) -> tuple[str, List[str]]: 176 | """ 177 | Validates a single command without shell operators. 178 | 179 | Args: 180 | command_string (str): The command string to validate. 181 | 182 | Returns: 183 | tuple[str, List[str]]: A tuple containing the command and validated arguments. 184 | 185 | Raises: 186 | CommandSecurityError: If the command fails validation. 187 | """ 188 | try: 189 | parts = shlex.split(command_string) 190 | if not parts: 191 | raise CommandSecurityError("Empty command") 192 | 193 | command, args = parts[0], parts[1:] 194 | 195 | # Validate command if not in allow-all mode 196 | if ( 197 | not self.security_config.allow_all_commands 198 | and command not in self.security_config.allowed_commands 199 | ): 200 | raise CommandSecurityError(f"Command '{command}' is not allowed") 201 | 202 | # Process and validate arguments 203 | validated_args = [] 204 | for arg in args: 205 | is_explicit_path = (arg.startswith(("./", "../", "/")) and not arg.startswith("//")) or arg == "." 206 | 207 | if arg.startswith("-"): 208 | if ( 209 | not self.security_config.allow_all_flags 210 | and arg not in self.security_config.allowed_flags 211 | ): 212 | raise CommandSecurityError(f"Flag '{arg}' is not allowed") 213 | validated_args.append(arg) 214 | continue 215 | # For any path-like argument, validate it 216 | if is_explicit_path or ("/" in arg and os.path.exists(os.path.join(self.allowed_dir, arg))): 217 | if self._is_url_path(arg): 218 | # If it's a URL, we don't need to normalize it 219 | validated_args.append(arg) 220 | continue 221 | 222 | normalized_path = self._normalize_path(arg) 223 | validated_args.append(normalized_path) 224 | else: 225 | # For non-path arguments, add them as-is 226 | validated_args.append(arg) 227 | 228 | return command, validated_args 229 | 230 | except ValueError as e: 231 | raise CommandSecurityError(f"Invalid command format: {str(e)}") 232 | 233 | def _validate_command_with_operators( 234 | self, command_string: str, shell_operators: List[str] 235 | ) -> tuple[str, List[str]]: 236 | """ 237 | Validates a command string that contains shell operators. 238 | 239 | Splits the command string by shell operators and validates each part individually. 240 | If all parts are valid, returns the original command to be executed with shell=True. 241 | 242 | Args: 243 | command_string (str): The command string containing shell operators. 244 | shell_operators (List[str]): List of shell operators to split by. 245 | 246 | Returns: 247 | tuple[str, List[str]]: A tuple containing the command and empty args list 248 | (since the command will be executed with shell=True) 249 | 250 | Raises: 251 | CommandSecurityError: If any part of the command fails validation. 252 | """ 253 | # Create a regex pattern to split by any of the shell operators 254 | # We need to escape special regex characters in the operators 255 | escaped_operators = [re.escape(op) for op in shell_operators] 256 | pattern = "|".join(escaped_operators) 257 | 258 | # Split the command string by shell operators, keeping the operators 259 | parts = re.split(f"({pattern})", command_string) 260 | 261 | # Filter out empty parts and whitespace-only parts 262 | parts = [part.strip() for part in parts if part.strip()] 263 | 264 | # Group commands and operators 265 | commands = [] 266 | i = 0 267 | while i < len(parts): 268 | if i + 1 < len(parts) and parts[i + 1] in shell_operators: 269 | # If next part is an operator, current part is a command 270 | if parts[i]: # Skip empty commands 271 | commands.append(parts[i]) 272 | i += 2 # Skip the operator 273 | else: 274 | # If no operator follows, this is the last command 275 | if ( 276 | parts[i] and parts[i] not in shell_operators 277 | ): # Skip if it's an operator 278 | commands.append(parts[i]) 279 | i += 1 280 | 281 | # Validate each command individually 282 | for cmd in commands: 283 | try: 284 | # Use the extracted validation method for each command 285 | self._validate_single_command(cmd) 286 | except CommandSecurityError as e: 287 | raise CommandSecurityError(f"Invalid command part '{cmd}': {str(e)}") 288 | except ValueError as e: 289 | raise CommandSecurityError( 290 | f"Invalid command format in '{cmd}': {str(e)}" 291 | ) 292 | 293 | # If we get here, all commands passed validation 294 | # Return the original command string to be executed with shell=True 295 | return command_string, [] 296 | 297 | def execute(self, command_string: str) -> subprocess.CompletedProcess: 298 | """ 299 | Executes a command string in a secure, controlled environment. 300 | 301 | Runs the command after validating it against security constraints including length limits 302 | and shell operator restrictions. Executes with controlled parameters for safety. 303 | 304 | Args: 305 | command_string (str): The command string to execute. 306 | 307 | Returns: 308 | subprocess.CompletedProcess: The result of the command execution containing 309 | stdout, stderr, and return code. 310 | 311 | Raises: 312 | CommandSecurityError: If the command: 313 | - Exceeds maximum length 314 | - Fails security validation 315 | - Fails during execution 316 | 317 | Notes: 318 | - Uses shell=True for commands with shell operators, shell=False otherwise 319 | - Uses timeout and working directory constraints 320 | - Captures both stdout and stderr 321 | """ 322 | if len(command_string) > self.security_config.max_command_length: 323 | raise CommandSecurityError( 324 | f"Command exceeds maximum length of {self.security_config.max_command_length}" 325 | ) 326 | 327 | try: 328 | command, args = self.validate_command(command_string) 329 | 330 | # Check if this is a command with shell operators 331 | shell_operators = ["&&", "||", "|", ">", ">>", "<", "<<", ";"] 332 | use_shell = any(operator in command_string for operator in shell_operators) 333 | 334 | # Double-check that shell operators are allowed if they are present 335 | if use_shell and not self.security_config.allow_shell_operators: 336 | for operator in shell_operators: 337 | if operator in command_string: 338 | raise CommandSecurityError( 339 | f"Shell operator '{operator}' is not supported. Set ALLOW_SHELL_OPERATORS=true to enable." 340 | ) 341 | 342 | if use_shell: 343 | # For commands with shell operators, execute with shell=True 344 | return subprocess.run( 345 | command, # command is the full command string in this case 346 | shell=True, 347 | text=True, 348 | capture_output=True, 349 | timeout=self.security_config.command_timeout, 350 | cwd=self.allowed_dir, 351 | ) 352 | else: 353 | # For regular commands, execute with shell=False 354 | return subprocess.run( 355 | [command] + args, 356 | shell=False, 357 | text=True, 358 | capture_output=True, 359 | timeout=self.security_config.command_timeout, 360 | cwd=self.allowed_dir, 361 | ) 362 | except subprocess.TimeoutExpired: 363 | raise CommandTimeoutError( 364 | f"Command timed out after {self.security_config.command_timeout} seconds" 365 | ) 366 | except CommandError: 367 | raise 368 | except Exception as e: 369 | raise CommandExecutionError(f"Command execution failed: {str(e)}") 370 | 371 | 372 | # Load security configuration from environment 373 | def load_security_config() -> SecurityConfig: 374 | """ 375 | Loads security configuration from environment variables with default fallbacks. 376 | 377 | Creates a SecurityConfig instance using environment variables to configure allowed 378 | commands, flags, patterns, and execution constraints. Uses predefined defaults if 379 | environment variables are not set. 380 | 381 | Returns: 382 | SecurityConfig: Configuration object containing: 383 | - allowed_commands: Set of permitted command names 384 | - allowed_flags: Set of permitted command flags/options 385 | - max_command_length: Maximum length of command string 386 | - command_timeout: Maximum execution time in seconds 387 | - allow_all_commands: Whether all commands are allowed 388 | - allow_all_flags: Whether all flags are allowed 389 | - allow_shell_operators: Whether shell operators (&&, ||, |, etc.) are allowed 390 | 391 | Environment Variables: 392 | ALLOWED_COMMANDS: Comma-separated list of allowed commands or 'all' (default: "ls,cat,pwd") 393 | ALLOWED_FLAGS: Comma-separated list of allowed flags or 'all' (default: "-l,-a,--help") 394 | MAX_COMMAND_LENGTH: Maximum command string length (default: 1024) 395 | COMMAND_TIMEOUT: Command timeout in seconds (default: 30) 396 | ALLOW_SHELL_OPERATORS: Whether to allow shell operators like &&, ||, |, >, etc. (default: false) 397 | Set to "true" or "1" to enable, any other value to disable. 398 | """ 399 | allowed_commands = os.getenv("ALLOWED_COMMANDS", "ls,cat,pwd") 400 | allowed_flags = os.getenv("ALLOWED_FLAGS", "-l,-a,--help") 401 | allow_shell_operators_env = os.getenv("ALLOW_SHELL_OPERATORS", "false") 402 | 403 | allow_all_commands = allowed_commands.lower() == "all" 404 | allow_all_flags = allowed_flags.lower() == "all" 405 | allow_shell_operators = allow_shell_operators_env.lower() in ("true", "1") 406 | 407 | return SecurityConfig( 408 | allowed_commands=( 409 | set() if allow_all_commands else set(allowed_commands.split(",")) 410 | ), 411 | allowed_flags=set() if allow_all_flags else set(allowed_flags.split(",")), 412 | max_command_length=int(os.getenv("MAX_COMMAND_LENGTH", "1024")), 413 | command_timeout=int(os.getenv("COMMAND_TIMEOUT", "30")), 414 | allow_all_commands=allow_all_commands, 415 | allow_all_flags=allow_all_flags, 416 | allow_shell_operators=allow_shell_operators, 417 | ) 418 | 419 | 420 | executor = CommandExecutor( 421 | allowed_dir=os.getenv("ALLOWED_DIR", ""), security_config=load_security_config() 422 | ) 423 | 424 | 425 | @server.list_tools() 426 | async def handle_list_tools() -> list[types.Tool]: 427 | commands_desc = ( 428 | "all commands" 429 | if executor.security_config.allow_all_commands 430 | else ", ".join(executor.security_config.allowed_commands) 431 | ) 432 | flags_desc = ( 433 | "all flags" 434 | if executor.security_config.allow_all_flags 435 | else ", ".join(executor.security_config.allowed_flags) 436 | ) 437 | 438 | return [ 439 | types.Tool( 440 | name="run_command", 441 | description=( 442 | f"Allows command (CLI) execution in the directory: {executor.allowed_dir}\n\n" 443 | f"Available commands: {commands_desc}\n" 444 | f"Available flags: {flags_desc}\n\n" 445 | f"Shell operators (&&, ||, |, >, >>, <, <<, ;) are {'supported' if executor.security_config.allow_shell_operators else 'not supported'}. Set ALLOW_SHELL_OPERATORS=true to enable." 446 | ), 447 | inputSchema={ 448 | "type": "object", 449 | "properties": { 450 | "command": { 451 | "type": "string", 452 | "description": "Single command to execute (example: 'ls -l' or 'cat file.txt')", 453 | } 454 | }, 455 | "required": ["command"], 456 | }, 457 | ), 458 | types.Tool( 459 | name="show_security_rules", 460 | description=( 461 | "Show what commands and operations are allowed in this environment.\n" 462 | ), 463 | inputSchema={ 464 | "type": "object", 465 | "properties": {}, 466 | }, 467 | ), 468 | ] 469 | 470 | 471 | @server.call_tool() 472 | async def handle_call_tool( 473 | name: str, arguments: Optional[Dict[str, Any]] 474 | ) -> List[types.TextContent]: 475 | if name == "run_command": 476 | if not arguments or "command" not in arguments: 477 | return [ 478 | types.TextContent(type="text", text="No command provided", error=True) 479 | ] 480 | 481 | try: 482 | result = executor.execute(arguments["command"]) 483 | 484 | response = [] 485 | if result.stdout: 486 | response.append(types.TextContent(type="text", text=result.stdout)) 487 | if result.stderr: 488 | response.append( 489 | types.TextContent(type="text", text=result.stderr, error=True) 490 | ) 491 | 492 | response.append( 493 | types.TextContent( 494 | type="text", 495 | text=f"\nCommand completed with return code: {result.returncode}", 496 | ) 497 | ) 498 | 499 | return response 500 | 501 | except CommandSecurityError as e: 502 | return [ 503 | types.TextContent( 504 | type="text", text=f"Security violation: {str(e)}", error=True 505 | ) 506 | ] 507 | except subprocess.TimeoutExpired: 508 | return [ 509 | types.TextContent( 510 | type="text", 511 | text=f"Command timed out after {executor.security_config.command_timeout} seconds", 512 | error=True, 513 | ) 514 | ] 515 | except Exception as e: 516 | return [types.TextContent(type="text", text=f"Error: {str(e)}", error=True)] 517 | 518 | elif name == "show_security_rules": 519 | commands_desc = ( 520 | "All commands allowed" 521 | if executor.security_config.allow_all_commands 522 | else ", ".join(sorted(executor.security_config.allowed_commands)) 523 | ) 524 | flags_desc = ( 525 | "All flags allowed" 526 | if executor.security_config.allow_all_flags 527 | else ", ".join(sorted(executor.security_config.allowed_flags)) 528 | ) 529 | 530 | security_info = ( 531 | "Security Configuration:\n" 532 | f"==================\n" 533 | f"Working Directory: {executor.allowed_dir}\n" 534 | f"\nAllowed Commands:\n" 535 | f"----------------\n" 536 | f"{commands_desc}\n" 537 | f"\nAllowed Flags:\n" 538 | f"-------------\n" 539 | f"{flags_desc}\n" 540 | f"\nSecurity Limits:\n" 541 | f"---------------\n" 542 | f"Max Command Length: {executor.security_config.max_command_length} characters\n" 543 | f"Command Timeout: {executor.security_config.command_timeout} seconds\n" 544 | ) 545 | return [types.TextContent(type="text", text=security_info)] 546 | 547 | raise ValueError(f"Unknown tool: {name}") 548 | 549 | 550 | async def main(): 551 | async with mcp.server.stdio.stdio_server() as (read_stream, write_stream): 552 | await server.run( 553 | read_stream, 554 | write_stream, 555 | InitializationOptions( 556 | server_name="cli-mcp-server", 557 | server_version="0.2.1", 558 | capabilities=server.get_capabilities( 559 | notification_options=NotificationOptions(), 560 | experimental_capabilities={}, 561 | ), 562 | ), 563 | ) 564 | -------------------------------------------------------------------------------- /uv.lock: -------------------------------------------------------------------------------- 1 | version = 1 2 | revision = 1 3 | requires-python = ">=3.10" 4 | 5 | [[package]] 6 | name = "annotated-types" 7 | version = "0.7.0" 8 | source = { registry = "https://pypi.org/simple" } 9 | sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081 } 10 | wheels = [ 11 | { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643 }, 12 | ] 13 | 14 | [[package]] 15 | name = "anyio" 16 | version = "4.9.0" 17 | source = { registry = "https://pypi.org/simple" } 18 | dependencies = [ 19 | { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, 20 | { name = "idna" }, 21 | { name = "sniffio" }, 22 | { name = "typing-extensions", marker = "python_full_version < '3.13'" }, 23 | ] 24 | sdist = { url = "https://files.pythonhosted.org/packages/95/7d/4c1bd541d4dffa1b52bd83fb8527089e097a106fc90b467a7313b105f840/anyio-4.9.0.tar.gz", hash = "sha256:673c0c244e15788651a4ff38710fea9675823028a6f08a5eda409e0c9840a028", size = 190949 } 25 | wheels = [ 26 | { url = "https://files.pythonhosted.org/packages/a1/ee/48ca1a7c89ffec8b6a0c5d02b89c305671d5ffd8d3c94acf8b8c408575bb/anyio-4.9.0-py3-none-any.whl", hash = "sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c", size = 100916 }, 27 | ] 28 | 29 | [[package]] 30 | name = "attrs" 31 | version = "25.3.0" 32 | source = { registry = "https://pypi.org/simple" } 33 | sdist = { url = "https://files.pythonhosted.org/packages/5a/b0/1367933a8532ee6ff8d63537de4f1177af4bff9f3e829baf7331f595bb24/attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b", size = 812032 } 34 | wheels = [ 35 | { url = "https://files.pythonhosted.org/packages/77/06/bb80f5f86020c4551da315d78b3ab75e8228f89f0162f2c3a819e407941a/attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3", size = 63815 }, 36 | ] 37 | 38 | [[package]] 39 | name = "certifi" 40 | version = "2025.1.31" 41 | source = { registry = "https://pypi.org/simple" } 42 | sdist = { url = "https://files.pythonhosted.org/packages/1c/ab/c9f1e32b7b1bf505bf26f0ef697775960db7932abeb7b516de930ba2705f/certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651", size = 167577 } 43 | wheels = [ 44 | { url = "https://files.pythonhosted.org/packages/38/fc/bce832fd4fd99766c04d1ee0eead6b0ec6486fb100ae5e74c1d91292b982/certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe", size = 166393 }, 45 | ] 46 | 47 | [[package]] 48 | name = "cli-mcp-server" 49 | version = "0.2.5" 50 | source = { editable = "." } 51 | dependencies = [ 52 | { name = "mcp" }, 53 | ] 54 | 55 | [package.metadata] 56 | requires-dist = [{ name = "mcp", specifier = ">=1.10.1" }] 57 | 58 | [[package]] 59 | name = "click" 60 | version = "8.1.8" 61 | source = { registry = "https://pypi.org/simple" } 62 | dependencies = [ 63 | { name = "colorama", marker = "sys_platform == 'win32'" }, 64 | ] 65 | sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593 } 66 | wheels = [ 67 | { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188 }, 68 | ] 69 | 70 | [[package]] 71 | name = "colorama" 72 | version = "0.4.6" 73 | source = { registry = "https://pypi.org/simple" } 74 | sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } 75 | wheels = [ 76 | { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, 77 | ] 78 | 79 | [[package]] 80 | name = "exceptiongroup" 81 | version = "1.2.2" 82 | source = { registry = "https://pypi.org/simple" } 83 | sdist = { url = "https://files.pythonhosted.org/packages/09/35/2495c4ac46b980e4ca1f6ad6db102322ef3ad2410b79fdde159a4b0f3b92/exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc", size = 28883 } 84 | wheels = [ 85 | { url = "https://files.pythonhosted.org/packages/02/cc/b7e31358aac6ed1ef2bb790a9746ac2c69bcb3c8588b41616914eb106eaf/exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b", size = 16453 }, 86 | ] 87 | 88 | [[package]] 89 | name = "h11" 90 | version = "0.14.0" 91 | source = { registry = "https://pypi.org/simple" } 92 | sdist = { url = "https://files.pythonhosted.org/packages/f5/38/3af3d3633a34a3316095b39c8e8fb4853a28a536e55d347bd8d8e9a14b03/h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d", size = 100418 } 93 | wheels = [ 94 | { url = "https://files.pythonhosted.org/packages/95/04/ff642e65ad6b90db43e668d70ffb6736436c7ce41fcc549f4e9472234127/h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761", size = 58259 }, 95 | ] 96 | 97 | [[package]] 98 | name = "httpcore" 99 | version = "1.0.8" 100 | source = { registry = "https://pypi.org/simple" } 101 | dependencies = [ 102 | { name = "certifi" }, 103 | { name = "h11" }, 104 | ] 105 | sdist = { url = "https://files.pythonhosted.org/packages/9f/45/ad3e1b4d448f22c0cff4f5692f5ed0666658578e358b8d58a19846048059/httpcore-1.0.8.tar.gz", hash = "sha256:86e94505ed24ea06514883fd44d2bc02d90e77e7979c8eb71b90f41d364a1bad", size = 85385 } 106 | wheels = [ 107 | { url = "https://files.pythonhosted.org/packages/18/8d/f052b1e336bb2c1fc7ed1aaed898aa570c0b61a09707b108979d9fc6e308/httpcore-1.0.8-py3-none-any.whl", hash = "sha256:5254cf149bcb5f75e9d1b2b9f729ea4a4b883d1ad7379fc632b727cec23674be", size = 78732 }, 108 | ] 109 | 110 | [[package]] 111 | name = "httpx" 112 | version = "0.28.1" 113 | source = { registry = "https://pypi.org/simple" } 114 | dependencies = [ 115 | { name = "anyio" }, 116 | { name = "certifi" }, 117 | { name = "httpcore" }, 118 | { name = "idna" }, 119 | ] 120 | sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406 } 121 | wheels = [ 122 | { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517 }, 123 | ] 124 | 125 | [[package]] 126 | name = "httpx-sse" 127 | version = "0.4.0" 128 | source = { registry = "https://pypi.org/simple" } 129 | sdist = { url = "https://files.pythonhosted.org/packages/4c/60/8f4281fa9bbf3c8034fd54c0e7412e66edbab6bc74c4996bd616f8d0406e/httpx-sse-0.4.0.tar.gz", hash = "sha256:1e81a3a3070ce322add1d3529ed42eb5f70817f45ed6ec915ab753f961139721", size = 12624 } 130 | wheels = [ 131 | { url = "https://files.pythonhosted.org/packages/e1/9b/a181f281f65d776426002f330c31849b86b31fc9d848db62e16f03ff739f/httpx_sse-0.4.0-py3-none-any.whl", hash = "sha256:f329af6eae57eaa2bdfd962b42524764af68075ea87370a2de920af5341e318f", size = 7819 }, 132 | ] 133 | 134 | [[package]] 135 | name = "idna" 136 | version = "3.10" 137 | source = { registry = "https://pypi.org/simple" } 138 | sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 } 139 | wheels = [ 140 | { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 }, 141 | ] 142 | 143 | [[package]] 144 | name = "jsonschema" 145 | version = "4.24.0" 146 | source = { registry = "https://pypi.org/simple" } 147 | dependencies = [ 148 | { name = "attrs" }, 149 | { name = "jsonschema-specifications" }, 150 | { name = "referencing" }, 151 | { name = "rpds-py" }, 152 | ] 153 | sdist = { url = "https://files.pythonhosted.org/packages/bf/d3/1cf5326b923a53515d8f3a2cd442e6d7e94fcc444716e879ea70a0ce3177/jsonschema-4.24.0.tar.gz", hash = "sha256:0b4e8069eb12aedfa881333004bccaec24ecef5a8a6a4b6df142b2cc9599d196", size = 353480 } 154 | wheels = [ 155 | { url = "https://files.pythonhosted.org/packages/a2/3d/023389198f69c722d039351050738d6755376c8fd343e91dc493ea485905/jsonschema-4.24.0-py3-none-any.whl", hash = "sha256:a462455f19f5faf404a7902952b6f0e3ce868f3ee09a359b05eca6673bd8412d", size = 88709 }, 156 | ] 157 | 158 | [[package]] 159 | name = "jsonschema-specifications" 160 | version = "2025.4.1" 161 | source = { registry = "https://pypi.org/simple" } 162 | dependencies = [ 163 | { name = "referencing" }, 164 | ] 165 | sdist = { url = "https://files.pythonhosted.org/packages/bf/ce/46fbd9c8119cfc3581ee5643ea49464d168028cfb5caff5fc0596d0cf914/jsonschema_specifications-2025.4.1.tar.gz", hash = "sha256:630159c9f4dbea161a6a2205c3011cc4f18ff381b189fff48bb39b9bf26ae608", size = 15513 } 166 | wheels = [ 167 | { url = "https://files.pythonhosted.org/packages/01/0e/b27cdbaccf30b890c40ed1da9fd4a3593a5cf94dae54fb34f8a4b74fcd3f/jsonschema_specifications-2025.4.1-py3-none-any.whl", hash = "sha256:4653bffbd6584f7de83a67e0d620ef16900b390ddc7939d56684d6c81e33f1af", size = 18437 }, 168 | ] 169 | 170 | [[package]] 171 | name = "mcp" 172 | version = "1.10.1" 173 | source = { registry = "https://pypi.org/simple" } 174 | dependencies = [ 175 | { name = "anyio" }, 176 | { name = "httpx" }, 177 | { name = "httpx-sse" }, 178 | { name = "jsonschema" }, 179 | { name = "pydantic" }, 180 | { name = "pydantic-settings" }, 181 | { name = "python-multipart" }, 182 | { name = "sse-starlette" }, 183 | { name = "starlette" }, 184 | { name = "uvicorn", marker = "sys_platform != 'emscripten'" }, 185 | ] 186 | sdist = { url = "https://files.pythonhosted.org/packages/7c/68/63045305f29ff680a9cd5be360c755270109e6b76f696ea6824547ddbc30/mcp-1.10.1.tar.gz", hash = "sha256:aaa0957d8307feeff180da2d9d359f2b801f35c0c67f1882136239055ef034c2", size = 392969 } 187 | wheels = [ 188 | { url = "https://files.pythonhosted.org/packages/d7/3f/435a5b3d10ae242a9d6c2b33175551173c3c61fe637dc893be05c4ed0aaf/mcp-1.10.1-py3-none-any.whl", hash = "sha256:4d08301aefe906dce0fa482289db55ce1db831e3e67212e65b5e23ad8454b3c5", size = 150878 }, 189 | ] 190 | 191 | [[package]] 192 | name = "pydantic" 193 | version = "2.11.3" 194 | source = { registry = "https://pypi.org/simple" } 195 | dependencies = [ 196 | { name = "annotated-types" }, 197 | { name = "pydantic-core" }, 198 | { name = "typing-extensions" }, 199 | { name = "typing-inspection" }, 200 | ] 201 | sdist = { url = "https://files.pythonhosted.org/packages/10/2e/ca897f093ee6c5f3b0bee123ee4465c50e75431c3d5b6a3b44a47134e891/pydantic-2.11.3.tar.gz", hash = "sha256:7471657138c16adad9322fe3070c0116dd6c3ad8d649300e3cbdfe91f4db4ec3", size = 785513 } 202 | wheels = [ 203 | { url = "https://files.pythonhosted.org/packages/b0/1d/407b29780a289868ed696d1616f4aad49d6388e5a77f567dcd2629dcd7b8/pydantic-2.11.3-py3-none-any.whl", hash = "sha256:a082753436a07f9ba1289c6ffa01cd93db3548776088aa917cc43b63f68fa60f", size = 443591 }, 204 | ] 205 | 206 | [[package]] 207 | name = "pydantic-core" 208 | version = "2.33.1" 209 | source = { registry = "https://pypi.org/simple" } 210 | dependencies = [ 211 | { name = "typing-extensions" }, 212 | ] 213 | sdist = { url = "https://files.pythonhosted.org/packages/17/19/ed6a078a5287aea7922de6841ef4c06157931622c89c2a47940837b5eecd/pydantic_core-2.33.1.tar.gz", hash = "sha256:bcc9c6fdb0ced789245b02b7d6603e17d1563064ddcfc36f046b61c0c05dd9df", size = 434395 } 214 | wheels = [ 215 | { url = "https://files.pythonhosted.org/packages/38/ea/5f572806ab4d4223d11551af814d243b0e3e02cc6913def4d1fe4a5ca41c/pydantic_core-2.33.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3077cfdb6125cc8dab61b155fdd714663e401f0e6883f9632118ec12cf42df26", size = 2044021 }, 216 | { url = "https://files.pythonhosted.org/packages/8c/d1/f86cc96d2aa80e3881140d16d12ef2b491223f90b28b9a911346c04ac359/pydantic_core-2.33.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8ffab8b2908d152e74862d276cf5017c81a2f3719f14e8e3e8d6b83fda863927", size = 1861742 }, 217 | { url = "https://files.pythonhosted.org/packages/37/08/fbd2cd1e9fc735a0df0142fac41c114ad9602d1c004aea340169ae90973b/pydantic_core-2.33.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5183e4f6a2d468787243ebcd70cf4098c247e60d73fb7d68d5bc1e1beaa0c4db", size = 1910414 }, 218 | { url = "https://files.pythonhosted.org/packages/7f/73/3ac217751decbf8d6cb9443cec9b9eb0130eeada6ae56403e11b486e277e/pydantic_core-2.33.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:398a38d323f37714023be1e0285765f0a27243a8b1506b7b7de87b647b517e48", size = 1996848 }, 219 | { url = "https://files.pythonhosted.org/packages/9a/f5/5c26b265cdcff2661e2520d2d1e9db72d117ea00eb41e00a76efe68cb009/pydantic_core-2.33.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:87d3776f0001b43acebfa86f8c64019c043b55cc5a6a2e313d728b5c95b46969", size = 2141055 }, 220 | { url = "https://files.pythonhosted.org/packages/5d/14/a9c3cee817ef2f8347c5ce0713e91867a0dceceefcb2973942855c917379/pydantic_core-2.33.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c566dd9c5f63d22226409553531f89de0cac55397f2ab8d97d6f06cfce6d947e", size = 2753806 }, 221 | { url = "https://files.pythonhosted.org/packages/f2/68/866ce83a51dd37e7c604ce0050ff6ad26de65a7799df89f4db87dd93d1d6/pydantic_core-2.33.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0d5f3acc81452c56895e90643a625302bd6be351e7010664151cc55b7b97f89", size = 2007777 }, 222 | { url = "https://files.pythonhosted.org/packages/b6/a8/36771f4404bb3e49bd6d4344da4dede0bf89cc1e01f3b723c47248a3761c/pydantic_core-2.33.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d3a07fadec2a13274a8d861d3d37c61e97a816beae717efccaa4b36dfcaadcde", size = 2122803 }, 223 | { url = "https://files.pythonhosted.org/packages/18/9c/730a09b2694aa89360d20756369822d98dc2f31b717c21df33b64ffd1f50/pydantic_core-2.33.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:f99aeda58dce827f76963ee87a0ebe75e648c72ff9ba1174a253f6744f518f65", size = 2086755 }, 224 | { url = "https://files.pythonhosted.org/packages/54/8e/2dccd89602b5ec31d1c58138d02340ecb2ebb8c2cac3cc66b65ce3edb6ce/pydantic_core-2.33.1-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:902dbc832141aa0ec374f4310f1e4e7febeebc3256f00dc359a9ac3f264a45dc", size = 2257358 }, 225 | { url = "https://files.pythonhosted.org/packages/d1/9c/126e4ac1bfad8a95a9837acdd0963695d69264179ba4ede8b8c40d741702/pydantic_core-2.33.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fe44d56aa0b00d66640aa84a3cbe80b7a3ccdc6f0b1ca71090696a6d4777c091", size = 2257916 }, 226 | { url = "https://files.pythonhosted.org/packages/7d/ba/91eea2047e681a6853c81c20aeca9dcdaa5402ccb7404a2097c2adf9d038/pydantic_core-2.33.1-cp310-cp310-win32.whl", hash = "sha256:ed3eb16d51257c763539bde21e011092f127a2202692afaeaccb50db55a31383", size = 1923823 }, 227 | { url = "https://files.pythonhosted.org/packages/94/c0/fcdf739bf60d836a38811476f6ecd50374880b01e3014318b6e809ddfd52/pydantic_core-2.33.1-cp310-cp310-win_amd64.whl", hash = "sha256:694ad99a7f6718c1a498dc170ca430687a39894a60327f548e02a9c7ee4b6504", size = 1952494 }, 228 | { url = "https://files.pythonhosted.org/packages/d6/7f/c6298830cb780c46b4f46bb24298d01019ffa4d21769f39b908cd14bbd50/pydantic_core-2.33.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:6e966fc3caaf9f1d96b349b0341c70c8d6573bf1bac7261f7b0ba88f96c56c24", size = 2044224 }, 229 | { url = "https://files.pythonhosted.org/packages/a8/65/6ab3a536776cad5343f625245bd38165d6663256ad43f3a200e5936afd6c/pydantic_core-2.33.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bfd0adeee563d59c598ceabddf2c92eec77abcb3f4a391b19aa7366170bd9e30", size = 1858845 }, 230 | { url = "https://files.pythonhosted.org/packages/e9/15/9a22fd26ba5ee8c669d4b8c9c244238e940cd5d818649603ca81d1c69861/pydantic_core-2.33.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91815221101ad3c6b507804178a7bb5cb7b2ead9ecd600041669c8d805ebd595", size = 1910029 }, 231 | { url = "https://files.pythonhosted.org/packages/d5/33/8cb1a62818974045086f55f604044bf35b9342900318f9a2a029a1bec460/pydantic_core-2.33.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9fea9c1869bb4742d174a57b4700c6dadea951df8b06de40c2fedb4f02931c2e", size = 1997784 }, 232 | { url = "https://files.pythonhosted.org/packages/c0/ca/49958e4df7715c71773e1ea5be1c74544923d10319173264e6db122543f9/pydantic_core-2.33.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d20eb4861329bb2484c021b9d9a977566ab16d84000a57e28061151c62b349a", size = 2141075 }, 233 | { url = "https://files.pythonhosted.org/packages/7b/a6/0b3a167a9773c79ba834b959b4e18c3ae9216b8319bd8422792abc8a41b1/pydantic_core-2.33.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb935c5591573ae3201640579f30128ccc10739b45663f93c06796854405505", size = 2745849 }, 234 | { url = "https://files.pythonhosted.org/packages/0b/60/516484135173aa9e5861d7a0663dce82e4746d2e7f803627d8c25dfa5578/pydantic_core-2.33.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c964fd24e6166420d18fb53996d8c9fd6eac9bf5ae3ec3d03015be4414ce497f", size = 2005794 }, 235 | { url = "https://files.pythonhosted.org/packages/86/70/05b1eb77459ad47de00cf78ee003016da0cedf8b9170260488d7c21e9181/pydantic_core-2.33.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:681d65e9011f7392db5aa002b7423cc442d6a673c635668c227c6c8d0e5a4f77", size = 2123237 }, 236 | { url = "https://files.pythonhosted.org/packages/c7/57/12667a1409c04ae7dc95d3b43158948eb0368e9c790be8b095cb60611459/pydantic_core-2.33.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e100c52f7355a48413e2999bfb4e139d2977a904495441b374f3d4fb4a170961", size = 2086351 }, 237 | { url = "https://files.pythonhosted.org/packages/57/61/cc6d1d1c1664b58fdd6ecc64c84366c34ec9b606aeb66cafab6f4088974c/pydantic_core-2.33.1-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:048831bd363490be79acdd3232f74a0e9951b11b2b4cc058aeb72b22fdc3abe1", size = 2258914 }, 238 | { url = "https://files.pythonhosted.org/packages/d1/0a/edb137176a1f5419b2ddee8bde6a0a548cfa3c74f657f63e56232df8de88/pydantic_core-2.33.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:bdc84017d28459c00db6f918a7272a5190bec3090058334e43a76afb279eac7c", size = 2257385 }, 239 | { url = "https://files.pythonhosted.org/packages/26/3c/48ca982d50e4b0e1d9954919c887bdc1c2b462801bf408613ccc641b3daa/pydantic_core-2.33.1-cp311-cp311-win32.whl", hash = "sha256:32cd11c5914d1179df70406427097c7dcde19fddf1418c787540f4b730289896", size = 1923765 }, 240 | { url = "https://files.pythonhosted.org/packages/33/cd/7ab70b99e5e21559f5de38a0928ea84e6f23fdef2b0d16a6feaf942b003c/pydantic_core-2.33.1-cp311-cp311-win_amd64.whl", hash = "sha256:2ea62419ba8c397e7da28a9170a16219d310d2cf4970dbc65c32faf20d828c83", size = 1950688 }, 241 | { url = "https://files.pythonhosted.org/packages/4b/ae/db1fc237b82e2cacd379f63e3335748ab88b5adde98bf7544a1b1bd10a84/pydantic_core-2.33.1-cp311-cp311-win_arm64.whl", hash = "sha256:fc903512177361e868bc1f5b80ac8c8a6e05fcdd574a5fb5ffeac5a9982b9e89", size = 1908185 }, 242 | { url = "https://files.pythonhosted.org/packages/c8/ce/3cb22b07c29938f97ff5f5bb27521f95e2ebec399b882392deb68d6c440e/pydantic_core-2.33.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:1293d7febb995e9d3ec3ea09caf1a26214eec45b0f29f6074abb004723fc1de8", size = 2026640 }, 243 | { url = "https://files.pythonhosted.org/packages/19/78/f381d643b12378fee782a72126ec5d793081ef03791c28a0fd542a5bee64/pydantic_core-2.33.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:99b56acd433386c8f20be5c4000786d1e7ca0523c8eefc995d14d79c7a081498", size = 1852649 }, 244 | { url = "https://files.pythonhosted.org/packages/9d/2b/98a37b80b15aac9eb2c6cfc6dbd35e5058a352891c5cce3a8472d77665a6/pydantic_core-2.33.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:35a5ec3fa8c2fe6c53e1b2ccc2454398f95d5393ab398478f53e1afbbeb4d939", size = 1892472 }, 245 | { url = "https://files.pythonhosted.org/packages/4e/d4/3c59514e0f55a161004792b9ff3039da52448f43f5834f905abef9db6e4a/pydantic_core-2.33.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b172f7b9d2f3abc0efd12e3386f7e48b576ef309544ac3a63e5e9cdd2e24585d", size = 1977509 }, 246 | { url = "https://files.pythonhosted.org/packages/a9/b6/c2c7946ef70576f79a25db59a576bce088bdc5952d1b93c9789b091df716/pydantic_core-2.33.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9097b9f17f91eea659b9ec58148c0747ec354a42f7389b9d50701610d86f812e", size = 2128702 }, 247 | { url = "https://files.pythonhosted.org/packages/88/fe/65a880f81e3f2a974312b61f82a03d85528f89a010ce21ad92f109d94deb/pydantic_core-2.33.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cc77ec5b7e2118b152b0d886c7514a4653bcb58c6b1d760134a9fab915f777b3", size = 2679428 }, 248 | { url = "https://files.pythonhosted.org/packages/6f/ff/4459e4146afd0462fb483bb98aa2436d69c484737feaceba1341615fb0ac/pydantic_core-2.33.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d5e3d15245b08fa4a84cefc6c9222e6f37c98111c8679fbd94aa145f9a0ae23d", size = 2008753 }, 249 | { url = "https://files.pythonhosted.org/packages/7c/76/1c42e384e8d78452ededac8b583fe2550c84abfef83a0552e0e7478ccbc3/pydantic_core-2.33.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ef99779001d7ac2e2461d8ab55d3373fe7315caefdbecd8ced75304ae5a6fc6b", size = 2114849 }, 250 | { url = "https://files.pythonhosted.org/packages/00/72/7d0cf05095c15f7ffe0eb78914b166d591c0eed72f294da68378da205101/pydantic_core-2.33.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:fc6bf8869e193855e8d91d91f6bf59699a5cdfaa47a404e278e776dd7f168b39", size = 2069541 }, 251 | { url = "https://files.pythonhosted.org/packages/b3/69/94a514066bb7d8be499aa764926937409d2389c09be0b5107a970286ef81/pydantic_core-2.33.1-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:b1caa0bc2741b043db7823843e1bde8aaa58a55a58fda06083b0569f8b45693a", size = 2239225 }, 252 | { url = "https://files.pythonhosted.org/packages/84/b0/e390071eadb44b41f4f54c3cef64d8bf5f9612c92686c9299eaa09e267e2/pydantic_core-2.33.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ec259f62538e8bf364903a7d0d0239447059f9434b284f5536e8402b7dd198db", size = 2248373 }, 253 | { url = "https://files.pythonhosted.org/packages/d6/b2/288b3579ffc07e92af66e2f1a11be3b056fe1214aab314748461f21a31c3/pydantic_core-2.33.1-cp312-cp312-win32.whl", hash = "sha256:e14f369c98a7c15772b9da98987f58e2b509a93235582838bd0d1d8c08b68fda", size = 1907034 }, 254 | { url = "https://files.pythonhosted.org/packages/02/28/58442ad1c22b5b6742b992ba9518420235adced665513868f99a1c2638a5/pydantic_core-2.33.1-cp312-cp312-win_amd64.whl", hash = "sha256:1c607801d85e2e123357b3893f82c97a42856192997b95b4d8325deb1cd0c5f4", size = 1956848 }, 255 | { url = "https://files.pythonhosted.org/packages/a1/eb/f54809b51c7e2a1d9f439f158b8dd94359321abcc98767e16fc48ae5a77e/pydantic_core-2.33.1-cp312-cp312-win_arm64.whl", hash = "sha256:8d13f0276806ee722e70a1c93da19748594f19ac4299c7e41237fc791d1861ea", size = 1903986 }, 256 | { url = "https://files.pythonhosted.org/packages/7a/24/eed3466a4308d79155f1cdd5c7432c80ddcc4530ba8623b79d5ced021641/pydantic_core-2.33.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:70af6a21237b53d1fe7b9325b20e65cbf2f0a848cf77bed492b029139701e66a", size = 2033551 }, 257 | { url = "https://files.pythonhosted.org/packages/ab/14/df54b1a0bc9b6ded9b758b73139d2c11b4e8eb43e8ab9c5847c0a2913ada/pydantic_core-2.33.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:282b3fe1bbbe5ae35224a0dbd05aed9ccabccd241e8e6b60370484234b456266", size = 1852785 }, 258 | { url = "https://files.pythonhosted.org/packages/fa/96/e275f15ff3d34bb04b0125d9bc8848bf69f25d784d92a63676112451bfb9/pydantic_core-2.33.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b315e596282bbb5822d0c7ee9d255595bd7506d1cb20c2911a4da0b970187d3", size = 1897758 }, 259 | { url = "https://files.pythonhosted.org/packages/b7/d8/96bc536e975b69e3a924b507d2a19aedbf50b24e08c80fb00e35f9baaed8/pydantic_core-2.33.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1dfae24cf9921875ca0ca6a8ecb4bb2f13c855794ed0d468d6abbec6e6dcd44a", size = 1986109 }, 260 | { url = "https://files.pythonhosted.org/packages/90/72/ab58e43ce7e900b88cb571ed057b2fcd0e95b708a2e0bed475b10130393e/pydantic_core-2.33.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6dd8ecfde08d8bfadaea669e83c63939af76f4cf5538a72597016edfa3fad516", size = 2129159 }, 261 | { url = "https://files.pythonhosted.org/packages/dc/3f/52d85781406886c6870ac995ec0ba7ccc028b530b0798c9080531b409fdb/pydantic_core-2.33.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2f593494876eae852dc98c43c6f260f45abdbfeec9e4324e31a481d948214764", size = 2680222 }, 262 | { url = "https://files.pythonhosted.org/packages/f4/56/6e2ef42f363a0eec0fd92f74a91e0ac48cd2e49b695aac1509ad81eee86a/pydantic_core-2.33.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:948b73114f47fd7016088e5186d13faf5e1b2fe83f5e320e371f035557fd264d", size = 2006980 }, 263 | { url = "https://files.pythonhosted.org/packages/4c/c0/604536c4379cc78359f9ee0aa319f4aedf6b652ec2854953f5a14fc38c5a/pydantic_core-2.33.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e11f3864eb516af21b01e25fac915a82e9ddad3bb0fb9e95a246067398b435a4", size = 2120840 }, 264 | { url = "https://files.pythonhosted.org/packages/1f/46/9eb764814f508f0edfb291a0f75d10854d78113fa13900ce13729aaec3ae/pydantic_core-2.33.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:549150be302428b56fdad0c23c2741dcdb5572413776826c965619a25d9c6bde", size = 2072518 }, 265 | { url = "https://files.pythonhosted.org/packages/42/e3/fb6b2a732b82d1666fa6bf53e3627867ea3131c5f39f98ce92141e3e3dc1/pydantic_core-2.33.1-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:495bc156026efafd9ef2d82372bd38afce78ddd82bf28ef5276c469e57c0c83e", size = 2248025 }, 266 | { url = "https://files.pythonhosted.org/packages/5c/9d/fbe8fe9d1aa4dac88723f10a921bc7418bd3378a567cb5e21193a3c48b43/pydantic_core-2.33.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ec79de2a8680b1a67a07490bddf9636d5c2fab609ba8c57597e855fa5fa4dacd", size = 2254991 }, 267 | { url = "https://files.pythonhosted.org/packages/aa/99/07e2237b8a66438d9b26482332cda99a9acccb58d284af7bc7c946a42fd3/pydantic_core-2.33.1-cp313-cp313-win32.whl", hash = "sha256:ee12a7be1742f81b8a65b36c6921022301d466b82d80315d215c4c691724986f", size = 1915262 }, 268 | { url = "https://files.pythonhosted.org/packages/8a/f4/e457a7849beeed1e5defbcf5051c6f7b3c91a0624dd31543a64fc9adcf52/pydantic_core-2.33.1-cp313-cp313-win_amd64.whl", hash = "sha256:ede9b407e39949d2afc46385ce6bd6e11588660c26f80576c11c958e6647bc40", size = 1956626 }, 269 | { url = "https://files.pythonhosted.org/packages/20/d0/e8d567a7cff7b04e017ae164d98011f1e1894269fe8e90ea187a3cbfb562/pydantic_core-2.33.1-cp313-cp313-win_arm64.whl", hash = "sha256:aa687a23d4b7871a00e03ca96a09cad0f28f443690d300500603bd0adba4b523", size = 1909590 }, 270 | { url = "https://files.pythonhosted.org/packages/ef/fd/24ea4302d7a527d672c5be06e17df16aabfb4e9fdc6e0b345c21580f3d2a/pydantic_core-2.33.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:401d7b76e1000d0dd5538e6381d28febdcacb097c8d340dde7d7fc6e13e9f95d", size = 1812963 }, 271 | { url = "https://files.pythonhosted.org/packages/5f/95/4fbc2ecdeb5c1c53f1175a32d870250194eb2fdf6291b795ab08c8646d5d/pydantic_core-2.33.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7aeb055a42d734c0255c9e489ac67e75397d59c6fbe60d155851e9782f276a9c", size = 1986896 }, 272 | { url = "https://files.pythonhosted.org/packages/71/ae/fe31e7f4a62431222d8f65a3bd02e3fa7e6026d154a00818e6d30520ea77/pydantic_core-2.33.1-cp313-cp313t-win_amd64.whl", hash = "sha256:338ea9b73e6e109f15ab439e62cb3b78aa752c7fd9536794112e14bee02c8d18", size = 1931810 }, 273 | { url = "https://files.pythonhosted.org/packages/9c/c7/8b311d5adb0fe00a93ee9b4e92a02b0ec08510e9838885ef781ccbb20604/pydantic_core-2.33.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5c834f54f8f4640fd7e4b193f80eb25a0602bba9e19b3cd2fc7ffe8199f5ae02", size = 2041659 }, 274 | { url = "https://files.pythonhosted.org/packages/8a/d6/4f58d32066a9e26530daaf9adc6664b01875ae0691570094968aaa7b8fcc/pydantic_core-2.33.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:049e0de24cf23766f12cc5cc71d8abc07d4a9deb9061b334b62093dedc7cb068", size = 1873294 }, 275 | { url = "https://files.pythonhosted.org/packages/f7/3f/53cc9c45d9229da427909c751f8ed2bf422414f7664ea4dde2d004f596ba/pydantic_core-2.33.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a28239037b3d6f16916a4c831a5a0eadf856bdd6d2e92c10a0da3a59eadcf3e", size = 1903771 }, 276 | { url = "https://files.pythonhosted.org/packages/f0/49/bf0783279ce674eb9903fb9ae43f6c614cb2f1c4951370258823f795368b/pydantic_core-2.33.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d3da303ab5f378a268fa7d45f37d7d85c3ec19769f28d2cc0c61826a8de21fe", size = 2083558 }, 277 | { url = "https://files.pythonhosted.org/packages/9c/5b/0d998367687f986c7d8484a2c476d30f07bf5b8b1477649a6092bd4c540e/pydantic_core-2.33.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:25626fb37b3c543818c14821afe0fd3830bc327a43953bc88db924b68c5723f1", size = 2118038 }, 278 | { url = "https://files.pythonhosted.org/packages/b3/33/039287d410230ee125daee57373ac01940d3030d18dba1c29cd3089dc3ca/pydantic_core-2.33.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:3ab2d36e20fbfcce8f02d73c33a8a7362980cff717926bbae030b93ae46b56c7", size = 2079315 }, 279 | { url = "https://files.pythonhosted.org/packages/1f/85/6d8b2646d99c062d7da2d0ab2faeb0d6ca9cca4c02da6076376042a20da3/pydantic_core-2.33.1-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:2f9284e11c751b003fd4215ad92d325d92c9cb19ee6729ebd87e3250072cdcde", size = 2249063 }, 280 | { url = "https://files.pythonhosted.org/packages/17/d7/c37d208d5738f7b9ad8f22ae8a727d88ebf9c16c04ed2475122cc3f7224a/pydantic_core-2.33.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:048c01eee07d37cbd066fc512b9d8b5ea88ceeb4e629ab94b3e56965ad655add", size = 2254631 }, 281 | { url = "https://files.pythonhosted.org/packages/13/e0/bafa46476d328e4553b85ab9b2f7409e7aaef0ce4c937c894821c542d347/pydantic_core-2.33.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:5ccd429694cf26af7997595d627dd2637e7932214486f55b8a357edaac9dae8c", size = 2080877 }, 282 | { url = "https://files.pythonhosted.org/packages/0b/76/1794e440c1801ed35415238d2c728f26cd12695df9057154ad768b7b991c/pydantic_core-2.33.1-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3a371dc00282c4b84246509a5ddc808e61b9864aa1eae9ecc92bb1268b82db4a", size = 2042858 }, 283 | { url = "https://files.pythonhosted.org/packages/73/b4/9cd7b081fb0b1b4f8150507cd59d27b275c3e22ad60b35cb19ea0977d9b9/pydantic_core-2.33.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:f59295ecc75a1788af8ba92f2e8c6eeaa5a94c22fc4d151e8d9638814f85c8fc", size = 1873745 }, 284 | { url = "https://files.pythonhosted.org/packages/e1/d7/9ddb7575d4321e40d0363903c2576c8c0c3280ebea137777e5ab58d723e3/pydantic_core-2.33.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:08530b8ac922003033f399128505f513e30ca770527cc8bbacf75a84fcc2c74b", size = 1904188 }, 285 | { url = "https://files.pythonhosted.org/packages/d1/a8/3194ccfe461bb08da19377ebec8cb4f13c9bd82e13baebc53c5c7c39a029/pydantic_core-2.33.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bae370459da6a5466978c0eacf90690cb57ec9d533f8e63e564ef3822bfa04fe", size = 2083479 }, 286 | { url = "https://files.pythonhosted.org/packages/42/c7/84cb569555d7179ca0b3f838cef08f66f7089b54432f5b8599aac6e9533e/pydantic_core-2.33.1-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e3de2777e3b9f4d603112f78006f4ae0acb936e95f06da6cb1a45fbad6bdb4b5", size = 2118415 }, 287 | { url = "https://files.pythonhosted.org/packages/3b/67/72abb8c73e0837716afbb58a59cc9e3ae43d1aa8677f3b4bc72c16142716/pydantic_core-2.33.1-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:3a64e81e8cba118e108d7126362ea30e021291b7805d47e4896e52c791be2761", size = 2079623 }, 288 | { url = "https://files.pythonhosted.org/packages/0b/cd/c59707e35a47ba4cbbf153c3f7c56420c58653b5801b055dc52cccc8e2dc/pydantic_core-2.33.1-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:52928d8c1b6bda03cc6d811e8923dffc87a2d3c8b3bfd2ce16471c7147a24850", size = 2250175 }, 289 | { url = "https://files.pythonhosted.org/packages/84/32/e4325a6676b0bed32d5b084566ec86ed7fd1e9bcbfc49c578b1755bde920/pydantic_core-2.33.1-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:1b30d92c9412beb5ac6b10a3eb7ef92ccb14e3f2a8d7732e2d739f58b3aa7544", size = 2254674 }, 290 | { url = "https://files.pythonhosted.org/packages/12/6f/5596dc418f2e292ffc661d21931ab34591952e2843e7168ea5a52591f6ff/pydantic_core-2.33.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:f995719707e0e29f0f41a8aa3bcea6e761a36c9136104d3189eafb83f5cec5e5", size = 2080951 }, 291 | ] 292 | 293 | [[package]] 294 | name = "pydantic-settings" 295 | version = "2.9.1" 296 | source = { registry = "https://pypi.org/simple" } 297 | dependencies = [ 298 | { name = "pydantic" }, 299 | { name = "python-dotenv" }, 300 | { name = "typing-inspection" }, 301 | ] 302 | sdist = { url = "https://files.pythonhosted.org/packages/67/1d/42628a2c33e93f8e9acbde0d5d735fa0850f3e6a2f8cb1eb6c40b9a732ac/pydantic_settings-2.9.1.tar.gz", hash = "sha256:c509bf79d27563add44e8446233359004ed85066cd096d8b510f715e6ef5d268", size = 163234 } 303 | wheels = [ 304 | { url = "https://files.pythonhosted.org/packages/b6/5f/d6d641b490fd3ec2c4c13b4244d68deea3a1b970a97be64f34fb5504ff72/pydantic_settings-2.9.1-py3-none-any.whl", hash = "sha256:59b4f431b1defb26fe620c71a7d3968a710d719f5f4cdbbdb7926edeb770f6ef", size = 44356 }, 305 | ] 306 | 307 | [[package]] 308 | name = "python-dotenv" 309 | version = "1.1.0" 310 | source = { registry = "https://pypi.org/simple" } 311 | sdist = { url = "https://files.pythonhosted.org/packages/88/2c/7bb1416c5620485aa793f2de31d3df393d3686aa8a8506d11e10e13c5baf/python_dotenv-1.1.0.tar.gz", hash = "sha256:41f90bc6f5f177fb41f53e87666db362025010eb28f60a01c9143bfa33a2b2d5", size = 39920 } 312 | wheels = [ 313 | { url = "https://files.pythonhosted.org/packages/1e/18/98a99ad95133c6a6e2005fe89faedf294a748bd5dc803008059409ac9b1e/python_dotenv-1.1.0-py3-none-any.whl", hash = "sha256:d7c01d9e2293916c18baf562d95698754b0dbbb5e74d457c45d4f6561fb9d55d", size = 20256 }, 314 | ] 315 | 316 | [[package]] 317 | name = "python-multipart" 318 | version = "0.0.20" 319 | source = { registry = "https://pypi.org/simple" } 320 | sdist = { url = "https://files.pythonhosted.org/packages/f3/87/f44d7c9f274c7ee665a29b885ec97089ec5dc034c7f3fafa03da9e39a09e/python_multipart-0.0.20.tar.gz", hash = "sha256:8dd0cab45b8e23064ae09147625994d090fa46f5b0d1e13af944c331a7fa9d13", size = 37158 } 321 | wheels = [ 322 | { url = "https://files.pythonhosted.org/packages/45/58/38b5afbc1a800eeea951b9285d3912613f2603bdf897a4ab0f4bd7f405fc/python_multipart-0.0.20-py3-none-any.whl", hash = "sha256:8a62d3a8335e06589fe01f2a3e178cdcc632f3fbe0d492ad9ee0ec35aab1f104", size = 24546 }, 323 | ] 324 | 325 | [[package]] 326 | name = "referencing" 327 | version = "0.36.2" 328 | source = { registry = "https://pypi.org/simple" } 329 | dependencies = [ 330 | { name = "attrs" }, 331 | { name = "rpds-py" }, 332 | { name = "typing-extensions", marker = "python_full_version < '3.13'" }, 333 | ] 334 | sdist = { url = "https://files.pythonhosted.org/packages/2f/db/98b5c277be99dd18bfd91dd04e1b759cad18d1a338188c936e92f921c7e2/referencing-0.36.2.tar.gz", hash = "sha256:df2e89862cd09deabbdba16944cc3f10feb6b3e6f18e902f7cc25609a34775aa", size = 74744 } 335 | wheels = [ 336 | { url = "https://files.pythonhosted.org/packages/c1/b1/3baf80dc6d2b7bc27a95a67752d0208e410351e3feb4eb78de5f77454d8d/referencing-0.36.2-py3-none-any.whl", hash = "sha256:e8699adbbf8b5c7de96d8ffa0eb5c158b3beafce084968e2ea8bb08c6794dcd0", size = 26775 }, 337 | ] 338 | 339 | [[package]] 340 | name = "rpds-py" 341 | version = "0.26.0" 342 | source = { registry = "https://pypi.org/simple" } 343 | sdist = { url = "https://files.pythonhosted.org/packages/a5/aa/4456d84bbb54adc6a916fb10c9b374f78ac840337644e4a5eda229c81275/rpds_py-0.26.0.tar.gz", hash = "sha256:20dae58a859b0906f0685642e591056f1e787f3a8b39c8e8749a45dc7d26bdb0", size = 27385 } 344 | wheels = [ 345 | { url = "https://files.pythonhosted.org/packages/b9/31/1459645f036c3dfeacef89e8e5825e430c77dde8489f3b99eaafcd4a60f5/rpds_py-0.26.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:4c70c70f9169692b36307a95f3d8c0a9fcd79f7b4a383aad5eaa0e9718b79b37", size = 372466 }, 346 | { url = "https://files.pythonhosted.org/packages/dd/ff/3d0727f35836cc8773d3eeb9a46c40cc405854e36a8d2e951f3a8391c976/rpds_py-0.26.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:777c62479d12395bfb932944e61e915741e364c843afc3196b694db3d669fcd0", size = 357825 }, 347 | { url = "https://files.pythonhosted.org/packages/bf/ce/badc5e06120a54099ae287fa96d82cbb650a5f85cf247ffe19c7b157fd1f/rpds_py-0.26.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec671691e72dff75817386aa02d81e708b5a7ec0dec6669ec05213ff6b77e1bd", size = 381530 }, 348 | { url = "https://files.pythonhosted.org/packages/1e/a5/fa5d96a66c95d06c62d7a30707b6a4cfec696ab8ae280ee7be14e961e118/rpds_py-0.26.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6a1cb5d6ce81379401bbb7f6dbe3d56de537fb8235979843f0d53bc2e9815a79", size = 396933 }, 349 | { url = "https://files.pythonhosted.org/packages/00/a7/7049d66750f18605c591a9db47d4a059e112a0c9ff8de8daf8fa0f446bba/rpds_py-0.26.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4f789e32fa1fb6a7bf890e0124e7b42d1e60d28ebff57fe806719abb75f0e9a3", size = 513973 }, 350 | { url = "https://files.pythonhosted.org/packages/0e/f1/528d02c7d6b29d29fac8fd784b354d3571cc2153f33f842599ef0cf20dd2/rpds_py-0.26.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c55b0a669976cf258afd718de3d9ad1b7d1fe0a91cd1ab36f38b03d4d4aeaaf", size = 402293 }, 351 | { url = "https://files.pythonhosted.org/packages/15/93/fde36cd6e4685df2cd08508f6c45a841e82f5bb98c8d5ecf05649522acb5/rpds_py-0.26.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c70d9ec912802ecfd6cd390dadb34a9578b04f9bcb8e863d0a7598ba5e9e7ccc", size = 383787 }, 352 | { url = "https://files.pythonhosted.org/packages/69/f2/5007553aaba1dcae5d663143683c3dfd03d9395289f495f0aebc93e90f24/rpds_py-0.26.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3021933c2cb7def39d927b9862292e0f4c75a13d7de70eb0ab06efed4c508c19", size = 416312 }, 353 | { url = "https://files.pythonhosted.org/packages/8f/a7/ce52c75c1e624a79e48a69e611f1c08844564e44c85db2b6f711d76d10ce/rpds_py-0.26.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8a7898b6ca3b7d6659e55cdac825a2e58c638cbf335cde41f4619e290dd0ad11", size = 558403 }, 354 | { url = "https://files.pythonhosted.org/packages/79/d5/e119db99341cc75b538bf4cb80504129fa22ce216672fb2c28e4a101f4d9/rpds_py-0.26.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:12bff2ad9447188377f1b2794772f91fe68bb4bbfa5a39d7941fbebdbf8c500f", size = 588323 }, 355 | { url = "https://files.pythonhosted.org/packages/93/94/d28272a0b02f5fe24c78c20e13bbcb95f03dc1451b68e7830ca040c60bd6/rpds_py-0.26.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:191aa858f7d4902e975d4cf2f2d9243816c91e9605070aeb09c0a800d187e323", size = 554541 }, 356 | { url = "https://files.pythonhosted.org/packages/93/e0/8c41166602f1b791da892d976057eba30685486d2e2c061ce234679c922b/rpds_py-0.26.0-cp310-cp310-win32.whl", hash = "sha256:b37a04d9f52cb76b6b78f35109b513f6519efb481d8ca4c321f6a3b9580b3f45", size = 220442 }, 357 | { url = "https://files.pythonhosted.org/packages/87/f0/509736bb752a7ab50fb0270c2a4134d671a7b3038030837e5536c3de0e0b/rpds_py-0.26.0-cp310-cp310-win_amd64.whl", hash = "sha256:38721d4c9edd3eb6670437d8d5e2070063f305bfa2d5aa4278c51cedcd508a84", size = 231314 }, 358 | { url = "https://files.pythonhosted.org/packages/09/4c/4ee8f7e512030ff79fda1df3243c88d70fc874634e2dbe5df13ba4210078/rpds_py-0.26.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:9e8cb77286025bdb21be2941d64ac6ca016130bfdcd228739e8ab137eb4406ed", size = 372610 }, 359 | { url = "https://files.pythonhosted.org/packages/fa/9d/3dc16be00f14fc1f03c71b1d67c8df98263ab2710a2fbd65a6193214a527/rpds_py-0.26.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5e09330b21d98adc8ccb2dbb9fc6cb434e8908d4c119aeaa772cb1caab5440a0", size = 358032 }, 360 | { url = "https://files.pythonhosted.org/packages/e7/5a/7f1bf8f045da2866324a08ae80af63e64e7bfaf83bd31f865a7b91a58601/rpds_py-0.26.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c9c1b92b774b2e68d11193dc39620d62fd8ab33f0a3c77ecdabe19c179cdbc1", size = 381525 }, 361 | { url = "https://files.pythonhosted.org/packages/45/8a/04479398c755a066ace10e3d158866beb600867cacae194c50ffa783abd0/rpds_py-0.26.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:824e6d3503ab990d7090768e4dfd9e840837bae057f212ff9f4f05ec6d1975e7", size = 397089 }, 362 | { url = "https://files.pythonhosted.org/packages/72/88/9203f47268db488a1b6d469d69c12201ede776bb728b9d9f29dbfd7df406/rpds_py-0.26.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8ad7fd2258228bf288f2331f0a6148ad0186b2e3643055ed0db30990e59817a6", size = 514255 }, 363 | { url = "https://files.pythonhosted.org/packages/f5/b4/01ce5d1e853ddf81fbbd4311ab1eff0b3cf162d559288d10fd127e2588b5/rpds_py-0.26.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0dc23bbb3e06ec1ea72d515fb572c1fea59695aefbffb106501138762e1e915e", size = 402283 }, 364 | { url = "https://files.pythonhosted.org/packages/34/a2/004c99936997bfc644d590a9defd9e9c93f8286568f9c16cdaf3e14429a7/rpds_py-0.26.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d80bf832ac7b1920ee29a426cdca335f96a2b5caa839811803e999b41ba9030d", size = 383881 }, 365 | { url = "https://files.pythonhosted.org/packages/05/1b/ef5fba4a8f81ce04c427bfd96223f92f05e6cd72291ce9d7523db3b03a6c/rpds_py-0.26.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0919f38f5542c0a87e7b4afcafab6fd2c15386632d249e9a087498571250abe3", size = 415822 }, 366 | { url = "https://files.pythonhosted.org/packages/16/80/5c54195aec456b292f7bd8aa61741c8232964063fd8a75fdde9c1e982328/rpds_py-0.26.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d422b945683e409000c888e384546dbab9009bb92f7c0b456e217988cf316107", size = 558347 }, 367 | { url = "https://files.pythonhosted.org/packages/f2/1c/1845c1b1fd6d827187c43afe1841d91678d7241cbdb5420a4c6de180a538/rpds_py-0.26.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:77a7711fa562ba2da1aa757e11024ad6d93bad6ad7ede5afb9af144623e5f76a", size = 587956 }, 368 | { url = "https://files.pythonhosted.org/packages/2e/ff/9e979329dd131aa73a438c077252ddabd7df6d1a7ad7b9aacf6261f10faa/rpds_py-0.26.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:238e8c8610cb7c29460e37184f6799547f7e09e6a9bdbdab4e8edb90986a2318", size = 554363 }, 369 | { url = "https://files.pythonhosted.org/packages/00/8b/d78cfe034b71ffbe72873a136e71acc7a831a03e37771cfe59f33f6de8a2/rpds_py-0.26.0-cp311-cp311-win32.whl", hash = "sha256:893b022bfbdf26d7bedb083efeea624e8550ca6eb98bf7fea30211ce95b9201a", size = 220123 }, 370 | { url = "https://files.pythonhosted.org/packages/94/c1/3c8c94c7dd3905dbfde768381ce98778500a80db9924731d87ddcdb117e9/rpds_py-0.26.0-cp311-cp311-win_amd64.whl", hash = "sha256:87a5531de9f71aceb8af041d72fc4cab4943648d91875ed56d2e629bef6d4c03", size = 231732 }, 371 | { url = "https://files.pythonhosted.org/packages/67/93/e936fbed1b734eabf36ccb5d93c6a2e9246fbb13c1da011624b7286fae3e/rpds_py-0.26.0-cp311-cp311-win_arm64.whl", hash = "sha256:de2713f48c1ad57f89ac25b3cb7daed2156d8e822cf0eca9b96a6f990718cc41", size = 221917 }, 372 | { url = "https://files.pythonhosted.org/packages/ea/86/90eb87c6f87085868bd077c7a9938006eb1ce19ed4d06944a90d3560fce2/rpds_py-0.26.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:894514d47e012e794f1350f076c427d2347ebf82f9b958d554d12819849a369d", size = 363933 }, 373 | { url = "https://files.pythonhosted.org/packages/63/78/4469f24d34636242c924626082b9586f064ada0b5dbb1e9d096ee7a8e0c6/rpds_py-0.26.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc921b96fa95a097add244da36a1d9e4f3039160d1d30f1b35837bf108c21136", size = 350447 }, 374 | { url = "https://files.pythonhosted.org/packages/ad/91/c448ed45efdfdade82348d5e7995e15612754826ea640afc20915119734f/rpds_py-0.26.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e1157659470aa42a75448b6e943c895be8c70531c43cb78b9ba990778955582", size = 384711 }, 375 | { url = "https://files.pythonhosted.org/packages/ec/43/e5c86fef4be7f49828bdd4ecc8931f0287b1152c0bb0163049b3218740e7/rpds_py-0.26.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:521ccf56f45bb3a791182dc6b88ae5f8fa079dd705ee42138c76deb1238e554e", size = 400865 }, 376 | { url = "https://files.pythonhosted.org/packages/55/34/e00f726a4d44f22d5c5fe2e5ddd3ac3d7fd3f74a175607781fbdd06fe375/rpds_py-0.26.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9def736773fd56b305c0eef698be5192c77bfa30d55a0e5885f80126c4831a15", size = 517763 }, 377 | { url = "https://files.pythonhosted.org/packages/52/1c/52dc20c31b147af724b16104500fba13e60123ea0334beba7b40e33354b4/rpds_py-0.26.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cdad4ea3b4513b475e027be79e5a0ceac8ee1c113a1a11e5edc3c30c29f964d8", size = 406651 }, 378 | { url = "https://files.pythonhosted.org/packages/2e/77/87d7bfabfc4e821caa35481a2ff6ae0b73e6a391bb6b343db2c91c2b9844/rpds_py-0.26.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82b165b07f416bdccf5c84546a484cc8f15137ca38325403864bfdf2b5b72f6a", size = 386079 }, 379 | { url = "https://files.pythonhosted.org/packages/e3/d4/7f2200c2d3ee145b65b3cddc4310d51f7da6a26634f3ac87125fd789152a/rpds_py-0.26.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d04cab0a54b9dba4d278fe955a1390da3cf71f57feb78ddc7cb67cbe0bd30323", size = 421379 }, 380 | { url = "https://files.pythonhosted.org/packages/ae/13/9fdd428b9c820869924ab62236b8688b122baa22d23efdd1c566938a39ba/rpds_py-0.26.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:79061ba1a11b6a12743a2b0f72a46aa2758613d454aa6ba4f5a265cc48850158", size = 562033 }, 381 | { url = "https://files.pythonhosted.org/packages/f3/e1/b69686c3bcbe775abac3a4c1c30a164a2076d28df7926041f6c0eb5e8d28/rpds_py-0.26.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:f405c93675d8d4c5ac87364bb38d06c988e11028a64b52a47158a355079661f3", size = 591639 }, 382 | { url = "https://files.pythonhosted.org/packages/5c/c9/1e3d8c8863c84a90197ac577bbc3d796a92502124c27092413426f670990/rpds_py-0.26.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dafd4c44b74aa4bed4b250f1aed165b8ef5de743bcca3b88fc9619b6087093d2", size = 557105 }, 383 | { url = "https://files.pythonhosted.org/packages/9f/c5/90c569649057622959f6dcc40f7b516539608a414dfd54b8d77e3b201ac0/rpds_py-0.26.0-cp312-cp312-win32.whl", hash = "sha256:3da5852aad63fa0c6f836f3359647870e21ea96cf433eb393ffa45263a170d44", size = 223272 }, 384 | { url = "https://files.pythonhosted.org/packages/7d/16/19f5d9f2a556cfed454eebe4d354c38d51c20f3db69e7b4ce6cff904905d/rpds_py-0.26.0-cp312-cp312-win_amd64.whl", hash = "sha256:cf47cfdabc2194a669dcf7a8dbba62e37a04c5041d2125fae0233b720da6f05c", size = 234995 }, 385 | { url = "https://files.pythonhosted.org/packages/83/f0/7935e40b529c0e752dfaa7880224771b51175fce08b41ab4a92eb2fbdc7f/rpds_py-0.26.0-cp312-cp312-win_arm64.whl", hash = "sha256:20ab1ae4fa534f73647aad289003f1104092890849e0266271351922ed5574f8", size = 223198 }, 386 | { url = "https://files.pythonhosted.org/packages/6a/67/bb62d0109493b12b1c6ab00de7a5566aa84c0e44217c2d94bee1bd370da9/rpds_py-0.26.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:696764a5be111b036256c0b18cd29783fab22154690fc698062fc1b0084b511d", size = 363917 }, 387 | { url = "https://files.pythonhosted.org/packages/4b/f3/34e6ae1925a5706c0f002a8d2d7f172373b855768149796af87bd65dcdb9/rpds_py-0.26.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1e6c15d2080a63aaed876e228efe4f814bc7889c63b1e112ad46fdc8b368b9e1", size = 350073 }, 388 | { url = "https://files.pythonhosted.org/packages/75/83/1953a9d4f4e4de7fd0533733e041c28135f3c21485faaef56a8aadbd96b5/rpds_py-0.26.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:390e3170babf42462739a93321e657444f0862c6d722a291accc46f9d21ed04e", size = 384214 }, 389 | { url = "https://files.pythonhosted.org/packages/48/0e/983ed1b792b3322ea1d065e67f4b230f3b96025f5ce3878cc40af09b7533/rpds_py-0.26.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7da84c2c74c0f5bc97d853d9e17bb83e2dcafcff0dc48286916001cc114379a1", size = 400113 }, 390 | { url = "https://files.pythonhosted.org/packages/69/7f/36c0925fff6f660a80be259c5b4f5e53a16851f946eb080351d057698528/rpds_py-0.26.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4c5fe114a6dd480a510b6d3661d09d67d1622c4bf20660a474507aaee7eeeee9", size = 515189 }, 391 | { url = "https://files.pythonhosted.org/packages/13/45/cbf07fc03ba7a9b54662c9badb58294ecfb24f828b9732970bd1a431ed5c/rpds_py-0.26.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3100b3090269f3a7ea727b06a6080d4eb7439dca4c0e91a07c5d133bb1727ea7", size = 406998 }, 392 | { url = "https://files.pythonhosted.org/packages/6c/b0/8fa5e36e58657997873fd6a1cf621285ca822ca75b4b3434ead047daa307/rpds_py-0.26.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c03c9b0c64afd0320ae57de4c982801271c0c211aa2d37f3003ff5feb75bb04", size = 385903 }, 393 | { url = "https://files.pythonhosted.org/packages/4b/f7/b25437772f9f57d7a9fbd73ed86d0dcd76b4c7c6998348c070d90f23e315/rpds_py-0.26.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5963b72ccd199ade6ee493723d18a3f21ba7d5b957017607f815788cef50eaf1", size = 419785 }, 394 | { url = "https://files.pythonhosted.org/packages/a7/6b/63ffa55743dfcb4baf2e9e77a0b11f7f97ed96a54558fcb5717a4b2cd732/rpds_py-0.26.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9da4e873860ad5bab3291438525cae80169daecbfafe5657f7f5fb4d6b3f96b9", size = 561329 }, 395 | { url = "https://files.pythonhosted.org/packages/2f/07/1f4f5e2886c480a2346b1e6759c00278b8a69e697ae952d82ae2e6ee5db0/rpds_py-0.26.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:5afaddaa8e8c7f1f7b4c5c725c0070b6eed0228f705b90a1732a48e84350f4e9", size = 590875 }, 396 | { url = "https://files.pythonhosted.org/packages/cc/bc/e6639f1b91c3a55f8c41b47d73e6307051b6e246254a827ede730624c0f8/rpds_py-0.26.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4916dc96489616a6f9667e7526af8fa693c0fdb4f3acb0e5d9f4400eb06a47ba", size = 556636 }, 397 | { url = "https://files.pythonhosted.org/packages/05/4c/b3917c45566f9f9a209d38d9b54a1833f2bb1032a3e04c66f75726f28876/rpds_py-0.26.0-cp313-cp313-win32.whl", hash = "sha256:2a343f91b17097c546b93f7999976fd6c9d5900617aa848c81d794e062ab302b", size = 222663 }, 398 | { url = "https://files.pythonhosted.org/packages/e0/0b/0851bdd6025775aaa2365bb8de0697ee2558184c800bfef8d7aef5ccde58/rpds_py-0.26.0-cp313-cp313-win_amd64.whl", hash = "sha256:0a0b60701f2300c81b2ac88a5fb893ccfa408e1c4a555a77f908a2596eb875a5", size = 234428 }, 399 | { url = "https://files.pythonhosted.org/packages/ed/e8/a47c64ed53149c75fb581e14a237b7b7cd18217e969c30d474d335105622/rpds_py-0.26.0-cp313-cp313-win_arm64.whl", hash = "sha256:257d011919f133a4746958257f2c75238e3ff54255acd5e3e11f3ff41fd14256", size = 222571 }, 400 | { url = "https://files.pythonhosted.org/packages/89/bf/3d970ba2e2bcd17d2912cb42874107390f72873e38e79267224110de5e61/rpds_py-0.26.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:529c8156d7506fba5740e05da8795688f87119cce330c244519cf706a4a3d618", size = 360475 }, 401 | { url = "https://files.pythonhosted.org/packages/82/9f/283e7e2979fc4ec2d8ecee506d5a3675fce5ed9b4b7cb387ea5d37c2f18d/rpds_py-0.26.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f53ec51f9d24e9638a40cabb95078ade8c99251945dad8d57bf4aabe86ecee35", size = 346692 }, 402 | { url = "https://files.pythonhosted.org/packages/e3/03/7e50423c04d78daf391da3cc4330bdb97042fc192a58b186f2d5deb7befd/rpds_py-0.26.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab504c4d654e4a29558eaa5bb8cea5fdc1703ea60a8099ffd9c758472cf913f", size = 379415 }, 403 | { url = "https://files.pythonhosted.org/packages/57/00/d11ee60d4d3b16808432417951c63df803afb0e0fc672b5e8d07e9edaaae/rpds_py-0.26.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fd0641abca296bc1a00183fe44f7fced8807ed49d501f188faa642d0e4975b83", size = 391783 }, 404 | { url = "https://files.pythonhosted.org/packages/08/b3/1069c394d9c0d6d23c5b522e1f6546b65793a22950f6e0210adcc6f97c3e/rpds_py-0.26.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:69b312fecc1d017b5327afa81d4da1480f51c68810963a7336d92203dbb3d4f1", size = 512844 }, 405 | { url = "https://files.pythonhosted.org/packages/08/3b/c4fbf0926800ed70b2c245ceca99c49f066456755f5d6eb8863c2c51e6d0/rpds_py-0.26.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c741107203954f6fc34d3066d213d0a0c40f7bb5aafd698fb39888af277c70d8", size = 402105 }, 406 | { url = "https://files.pythonhosted.org/packages/1c/b0/db69b52ca07413e568dae9dc674627a22297abb144c4d6022c6d78f1e5cc/rpds_py-0.26.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc3e55a7db08dc9a6ed5fb7103019d2c1a38a349ac41901f9f66d7f95750942f", size = 383440 }, 407 | { url = "https://files.pythonhosted.org/packages/4c/e1/c65255ad5b63903e56b3bb3ff9dcc3f4f5c3badde5d08c741ee03903e951/rpds_py-0.26.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9e851920caab2dbcae311fd28f4313c6953993893eb5c1bb367ec69d9a39e7ed", size = 412759 }, 408 | { url = "https://files.pythonhosted.org/packages/e4/22/bb731077872377a93c6e93b8a9487d0406c70208985831034ccdeed39c8e/rpds_py-0.26.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:dfbf280da5f876d0b00c81f26bedce274e72a678c28845453885a9b3c22ae632", size = 556032 }, 409 | { url = "https://files.pythonhosted.org/packages/e0/8b/393322ce7bac5c4530fb96fc79cc9ea2f83e968ff5f6e873f905c493e1c4/rpds_py-0.26.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:1cc81d14ddfa53d7f3906694d35d54d9d3f850ef8e4e99ee68bc0d1e5fed9a9c", size = 585416 }, 410 | { url = "https://files.pythonhosted.org/packages/49/ae/769dc372211835bf759319a7aae70525c6eb523e3371842c65b7ef41c9c6/rpds_py-0.26.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dca83c498b4650a91efcf7b88d669b170256bf8017a5db6f3e06c2bf031f57e0", size = 554049 }, 411 | { url = "https://files.pythonhosted.org/packages/6b/f9/4c43f9cc203d6ba44ce3146246cdc38619d92c7bd7bad4946a3491bd5b70/rpds_py-0.26.0-cp313-cp313t-win32.whl", hash = "sha256:4d11382bcaf12f80b51d790dee295c56a159633a8e81e6323b16e55d81ae37e9", size = 218428 }, 412 | { url = "https://files.pythonhosted.org/packages/7e/8b/9286b7e822036a4a977f2f1e851c7345c20528dbd56b687bb67ed68a8ede/rpds_py-0.26.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ff110acded3c22c033e637dd8896e411c7d3a11289b2edf041f86663dbc791e9", size = 231524 }, 413 | { url = "https://files.pythonhosted.org/packages/55/07/029b7c45db910c74e182de626dfdae0ad489a949d84a468465cd0ca36355/rpds_py-0.26.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:da619979df60a940cd434084355c514c25cf8eb4cf9a508510682f6c851a4f7a", size = 364292 }, 414 | { url = "https://files.pythonhosted.org/packages/13/d1/9b3d3f986216b4d1f584878dca15ce4797aaf5d372d738974ba737bf68d6/rpds_py-0.26.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:ea89a2458a1a75f87caabefe789c87539ea4e43b40f18cff526052e35bbb4fdf", size = 350334 }, 415 | { url = "https://files.pythonhosted.org/packages/18/98/16d5e7bc9ec715fa9668731d0cf97f6b032724e61696e2db3d47aeb89214/rpds_py-0.26.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:feac1045b3327a45944e7dcbeb57530339f6b17baff154df51ef8b0da34c8c12", size = 384875 }, 416 | { url = "https://files.pythonhosted.org/packages/f9/13/aa5e2b1ec5ab0e86a5c464d53514c0467bec6ba2507027d35fc81818358e/rpds_py-0.26.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b818a592bd69bfe437ee8368603d4a2d928c34cffcdf77c2e761a759ffd17d20", size = 399993 }, 417 | { url = "https://files.pythonhosted.org/packages/17/03/8021810b0e97923abdbab6474c8b77c69bcb4b2c58330777df9ff69dc559/rpds_py-0.26.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1a8b0dd8648709b62d9372fc00a57466f5fdeefed666afe3fea5a6c9539a0331", size = 516683 }, 418 | { url = "https://files.pythonhosted.org/packages/dc/b1/da8e61c87c2f3d836954239fdbbfb477bb7b54d74974d8f6fcb34342d166/rpds_py-0.26.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6d3498ad0df07d81112aa6ec6c95a7e7b1ae00929fb73e7ebee0f3faaeabad2f", size = 408825 }, 419 | { url = "https://files.pythonhosted.org/packages/38/bc/1fc173edaaa0e52c94b02a655db20697cb5fa954ad5a8e15a2c784c5cbdd/rpds_py-0.26.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24a4146ccb15be237fdef10f331c568e1b0e505f8c8c9ed5d67759dac58ac246", size = 387292 }, 420 | { url = "https://files.pythonhosted.org/packages/7c/eb/3a9bb4bd90867d21916f253caf4f0d0be7098671b6715ad1cead9fe7bab9/rpds_py-0.26.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a9a63785467b2d73635957d32a4f6e73d5e4df497a16a6392fa066b753e87387", size = 420435 }, 421 | { url = "https://files.pythonhosted.org/packages/cd/16/e066dcdb56f5632713445271a3f8d3d0b426d51ae9c0cca387799df58b02/rpds_py-0.26.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:de4ed93a8c91debfd5a047be327b7cc8b0cc6afe32a716bbbc4aedca9e2a83af", size = 562410 }, 422 | { url = "https://files.pythonhosted.org/packages/60/22/ddbdec7eb82a0dc2e455be44c97c71c232983e21349836ce9f272e8a3c29/rpds_py-0.26.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:caf51943715b12af827696ec395bfa68f090a4c1a1d2509eb4e2cb69abbbdb33", size = 590724 }, 423 | { url = "https://files.pythonhosted.org/packages/2c/b4/95744085e65b7187d83f2fcb0bef70716a1ea0a9e5d8f7f39a86e5d83424/rpds_py-0.26.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:4a59e5bc386de021f56337f757301b337d7ab58baa40174fb150accd480bc953", size = 558285 }, 424 | { url = "https://files.pythonhosted.org/packages/37/37/6309a75e464d1da2559446f9c811aa4d16343cebe3dbb73701e63f760caa/rpds_py-0.26.0-cp314-cp314-win32.whl", hash = "sha256:92c8db839367ef16a662478f0a2fe13e15f2227da3c1430a782ad0f6ee009ec9", size = 223459 }, 425 | { url = "https://files.pythonhosted.org/packages/d9/6f/8e9c11214c46098b1d1391b7e02b70bb689ab963db3b19540cba17315291/rpds_py-0.26.0-cp314-cp314-win_amd64.whl", hash = "sha256:b0afb8cdd034150d4d9f53926226ed27ad15b7f465e93d7468caaf5eafae0d37", size = 236083 }, 426 | { url = "https://files.pythonhosted.org/packages/47/af/9c4638994dd623d51c39892edd9d08e8be8220a4b7e874fa02c2d6e91955/rpds_py-0.26.0-cp314-cp314-win_arm64.whl", hash = "sha256:ca3f059f4ba485d90c8dc75cb5ca897e15325e4e609812ce57f896607c1c0867", size = 223291 }, 427 | { url = "https://files.pythonhosted.org/packages/4d/db/669a241144460474aab03e254326b32c42def83eb23458a10d163cb9b5ce/rpds_py-0.26.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:5afea17ab3a126006dc2f293b14ffc7ef3c85336cf451564a0515ed7648033da", size = 361445 }, 428 | { url = "https://files.pythonhosted.org/packages/3b/2d/133f61cc5807c6c2fd086a46df0eb8f63a23f5df8306ff9f6d0fd168fecc/rpds_py-0.26.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:69f0c0a3df7fd3a7eec50a00396104bb9a843ea6d45fcc31c2d5243446ffd7a7", size = 347206 }, 429 | { url = "https://files.pythonhosted.org/packages/05/bf/0e8fb4c05f70273469eecf82f6ccf37248558526a45321644826555db31b/rpds_py-0.26.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:801a71f70f9813e82d2513c9a96532551fce1e278ec0c64610992c49c04c2dad", size = 380330 }, 430 | { url = "https://files.pythonhosted.org/packages/d4/a8/060d24185d8b24d3923322f8d0ede16df4ade226a74e747b8c7c978e3dd3/rpds_py-0.26.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:df52098cde6d5e02fa75c1f6244f07971773adb4a26625edd5c18fee906fa84d", size = 392254 }, 431 | { url = "https://files.pythonhosted.org/packages/b9/7b/7c2e8a9ee3e6bc0bae26bf29f5219955ca2fbb761dca996a83f5d2f773fe/rpds_py-0.26.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9bc596b30f86dc6f0929499c9e574601679d0341a0108c25b9b358a042f51bca", size = 516094 }, 432 | { url = "https://files.pythonhosted.org/packages/75/d6/f61cafbed8ba1499b9af9f1777a2a199cd888f74a96133d8833ce5eaa9c5/rpds_py-0.26.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9dfbe56b299cf5875b68eb6f0ebaadc9cac520a1989cac0db0765abfb3709c19", size = 402889 }, 433 | { url = "https://files.pythonhosted.org/packages/92/19/c8ac0a8a8df2dd30cdec27f69298a5c13e9029500d6d76718130f5e5be10/rpds_py-0.26.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac64f4b2bdb4ea622175c9ab7cf09444e412e22c0e02e906978b3b488af5fde8", size = 384301 }, 434 | { url = "https://files.pythonhosted.org/packages/41/e1/6b1859898bc292a9ce5776016c7312b672da00e25cec74d7beced1027286/rpds_py-0.26.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:181ef9b6bbf9845a264f9aa45c31836e9f3c1f13be565d0d010e964c661d1e2b", size = 412891 }, 435 | { url = "https://files.pythonhosted.org/packages/ef/b9/ceb39af29913c07966a61367b3c08b4f71fad841e32c6b59a129d5974698/rpds_py-0.26.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:49028aa684c144ea502a8e847d23aed5e4c2ef7cadfa7d5eaafcb40864844b7a", size = 557044 }, 436 | { url = "https://files.pythonhosted.org/packages/2f/27/35637b98380731a521f8ec4f3fd94e477964f04f6b2f8f7af8a2d889a4af/rpds_py-0.26.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:e5d524d68a474a9688336045bbf76cb0def88549c1b2ad9dbfec1fb7cfbe9170", size = 585774 }, 437 | { url = "https://files.pythonhosted.org/packages/52/d9/3f0f105420fecd18551b678c9a6ce60bd23986098b252a56d35781b3e7e9/rpds_py-0.26.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:c1851f429b822831bd2edcbe0cfd12ee9ea77868f8d3daf267b189371671c80e", size = 554886 }, 438 | { url = "https://files.pythonhosted.org/packages/6b/c5/347c056a90dc8dd9bc240a08c527315008e1b5042e7a4cf4ac027be9d38a/rpds_py-0.26.0-cp314-cp314t-win32.whl", hash = "sha256:7bdb17009696214c3b66bb3590c6d62e14ac5935e53e929bcdbc5a495987a84f", size = 219027 }, 439 | { url = "https://files.pythonhosted.org/packages/75/04/5302cea1aa26d886d34cadbf2dc77d90d7737e576c0065f357b96dc7a1a6/rpds_py-0.26.0-cp314-cp314t-win_amd64.whl", hash = "sha256:f14440b9573a6f76b4ee4770c13f0b5921f71dde3b6fcb8dabbefd13b7fe05d7", size = 232821 }, 440 | { url = "https://files.pythonhosted.org/packages/ef/9a/1f033b0b31253d03d785b0cd905bc127e555ab496ea6b4c7c2e1f951f2fd/rpds_py-0.26.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3c0909c5234543ada2515c05dc08595b08d621ba919629e94427e8e03539c958", size = 373226 }, 441 | { url = "https://files.pythonhosted.org/packages/58/29/5f88023fd6aaaa8ca3c4a6357ebb23f6f07da6079093ccf27c99efce87db/rpds_py-0.26.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:c1fb0cda2abcc0ac62f64e2ea4b4e64c57dfd6b885e693095460c61bde7bb18e", size = 359230 }, 442 | { url = "https://files.pythonhosted.org/packages/6c/6c/13eaebd28b439da6964dde22712b52e53fe2824af0223b8e403249d10405/rpds_py-0.26.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:84d142d2d6cf9b31c12aa4878d82ed3b2324226270b89b676ac62ccd7df52d08", size = 382363 }, 443 | { url = "https://files.pythonhosted.org/packages/55/fc/3bb9c486b06da19448646f96147796de23c5811ef77cbfc26f17307b6a9d/rpds_py-0.26.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a547e21c5610b7e9093d870be50682a6a6cf180d6da0f42c47c306073bfdbbf6", size = 397146 }, 444 | { url = "https://files.pythonhosted.org/packages/15/18/9d1b79eb4d18e64ba8bba9e7dec6f9d6920b639f22f07ee9368ca35d4673/rpds_py-0.26.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:35e9a70a0f335371275cdcd08bc5b8051ac494dd58bff3bbfb421038220dc871", size = 514804 }, 445 | { url = "https://files.pythonhosted.org/packages/4f/5a/175ad7191bdbcd28785204621b225ad70e85cdfd1e09cc414cb554633b21/rpds_py-0.26.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0dfa6115c6def37905344d56fb54c03afc49104e2ca473d5dedec0f6606913b4", size = 402820 }, 446 | { url = "https://files.pythonhosted.org/packages/11/45/6a67ecf6d61c4d4aff4bc056e864eec4b2447787e11d1c2c9a0242c6e92a/rpds_py-0.26.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:313cfcd6af1a55a286a3c9a25f64af6d0e46cf60bc5798f1db152d97a216ff6f", size = 384567 }, 447 | { url = "https://files.pythonhosted.org/packages/a1/ba/16589da828732b46454c61858950a78fe4c931ea4bf95f17432ffe64b241/rpds_py-0.26.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f7bf2496fa563c046d05e4d232d7b7fd61346e2402052064b773e5c378bf6f73", size = 416520 }, 448 | { url = "https://files.pythonhosted.org/packages/81/4b/00092999fc7c0c266045e984d56b7314734cc400a6c6dc4d61a35f135a9d/rpds_py-0.26.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:aa81873e2c8c5aa616ab8e017a481a96742fdf9313c40f14338ca7dbf50cb55f", size = 559362 }, 449 | { url = "https://files.pythonhosted.org/packages/96/0c/43737053cde1f93ac4945157f7be1428724ab943e2132a0d235a7e161d4e/rpds_py-0.26.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:68ffcf982715f5b5b7686bdd349ff75d422e8f22551000c24b30eaa1b7f7ae84", size = 588113 }, 450 | { url = "https://files.pythonhosted.org/packages/46/46/8e38f6161466e60a997ed7e9951ae5de131dedc3cf778ad35994b4af823d/rpds_py-0.26.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:6188de70e190847bb6db3dc3981cbadff87d27d6fe9b4f0e18726d55795cee9b", size = 555429 }, 451 | { url = "https://files.pythonhosted.org/packages/2c/ac/65da605e9f1dd643ebe615d5bbd11b6efa1d69644fc4bf623ea5ae385a82/rpds_py-0.26.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:1c962145c7473723df9722ba4c058de12eb5ebedcb4e27e7d902920aa3831ee8", size = 231950 }, 452 | { url = "https://files.pythonhosted.org/packages/51/f2/b5c85b758a00c513bb0389f8fc8e61eb5423050c91c958cdd21843faa3e6/rpds_py-0.26.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f61a9326f80ca59214d1cceb0a09bb2ece5b2563d4e0cd37bfd5515c28510674", size = 373505 }, 453 | { url = "https://files.pythonhosted.org/packages/23/e0/25db45e391251118e915e541995bb5f5ac5691a3b98fb233020ba53afc9b/rpds_py-0.26.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:183f857a53bcf4b1b42ef0f57ca553ab56bdd170e49d8091e96c51c3d69ca696", size = 359468 }, 454 | { url = "https://files.pythonhosted.org/packages/0b/73/dd5ee6075bb6491be3a646b301dfd814f9486d924137a5098e61f0487e16/rpds_py-0.26.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:941c1cfdf4799d623cf3aa1d326a6b4fdb7a5799ee2687f3516738216d2262fb", size = 382680 }, 455 | { url = "https://files.pythonhosted.org/packages/2f/10/84b522ff58763a5c443f5bcedc1820240e454ce4e620e88520f04589e2ea/rpds_py-0.26.0-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72a8d9564a717ee291f554eeb4bfeafe2309d5ec0aa6c475170bdab0f9ee8e88", size = 397035 }, 456 | { url = "https://files.pythonhosted.org/packages/06/ea/8667604229a10a520fcbf78b30ccc278977dcc0627beb7ea2c96b3becef0/rpds_py-0.26.0-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:511d15193cbe013619dd05414c35a7dedf2088fcee93c6bbb7c77859765bd4e8", size = 514922 }, 457 | { url = "https://files.pythonhosted.org/packages/24/e6/9ed5b625c0661c4882fc8cdf302bf8e96c73c40de99c31e0b95ed37d508c/rpds_py-0.26.0-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aea1f9741b603a8d8fedb0ed5502c2bc0accbc51f43e2ad1337fe7259c2b77a5", size = 402822 }, 458 | { url = "https://files.pythonhosted.org/packages/8a/58/212c7b6fd51946047fb45d3733da27e2fa8f7384a13457c874186af691b1/rpds_py-0.26.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4019a9d473c708cf2f16415688ef0b4639e07abaa569d72f74745bbeffafa2c7", size = 384336 }, 459 | { url = "https://files.pythonhosted.org/packages/aa/f5/a40ba78748ae8ebf4934d4b88e77b98497378bc2c24ba55ebe87a4e87057/rpds_py-0.26.0-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:093d63b4b0f52d98ebae33b8c50900d3d67e0666094b1be7a12fffd7f65de74b", size = 416871 }, 460 | { url = "https://files.pythonhosted.org/packages/d5/a6/33b1fc0c9f7dcfcfc4a4353daa6308b3ece22496ceece348b3e7a7559a09/rpds_py-0.26.0-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:2abe21d8ba64cded53a2a677e149ceb76dcf44284202d737178afe7ba540c1eb", size = 559439 }, 461 | { url = "https://files.pythonhosted.org/packages/71/2d/ceb3f9c12f8cfa56d34995097f6cd99da1325642c60d1b6680dd9df03ed8/rpds_py-0.26.0-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:4feb7511c29f8442cbbc28149a92093d32e815a28aa2c50d333826ad2a20fdf0", size = 588380 }, 462 | { url = "https://files.pythonhosted.org/packages/c8/ed/9de62c2150ca8e2e5858acf3f4f4d0d180a38feef9fdab4078bea63d8dba/rpds_py-0.26.0-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:e99685fc95d386da368013e7fb4269dd39c30d99f812a8372d62f244f662709c", size = 555334 }, 463 | ] 464 | 465 | [[package]] 466 | name = "sniffio" 467 | version = "1.3.1" 468 | source = { registry = "https://pypi.org/simple" } 469 | sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372 } 470 | wheels = [ 471 | { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235 }, 472 | ] 473 | 474 | [[package]] 475 | name = "sse-starlette" 476 | version = "2.2.1" 477 | source = { registry = "https://pypi.org/simple" } 478 | dependencies = [ 479 | { name = "anyio" }, 480 | { name = "starlette" }, 481 | ] 482 | sdist = { url = "https://files.pythonhosted.org/packages/71/a4/80d2a11af59fe75b48230846989e93979c892d3a20016b42bb44edb9e398/sse_starlette-2.2.1.tar.gz", hash = "sha256:54470d5f19274aeed6b2d473430b08b4b379ea851d953b11d7f1c4a2c118b419", size = 17376 } 483 | wheels = [ 484 | { url = "https://files.pythonhosted.org/packages/d9/e0/5b8bd393f27f4a62461c5cf2479c75a2cc2ffa330976f9f00f5f6e4f50eb/sse_starlette-2.2.1-py3-none-any.whl", hash = "sha256:6410a3d3ba0c89e7675d4c273a301d64649c03a5ef1ca101f10b47f895fd0e99", size = 10120 }, 485 | ] 486 | 487 | [[package]] 488 | name = "starlette" 489 | version = "0.46.2" 490 | source = { registry = "https://pypi.org/simple" } 491 | dependencies = [ 492 | { name = "anyio" }, 493 | ] 494 | sdist = { url = "https://files.pythonhosted.org/packages/ce/20/08dfcd9c983f6a6f4a1000d934b9e6d626cff8d2eeb77a89a68eef20a2b7/starlette-0.46.2.tar.gz", hash = "sha256:7f7361f34eed179294600af672f565727419830b54b7b084efe44bb82d2fccd5", size = 2580846 } 495 | wheels = [ 496 | { url = "https://files.pythonhosted.org/packages/8b/0c/9d30a4ebeb6db2b25a841afbb80f6ef9a854fc3b41be131d249a977b4959/starlette-0.46.2-py3-none-any.whl", hash = "sha256:595633ce89f8ffa71a015caed34a5b2dc1c0cdb3f0f1fbd1e69339cf2abeec35", size = 72037 }, 497 | ] 498 | 499 | [[package]] 500 | name = "typing-extensions" 501 | version = "4.13.2" 502 | source = { registry = "https://pypi.org/simple" } 503 | sdist = { url = "https://files.pythonhosted.org/packages/f6/37/23083fcd6e35492953e8d2aaaa68b860eb422b34627b13f2ce3eb6106061/typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef", size = 106967 } 504 | wheels = [ 505 | { url = "https://files.pythonhosted.org/packages/8b/54/b1ae86c0973cc6f0210b53d508ca3641fb6d0c56823f288d108bc7ab3cc8/typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c", size = 45806 }, 506 | ] 507 | 508 | [[package]] 509 | name = "typing-inspection" 510 | version = "0.4.0" 511 | source = { registry = "https://pypi.org/simple" } 512 | dependencies = [ 513 | { name = "typing-extensions" }, 514 | ] 515 | sdist = { url = "https://files.pythonhosted.org/packages/82/5c/e6082df02e215b846b4b8c0b887a64d7d08ffaba30605502639d44c06b82/typing_inspection-0.4.0.tar.gz", hash = "sha256:9765c87de36671694a67904bf2c96e395be9c6439bb6c87b5142569dcdd65122", size = 76222 } 516 | wheels = [ 517 | { url = "https://files.pythonhosted.org/packages/31/08/aa4fdfb71f7de5176385bd9e90852eaf6b5d622735020ad600f2bab54385/typing_inspection-0.4.0-py3-none-any.whl", hash = "sha256:50e72559fcd2a6367a19f7a7e610e6afcb9fac940c650290eed893d61386832f", size = 14125 }, 518 | ] 519 | 520 | [[package]] 521 | name = "uvicorn" 522 | version = "0.34.2" 523 | source = { registry = "https://pypi.org/simple" } 524 | dependencies = [ 525 | { name = "click" }, 526 | { name = "h11" }, 527 | { name = "typing-extensions", marker = "python_full_version < '3.11'" }, 528 | ] 529 | sdist = { url = "https://files.pythonhosted.org/packages/a6/ae/9bbb19b9e1c450cf9ecaef06463e40234d98d95bf572fab11b4f19ae5ded/uvicorn-0.34.2.tar.gz", hash = "sha256:0e929828f6186353a80b58ea719861d2629d766293b6d19baf086ba31d4f3328", size = 76815 } 530 | wheels = [ 531 | { url = "https://files.pythonhosted.org/packages/b1/4b/4cef6ce21a2aaca9d852a6e84ef4f135d99fcd74fa75105e2fc0c8308acd/uvicorn-0.34.2-py3-none-any.whl", hash = "sha256:deb49af569084536d269fe0a6d67e3754f104cf03aba7c11c40f01aadf33c403", size = 62483 }, 532 | ] 533 | --------------------------------------------------------------------------------