├── .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 | 
9 | 
10 | 
11 | [](https://smithery.ai/protocol/cli-mcp-server)
12 | [](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 |
--------------------------------------------------------------------------------