a digital entity named phi that roams bsky

feat: mcp-based architecture foundation (phase 1)

This is the first phase of simplifying phi to use MCP (Model Context Protocol).

## What's New

- **Simple SQLite memory** (src/bot/memory.py)
- Plain text storage, no vector embeddings
- Two tables: threads (JSON conversation history) and user_memories
- Completely interpretable - just open the database and read

- **MCP-enabled agent** (src/bot/agent.py)
- PydanticAI Agent with ATProto MCP server as toolset
- Agent has direct access to ATProto tools: post, like, repost, etc.
- Returns structured Response (action, text, reason)

- **ATProto MCP server** (src/bot/atproto_mcp/)
- Vendored from fastmcp examples
- Configured to use existing BLUESKY_* env vars
- Provides all Bluesky/ATProto operations as MCP tools

- **Updated dependencies**
- Added: fastmcp, websockets
- Removed: turbopuffer, openai (no longer needed)

## Philosophy

Replacing over-engineered complexity with simple, working, interpretable systems.

## Next Steps

- Phase 2: Integrate new agent into main.py and poller
- Phase 3: Delete old cruft (namespace_memory, approval system, etc.)
- Phase 4: Test end-to-end

See sandbox/REFACTOR_PROGRESS.md for full details.

+1819 -154
+2 -2
pyproject.toml
··· 9 9 "anthropic", 10 10 "atproto", 11 11 "fastapi", 12 + "fastmcp>=0.8.0", 12 13 "httpx", 13 - "openai", 14 14 "pydantic-ai", 15 15 "pydantic-settings", 16 16 "rich", 17 - "turbopuffer", 18 17 "uvicorn", 18 + "websockets>=15.0.1", 19 19 ] 20 20 21 21 [tool.hatch.version]
+140
sandbox/REFACTOR_PROGRESS.md
··· 1 + # MCP Refactor Progress 2 + 3 + ## Branch: `mcp-refactor` 4 + 5 + ## Completed ✅ 6 + 7 + ### Phase 1: Foundation 8 + 1. **Cloned and studied reference projects** 9 + - `sandbox/prefect-mcp-server` - Learned PydanticAI + MCP patterns 10 + - Understood how MCP servers work as toolsets for PydanticAI agents 11 + 12 + 2. **Created simplified memory system** (`src/bot/memory.py`) 13 + - Single SQLite database (threads.db) 14 + - Plain text storage - no embeddings, no vector search 15 + - Two tables: 16 + - `threads` - Full conversation history per thread (JSON) 17 + - `user_memories` - Simple facts about users 18 + - Completely interpretable - you can open the db and read everything 19 + 20 + 3. **Integrated ATProto MCP server** 21 + - Copied from `.eggs/fastmcp/examples/atproto_mcp` → `src/bot/atproto_mcp` 22 + - Updated settings to use existing env vars (BLUESKY_HANDLE, etc.) 23 + - Server provides tools: post(), like(), repost(), follow(), search(), create_thread() 24 + 25 + 4. **Created MCP-enabled agent** (`src/bot/agent.py`) 26 + - PydanticAI Agent with ATProto MCP tools as a toolset 27 + - Loads personality from `personalities/phi.md` 28 + - Integrates with memory system 29 + - Returns structured Response (action, text, reason) 30 + 31 + 5. **Updated dependencies** 32 + - ✅ Added: `fastmcp>=0.8.0`, `websockets>=15.0.1` 33 + - ❌ Removed: `turbopuffer`, `openai` (no longer needed for memory) 34 + 35 + ## What Changed 36 + 37 + ### Before (Complex) 38 + - **Memory**: TurboPuffer + OpenAI embeddings + semantic search 39 + - **Agent**: Custom response generator with manual action interpretation 40 + - **AT Protocol**: Direct client calls scattered throughout codebase 41 + - **Personality**: Dynamic loading from TurboPuffer 42 + - **Self-modification**: Complex approval system with DM workflow 43 + 44 + ### After (Simple) 45 + - **Memory**: SQLite with plain text (interpretable!) 46 + - **Agent**: PydanticAI with MCP tools (agent decides actions) 47 + - **AT Protocol**: MCP server provides all tools 48 + - **Personality**: Static file loading 49 + - **Self-modification**: Removed (cruft) 50 + 51 + ## How It Works Now 52 + 53 + ```python 54 + # Create agent with memory 55 + memory = Memory() 56 + agent = PhiAgent(memory) 57 + 58 + # Process a mention 59 + response = await agent.process_mention( 60 + mention_text="hey phi!", 61 + author_handle="user.bsky.social", 62 + thread_uri="at://did/post/123" 63 + ) 64 + 65 + # Agent returns: Response(action="reply", text="...", reason="...") 66 + # If action is "reply", agent can call MCP tool: post(text="...", reply_to="...") 67 + ``` 68 + 69 + The agent has access to all ATProto MCP tools and can decide: 70 + - Should I reply, like, or ignore this? 71 + - If replying, what should I say? 72 + - Should I use other tools (repost, follow, etc.)? 73 + 74 + ## Next Steps 75 + 76 + ### Phase 2: Integration (Not Started) 77 + 1. Update `src/bot/main.py` to use new agent 78 + 2. Simplify `src/bot/services/notification_poller.py` 79 + 3. Remove old response_generator.py 80 + 4. Test end-to-end 81 + 82 + ### Phase 3: Cleanup (Not Started) 83 + 1. Delete cruft: 84 + - `src/bot/ui/` (context visualization) 85 + - `src/bot/personality/editor.py` (approval system) 86 + - `src/bot/core/dm_approval.py` 87 + - `src/bot/memory/namespace_memory.py` 88 + - `src/bot/agents/anthropic_agent.py` (replaced by agent.py) 89 + 2. Update database.py to remove approval tables 90 + 3. Update tests 91 + 4. Update README.md and documentation 92 + 93 + ### Phase 4: Verification (Not Started) 94 + 1. Run the bot and test mentions 95 + 2. Verify thread memory works 96 + 3. Verify user memory works 97 + 4. Ensure online/offline status still works 98 + 99 + ## Testing 100 + 101 + Test script created: `sandbox/test_new_agent.py` 102 + 103 + ```bash 104 + uv run python sandbox/test_new_agent.py 105 + ``` 106 + 107 + ## Key Files 108 + 109 + ### New 110 + - `src/bot/memory.py` - Simple SQLite memory 111 + - `src/bot/agent.py` - MCP-enabled PydanticAI agent 112 + - `src/bot/atproto_mcp/` - ATProto MCP server (vendored) 113 + 114 + ### Modified 115 + - `pyproject.toml` - Updated dependencies 116 + 117 + ### To Be Deleted 118 + - `src/bot/memory/namespace_memory.py` 119 + - `src/bot/agents/anthropic_agent.py` 120 + - `src/bot/response_generator.py` 121 + - `src/bot/ui/` 122 + - `src/bot/personality/editor.py` 123 + - `src/bot/core/dm_approval.py` 124 + 125 + ## Philosophy 126 + 127 + **Before**: Over-engineered for capabilities we might want someday 128 + **After**: Simple, working, interpretable system that does what we need today 129 + 130 + The memory is now something you can: 131 + 1. Open with any SQLite browser 132 + 2. Read and understand immediately 133 + 3. Debug by just looking at the tables 134 + 4. Migrate or export trivially 135 + 136 + No more: 137 + - Vector embeddings you can't see 138 + - Complex namespace hierarchies 139 + - Approval workflows for every personality change 140 + - Multiple overlapping memory systems
+32
sandbox/test_new_agent.py
··· 1 + """Test the new MCP-enabled agent.""" 2 + 3 + import asyncio 4 + 5 + from bot.agent import PhiAgent 6 + from bot.memory import Memory 7 + 8 + 9 + async def main(): 10 + """Test basic agent functionality.""" 11 + # Create memory and agent 12 + memory = Memory() 13 + agent = PhiAgent(memory) 14 + 15 + # Test a simple interaction 16 + response = await agent.process_mention( 17 + mention_text="hey phi, what are you?", 18 + author_handle="test.user", 19 + thread_uri="at://test/thread/123", 20 + ) 21 + 22 + print(f"Action: {response.action}") 23 + print(f"Text: {response.text}") 24 + print(f"Reason: {response.reason}") 25 + 26 + # Check memory was stored 27 + context = memory.get_thread_context("at://test/thread/123") 28 + print(f"\nThread context:\n{context}") 29 + 30 + 31 + if __name__ == "__main__": 32 + asyncio.run(main())
+80
src/bot/agent.py
··· 1 + """MCP-enabled agent for phi using PydanticAI.""" 2 + 3 + import logging 4 + from pathlib import Path 5 + 6 + from pydantic import BaseModel 7 + from pydantic_ai import Agent 8 + 9 + from bot.atproto_mcp.server import atproto_mcp 10 + from bot.config import settings 11 + from bot.memory import Memory 12 + 13 + logger = logging.getLogger("bot.agent") 14 + 15 + 16 + class Response(BaseModel): 17 + """Agent response indicating what action to take.""" 18 + 19 + action: str # "reply", "like", "ignore" 20 + text: str | None = None 21 + reason: str | None = None 22 + 23 + 24 + class PhiAgent: 25 + """phi - an MCP-enabled agent for Bluesky.""" 26 + 27 + def __init__(self, memory: Memory | None = None): 28 + self.memory = memory or Memory() 29 + 30 + # Load personality 31 + personality_path = Path(settings.personality_file) 32 + self.personality = personality_path.read_text() 33 + 34 + # Create PydanticAI agent with ATProto MCP tools 35 + self.agent = Agent[dict, Response]( 36 + name="phi", 37 + model="anthropic:claude-3-5-haiku-latest", 38 + system_prompt=self.personality, 39 + output_type=Response, 40 + deps_type=dict, 41 + toolsets=[atproto_mcp], # ATProto MCP tools available 42 + ) 43 + 44 + logger.info("✅ phi agent initialized with ATProto MCP tools") 45 + 46 + async def process_mention( 47 + self, 48 + mention_text: str, 49 + author_handle: str, 50 + thread_uri: str | None = None, 51 + ) -> Response: 52 + """Process a mention and decide how to respond.""" 53 + # Build context from memory 54 + if thread_uri: 55 + context = self.memory.build_full_context(thread_uri, author_handle) 56 + else: 57 + context = self.memory.get_user_context(author_handle) 58 + 59 + # Build prompt 60 + prompt_parts = [] 61 + if context and context != "No prior context available.": 62 + prompt_parts.append(context) 63 + prompt_parts.append("\nNew message:") 64 + 65 + prompt_parts.append(f"@{author_handle} said: {mention_text}") 66 + prompt = "\n".join(prompt_parts) 67 + 68 + # Run agent 69 + logger.info(f"🤖 Processing mention from @{author_handle}") 70 + result = await self.agent.run(prompt, deps={"thread_uri": thread_uri}) 71 + 72 + # Store in memory if replying 73 + if thread_uri and result.output.action == "reply": 74 + self.memory.add_thread_message(thread_uri, author_handle, mention_text) 75 + if result.output.text: 76 + self.memory.add_thread_message( 77 + thread_uri, settings.bluesky_handle, result.output.text 78 + ) 79 + 80 + return result.output
+3
src/bot/atproto_mcp/__init__.py
··· 1 + from atproto_mcp.settings import settings 2 + 3 + __all__ = ["settings"]
+9
src/bot/atproto_mcp/__main__.py
··· 1 + from atproto_mcp.server import atproto_mcp 2 + 3 + 4 + def main(): 5 + atproto_mcp.run() 6 + 7 + 8 + if __name__ == "__main__": 9 + main()
+20
src/bot/atproto_mcp/_atproto/__init__.py
··· 1 + """Private ATProto implementation module.""" 2 + 3 + from ._client import get_client 4 + from ._posts import create_post, create_thread 5 + from ._profile import get_profile_info 6 + from ._read import fetch_notifications, fetch_timeline, search_for_posts 7 + from ._social import follow_user_by_handle, like_post_by_uri, repost_by_uri 8 + 9 + __all__ = [ 10 + "get_client", 11 + "get_profile_info", 12 + "create_post", 13 + "create_thread", 14 + "fetch_timeline", 15 + "search_for_posts", 16 + "fetch_notifications", 17 + "follow_user_by_handle", 18 + "like_post_by_uri", 19 + "repost_by_uri", 20 + ]
+16
src/bot/atproto_mcp/_atproto/_client.py
··· 1 + """ATProto client management.""" 2 + 3 + from atproto import Client 4 + 5 + from atproto_mcp.settings import settings 6 + 7 + _client: Client | None = None 8 + 9 + 10 + def get_client() -> Client: 11 + """Get or create an authenticated ATProto client.""" 12 + global _client 13 + if _client is None: 14 + _client = Client() 15 + _client.login(settings.atproto_handle, settings.atproto_password) 16 + return _client
+385
src/bot/atproto_mcp/_atproto/_posts.py
··· 1 + """Unified posting functionality.""" 2 + 3 + import time 4 + from datetime import datetime 5 + 6 + from atproto import models 7 + 8 + from atproto_mcp.types import ( 9 + PostResult, 10 + RichTextLink, 11 + RichTextMention, 12 + ThreadPost, 13 + ThreadResult, 14 + ) 15 + 16 + from ._client import get_client 17 + 18 + 19 + def create_post( 20 + text: str, 21 + images: list[str] | None = None, 22 + image_alts: list[str] | None = None, 23 + links: list[RichTextLink] | None = None, 24 + mentions: list[RichTextMention] | None = None, 25 + reply_to: str | None = None, 26 + reply_root: str | None = None, 27 + quote: str | None = None, 28 + ) -> PostResult: 29 + """Create a unified post with optional features. 30 + 31 + Args: 32 + text: Post text (max 300 chars) 33 + images: URLs of images to attach (max 4) 34 + image_alts: Alt text for images 35 + links: Links to embed in rich text 36 + mentions: User mentions to embed 37 + reply_to: URI of post to reply to 38 + reply_root: URI of thread root (defaults to reply_to) 39 + quote: URI of post to quote 40 + """ 41 + try: 42 + client = get_client() 43 + facets = [] 44 + embed = None 45 + reply_ref = None 46 + 47 + # Handle rich text facets (links and mentions) 48 + if links or mentions: 49 + facets = _build_facets(text, links, mentions, client) 50 + 51 + # Handle replies 52 + if reply_to: 53 + reply_ref = _build_reply_ref(reply_to, reply_root, client) 54 + 55 + # Handle quotes and images 56 + if quote and images: 57 + # Quote with images - create record with media embed 58 + embed = _build_quote_with_images_embed(quote, images, image_alts, client) 59 + elif quote: 60 + # Quote only 61 + embed = _build_quote_embed(quote, client) 62 + elif images: 63 + # Images only - use send_images for proper handling 64 + return _send_images(text, images, image_alts, facets, reply_ref, client) 65 + 66 + # Send the post 67 + post = client.send_post( 68 + text=text, 69 + facets=facets if facets else None, 70 + embed=embed, 71 + reply_to=reply_ref, 72 + ) 73 + 74 + return PostResult( 75 + success=True, 76 + uri=post.uri, 77 + cid=post.cid, 78 + text=text, 79 + created_at=datetime.now().isoformat(), 80 + error=None, 81 + ) 82 + except Exception as e: 83 + return PostResult( 84 + success=False, 85 + uri=None, 86 + cid=None, 87 + text=None, 88 + created_at=None, 89 + error=str(e), 90 + ) 91 + 92 + 93 + def _build_facets( 94 + text: str, 95 + links: list[RichTextLink] | None, 96 + mentions: list[RichTextMention] | None, 97 + client, 98 + ): 99 + """Build facets for rich text formatting.""" 100 + facets = [] 101 + 102 + # Process links 103 + if links: 104 + for link in links: 105 + start = text.find(link["text"]) 106 + if start == -1: 107 + continue 108 + end = start + len(link["text"]) 109 + 110 + facets.append( 111 + models.AppBskyRichtextFacet.Main( 112 + features=[models.AppBskyRichtextFacet.Link(uri=link["url"])], 113 + index=models.AppBskyRichtextFacet.ByteSlice( 114 + byte_start=len(text[:start].encode("UTF-8")), 115 + byte_end=len(text[:end].encode("UTF-8")), 116 + ), 117 + ) 118 + ) 119 + 120 + # Process mentions 121 + if mentions: 122 + for mention in mentions: 123 + display_text = mention.get("display_text") or f"@{mention['handle']}" 124 + start = text.find(display_text) 125 + if start == -1: 126 + continue 127 + end = start + len(display_text) 128 + 129 + # Resolve handle to DID 130 + resolved = client.app.bsky.actor.search_actors( 131 + params={"q": mention["handle"], "limit": 1} 132 + ) 133 + if not resolved.actors: 134 + continue 135 + 136 + did = resolved.actors[0].did 137 + facets.append( 138 + models.AppBskyRichtextFacet.Main( 139 + features=[models.AppBskyRichtextFacet.Mention(did=did)], 140 + index=models.AppBskyRichtextFacet.ByteSlice( 141 + byte_start=len(text[:start].encode("UTF-8")), 142 + byte_end=len(text[:end].encode("UTF-8")), 143 + ), 144 + ) 145 + ) 146 + 147 + return facets 148 + 149 + 150 + def _build_reply_ref(reply_to: str, reply_root: str | None, client): 151 + """Build reply reference.""" 152 + # Get parent post to extract CID 153 + parent_post = client.app.bsky.feed.get_posts(params={"uris": [reply_to]}) 154 + if not parent_post.posts: 155 + raise ValueError("Parent post not found") 156 + 157 + parent_cid = parent_post.posts[0].cid 158 + parent_ref = models.ComAtprotoRepoStrongRef.Main(uri=reply_to, cid=parent_cid) 159 + 160 + # If no root_uri provided, parent is the root 161 + if reply_root is None: 162 + root_ref = parent_ref 163 + else: 164 + # Get root post CID 165 + root_post = client.app.bsky.feed.get_posts(params={"uris": [reply_root]}) 166 + if not root_post.posts: 167 + raise ValueError("Root post not found") 168 + root_cid = root_post.posts[0].cid 169 + root_ref = models.ComAtprotoRepoStrongRef.Main(uri=reply_root, cid=root_cid) 170 + 171 + return models.AppBskyFeedPost.ReplyRef(parent=parent_ref, root=root_ref) 172 + 173 + 174 + def _build_quote_embed(quote_uri: str, client): 175 + """Build quote embed.""" 176 + # Get the post to quote 177 + quoted_post = client.app.bsky.feed.get_posts(params={"uris": [quote_uri]}) 178 + if not quoted_post.posts: 179 + raise ValueError("Quoted post not found") 180 + 181 + # Create strong ref for the quoted post 182 + quoted_cid = quoted_post.posts[0].cid 183 + quoted_ref = models.ComAtprotoRepoStrongRef.Main(uri=quote_uri, cid=quoted_cid) 184 + 185 + # Create the embed 186 + return models.AppBskyEmbedRecord.Main(record=quoted_ref) 187 + 188 + 189 + def _build_quote_with_images_embed( 190 + quote_uri: str, image_urls: list[str], image_alts: list[str] | None, client 191 + ): 192 + """Build quote embed with images.""" 193 + import httpx 194 + 195 + # Get the quoted post 196 + quoted_post = client.app.bsky.feed.get_posts(params={"uris": [quote_uri]}) 197 + if not quoted_post.posts: 198 + raise ValueError("Quoted post not found") 199 + 200 + quoted_cid = quoted_post.posts[0].cid 201 + quoted_ref = models.ComAtprotoRepoStrongRef.Main(uri=quote_uri, cid=quoted_cid) 202 + 203 + # Download and upload images 204 + images = [] 205 + alts = image_alts or [""] * len(image_urls) 206 + 207 + for i, url in enumerate(image_urls[:4]): 208 + response = httpx.get(url, follow_redirects=True) 209 + response.raise_for_status() 210 + 211 + # Upload to blob storage 212 + upload = client.upload_blob(response.content) 213 + images.append( 214 + models.AppBskyEmbedImages.Image( 215 + alt=alts[i] if i < len(alts) else "", 216 + image=upload.blob, 217 + ) 218 + ) 219 + 220 + # Create record with media embed 221 + return models.AppBskyEmbedRecordWithMedia.Main( 222 + record=models.AppBskyEmbedRecord.Main(record=quoted_ref), 223 + media=models.AppBskyEmbedImages.Main(images=images), 224 + ) 225 + 226 + 227 + def _send_images( 228 + text: str, 229 + image_urls: list[str], 230 + image_alts: list[str] | None, 231 + facets, 232 + reply_ref, 233 + client, 234 + ): 235 + """Send post with images using the client's send_images method.""" 236 + import httpx 237 + 238 + # Ensure alt_texts has same length as images 239 + if image_alts is None: 240 + image_alts = [""] * len(image_urls) 241 + elif len(image_alts) < len(image_urls): 242 + image_alts.extend([""] * (len(image_urls) - len(image_alts))) 243 + 244 + image_data = [] 245 + alts = [] 246 + for i, url in enumerate(image_urls[:4]): # Max 4 images 247 + # Download image (follow redirects) 248 + response = httpx.get(url, follow_redirects=True) 249 + response.raise_for_status() 250 + 251 + image_data.append(response.content) 252 + alts.append(image_alts[i] if i < len(image_alts) else "") 253 + 254 + # Send post with images 255 + # Note: send_images doesn't support facets or reply_to directly 256 + # So we need to use send_post with manual image upload if we have those 257 + if facets or reply_ref: 258 + # Manual image upload 259 + images = [] 260 + for i, data in enumerate(image_data): 261 + upload = client.upload_blob(data) 262 + images.append( 263 + models.AppBskyEmbedImages.Image( 264 + alt=alts[i], 265 + image=upload.blob, 266 + ) 267 + ) 268 + 269 + embed = models.AppBskyEmbedImages.Main(images=images) 270 + post = client.send_post( 271 + text=text, 272 + facets=facets if facets else None, 273 + embed=embed, 274 + reply_to=reply_ref, 275 + ) 276 + else: 277 + # Use simple send_images 278 + post = client.send_images( 279 + text=text, 280 + images=image_data, 281 + image_alts=alts, 282 + ) 283 + 284 + return PostResult( 285 + success=True, 286 + uri=post.uri, 287 + cid=post.cid, 288 + text=text, 289 + created_at=datetime.now().isoformat(), 290 + error=None, 291 + ) 292 + 293 + 294 + def create_thread(posts: list[ThreadPost]) -> ThreadResult: 295 + """Create a thread of posts with automatic linking. 296 + 297 + Args: 298 + posts: List of posts to create as a thread. First post is the root. 299 + """ 300 + if not posts: 301 + return ThreadResult( 302 + success=False, 303 + thread_uri=None, 304 + post_uris=[], 305 + post_count=0, 306 + error="No posts provided", 307 + ) 308 + 309 + try: 310 + post_uris = [] 311 + root_uri = None 312 + parent_uri = None 313 + 314 + for i, post_data in enumerate(posts): 315 + # First post is the root 316 + if i == 0: 317 + result = create_post( 318 + text=post_data["text"], 319 + images=post_data.get("images"), 320 + image_alts=post_data.get("image_alts"), 321 + links=post_data.get("links"), 322 + mentions=post_data.get("mentions"), 323 + quote=post_data.get("quote"), 324 + ) 325 + 326 + if not result["success"]: 327 + return ThreadResult( 328 + success=False, 329 + thread_uri=None, 330 + post_uris=post_uris, 331 + post_count=len(post_uris), 332 + error=f"Failed to create root post: {result['error']}", 333 + ) 334 + 335 + root_uri = result["uri"] 336 + parent_uri = root_uri 337 + post_uris.append(root_uri) 338 + 339 + # Small delay to ensure post is indexed 340 + time.sleep(0.5) 341 + else: 342 + # Subsequent posts reply to the previous one 343 + result = create_post( 344 + text=post_data["text"], 345 + images=post_data.get("images"), 346 + image_alts=post_data.get("image_alts"), 347 + links=post_data.get("links"), 348 + mentions=post_data.get("mentions"), 349 + quote=post_data.get("quote"), 350 + reply_to=parent_uri, 351 + reply_root=root_uri, 352 + ) 353 + 354 + if not result["success"]: 355 + return ThreadResult( 356 + success=False, 357 + thread_uri=root_uri, 358 + post_uris=post_uris, 359 + post_count=len(post_uris), 360 + error=f"Failed to create post {i + 1}: {result['error']}", 361 + ) 362 + 363 + parent_uri = result["uri"] 364 + post_uris.append(parent_uri) 365 + 366 + # Small delay between posts 367 + if i < len(posts) - 1: 368 + time.sleep(0.5) 369 + 370 + return ThreadResult( 371 + success=True, 372 + thread_uri=root_uri, 373 + post_uris=post_uris, 374 + post_count=len(post_uris), 375 + error=None, 376 + ) 377 + 378 + except Exception as e: 379 + return ThreadResult( 380 + success=False, 381 + thread_uri=None, 382 + post_uris=post_uris, 383 + post_count=len(post_uris), 384 + error=str(e), 385 + )
+33
src/bot/atproto_mcp/_atproto/_profile.py
··· 1 + """Profile-related operations.""" 2 + 3 + from atproto_mcp.types import ProfileInfo 4 + 5 + from ._client import get_client 6 + 7 + 8 + def get_profile_info() -> ProfileInfo: 9 + """Get profile information for the authenticated user.""" 10 + try: 11 + client = get_client() 12 + profile = client.get_profile(client.me.did) 13 + return ProfileInfo( 14 + connected=True, 15 + handle=profile.handle, 16 + display_name=profile.display_name, 17 + did=client.me.did, 18 + followers=profile.followers_count, 19 + following=profile.follows_count, 20 + posts=profile.posts_count, 21 + error=None, 22 + ) 23 + except Exception as e: 24 + return ProfileInfo( 25 + connected=False, 26 + handle=None, 27 + display_name=None, 28 + did=None, 29 + followers=None, 30 + following=None, 31 + posts=None, 32 + error=str(e), 33 + )
+124
src/bot/atproto_mcp/_atproto/_read.py
··· 1 + """Read-only operations for timeline, search, and notifications.""" 2 + 3 + from atproto_mcp.types import ( 4 + Notification, 5 + NotificationsResult, 6 + Post, 7 + SearchResult, 8 + TimelineResult, 9 + ) 10 + 11 + from ._client import get_client 12 + 13 + 14 + def fetch_timeline(limit: int = 10) -> TimelineResult: 15 + """Fetch the authenticated user's timeline.""" 16 + try: 17 + client = get_client() 18 + timeline = client.get_timeline(limit=limit) 19 + 20 + posts = [] 21 + for feed_view in timeline.feed: 22 + post = feed_view.post 23 + posts.append( 24 + Post( 25 + uri=post.uri, 26 + cid=post.cid, 27 + text=post.record.text if hasattr(post.record, "text") else "", 28 + author=post.author.handle, 29 + created_at=post.record.created_at, 30 + likes=post.like_count or 0, 31 + reposts=post.repost_count or 0, 32 + replies=post.reply_count or 0, 33 + ) 34 + ) 35 + 36 + return TimelineResult( 37 + success=True, 38 + posts=posts, 39 + count=len(posts), 40 + error=None, 41 + ) 42 + except Exception as e: 43 + return TimelineResult( 44 + success=False, 45 + posts=[], 46 + count=0, 47 + error=str(e), 48 + ) 49 + 50 + 51 + def search_for_posts(query: str, limit: int = 10) -> SearchResult: 52 + """Search for posts containing specific text.""" 53 + try: 54 + client = get_client() 55 + search_results = client.app.bsky.feed.search_posts( 56 + params={"q": query, "limit": limit} 57 + ) 58 + 59 + posts = [] 60 + for post in search_results.posts: 61 + posts.append( 62 + Post( 63 + uri=post.uri, 64 + cid=post.cid, 65 + text=post.record.text if hasattr(post.record, "text") else "", 66 + author=post.author.handle, 67 + created_at=post.record.created_at, 68 + likes=post.like_count or 0, 69 + reposts=post.repost_count or 0, 70 + replies=post.reply_count or 0, 71 + ) 72 + ) 73 + 74 + return SearchResult( 75 + success=True, 76 + query=query, 77 + posts=posts, 78 + count=len(posts), 79 + error=None, 80 + ) 81 + except Exception as e: 82 + return SearchResult( 83 + success=False, 84 + query=query, 85 + posts=[], 86 + count=0, 87 + error=str(e), 88 + ) 89 + 90 + 91 + def fetch_notifications(limit: int = 10) -> NotificationsResult: 92 + """Fetch recent notifications.""" 93 + try: 94 + client = get_client() 95 + notifs = client.app.bsky.notification.list_notifications( 96 + params={"limit": limit} 97 + ) 98 + 99 + notifications = [] 100 + for notif in notifs.notifications: 101 + notifications.append( 102 + Notification( 103 + uri=notif.uri, 104 + cid=notif.cid, 105 + author=notif.author.handle, 106 + reason=notif.reason, 107 + is_read=notif.is_read, 108 + indexed_at=notif.indexed_at, 109 + ) 110 + ) 111 + 112 + return NotificationsResult( 113 + success=True, 114 + notifications=notifications, 115 + count=len(notifications), 116 + error=None, 117 + ) 118 + except Exception as e: 119 + return NotificationsResult( 120 + success=False, 121 + notifications=[], 122 + count=0, 123 + error=str(e), 124 + )
+108
src/bot/atproto_mcp/_atproto/_social.py
··· 1 + """Social actions like follow, like, and repost.""" 2 + 3 + from atproto_mcp.types import FollowResult, LikeResult, RepostResult 4 + 5 + from ._client import get_client 6 + 7 + 8 + def follow_user_by_handle(handle: str) -> FollowResult: 9 + """Follow a user by their handle.""" 10 + try: 11 + client = get_client() 12 + # Search for the user to get their DID 13 + results = client.app.bsky.actor.search_actors(params={"q": handle, "limit": 1}) 14 + if not results.actors: 15 + return FollowResult( 16 + success=False, 17 + did=None, 18 + handle=None, 19 + uri=None, 20 + error=f"User @{handle} not found", 21 + ) 22 + 23 + actor = results.actors[0] 24 + # Create the follow 25 + follow = client.follow(actor.did) 26 + return FollowResult( 27 + success=True, 28 + did=actor.did, 29 + handle=actor.handle, 30 + uri=follow.uri, 31 + error=None, 32 + ) 33 + except Exception as e: 34 + return FollowResult( 35 + success=False, 36 + did=None, 37 + handle=None, 38 + uri=None, 39 + error=str(e), 40 + ) 41 + 42 + 43 + def like_post_by_uri(uri: str) -> LikeResult: 44 + """Like a post by its AT URI.""" 45 + try: 46 + client = get_client() 47 + # Parse the URI to get the components 48 + # URI format: at://did:plc:xxx/app.bsky.feed.post/yyy 49 + parts = uri.replace("at://", "").split("/") 50 + if len(parts) != 3 or parts[1] != "app.bsky.feed.post": 51 + raise ValueError("Invalid post URI format") 52 + 53 + # Get the post to retrieve its CID 54 + post = client.app.bsky.feed.get_posts(params={"uris": [uri]}) 55 + if not post.posts: 56 + raise ValueError("Post not found") 57 + 58 + cid = post.posts[0].cid 59 + 60 + # Now like the post with both URI and CID 61 + like = client.like(uri, cid) 62 + return LikeResult( 63 + success=True, 64 + liked_uri=uri, 65 + like_uri=like.uri, 66 + error=None, 67 + ) 68 + except Exception as e: 69 + return LikeResult( 70 + success=False, 71 + liked_uri=None, 72 + like_uri=None, 73 + error=str(e), 74 + ) 75 + 76 + 77 + def repost_by_uri(uri: str) -> RepostResult: 78 + """Repost a post by its AT URI.""" 79 + try: 80 + client = get_client() 81 + # Parse the URI to get the components 82 + # URI format: at://did:plc:xxx/app.bsky.feed.post/yyy 83 + parts = uri.replace("at://", "").split("/") 84 + if len(parts) != 3 or parts[1] != "app.bsky.feed.post": 85 + raise ValueError("Invalid post URI format") 86 + 87 + # Get the post to retrieve its CID 88 + post = client.app.bsky.feed.get_posts(params={"uris": [uri]}) 89 + if not post.posts: 90 + raise ValueError("Post not found") 91 + 92 + cid = post.posts[0].cid 93 + 94 + # Now repost with both URI and CID 95 + repost = client.repost(uri, cid) 96 + return RepostResult( 97 + success=True, 98 + reposted_uri=uri, 99 + repost_uri=repost.uri, 100 + error=None, 101 + ) 102 + except Exception as e: 103 + return RepostResult( 104 + success=False, 105 + reposted_uri=None, 106 + repost_uri=None, 107 + error=str(e), 108 + )
src/bot/atproto_mcp/py.typed

This is a binary file and will not be displayed.

+154
src/bot/atproto_mcp/server.py
··· 1 + """ATProto MCP Server - Public API exposing Bluesky tools and resources.""" 2 + 3 + from typing import Annotated 4 + 5 + from pydantic import Field 6 + 7 + from atproto_mcp import _atproto 8 + from atproto_mcp.settings import settings 9 + from atproto_mcp.types import ( 10 + FollowResult, 11 + LikeResult, 12 + NotificationsResult, 13 + PostResult, 14 + ProfileInfo, 15 + RepostResult, 16 + RichTextLink, 17 + RichTextMention, 18 + SearchResult, 19 + ThreadPost, 20 + ThreadResult, 21 + TimelineResult, 22 + ) 23 + from fastmcp import FastMCP 24 + 25 + atproto_mcp = FastMCP( 26 + "ATProto MCP Server", 27 + dependencies=[ 28 + "atproto_mcp@git+https://github.com/jlowin/fastmcp.git#subdirectory=examples/atproto_mcp", 29 + ], 30 + ) 31 + 32 + 33 + # Resources - read-only operations 34 + @atproto_mcp.resource("atproto://profile/status") 35 + def atproto_status() -> ProfileInfo: 36 + """Check the status of the ATProto connection and current user profile.""" 37 + return _atproto.get_profile_info() 38 + 39 + 40 + @atproto_mcp.resource("atproto://timeline") 41 + def get_timeline() -> TimelineResult: 42 + """Get the authenticated user's timeline feed.""" 43 + return _atproto.fetch_timeline(settings.atproto_timeline_default_limit) 44 + 45 + 46 + @atproto_mcp.resource("atproto://notifications") 47 + def get_notifications() -> NotificationsResult: 48 + """Get recent notifications for the authenticated user.""" 49 + return _atproto.fetch_notifications(settings.atproto_notifications_default_limit) 50 + 51 + 52 + # Tools - actions that modify state 53 + @atproto_mcp.tool 54 + def post( 55 + text: Annotated[ 56 + str, Field(max_length=300, description="The text content of the post") 57 + ], 58 + images: Annotated[ 59 + list[str] | None, 60 + Field(max_length=4, description="URLs of images to attach (max 4)"), 61 + ] = None, 62 + image_alts: Annotated[ 63 + list[str] | None, Field(description="Alt text for each image") 64 + ] = None, 65 + links: Annotated[ 66 + list[RichTextLink] | None, Field(description="Links to embed in the text") 67 + ] = None, 68 + mentions: Annotated[ 69 + list[RichTextMention] | None, Field(description="User mentions to embed") 70 + ] = None, 71 + reply_to: Annotated[ 72 + str | None, Field(description="AT URI of post to reply to") 73 + ] = None, 74 + reply_root: Annotated[ 75 + str | None, Field(description="AT URI of thread root (defaults to reply_to)") 76 + ] = None, 77 + quote: Annotated[str | None, Field(description="AT URI of post to quote")] = None, 78 + ) -> PostResult: 79 + """Create a post with optional rich features like images, quotes, replies, and rich text. 80 + 81 + Examples: 82 + - Simple post: post("Hello world!") 83 + - With image: post("Check this out!", images=["https://example.com/img.jpg"]) 84 + - Reply: post("I agree!", reply_to="at://did/app.bsky.feed.post/123") 85 + - Quote: post("Great point!", quote="at://did/app.bsky.feed.post/456") 86 + - Rich text: post("Check out example.com", links=[{"text": "example.com", "url": "https://example.com"}]) 87 + """ 88 + return _atproto.create_post( 89 + text, images, image_alts, links, mentions, reply_to, reply_root, quote 90 + ) 91 + 92 + 93 + @atproto_mcp.tool 94 + def follow( 95 + handle: Annotated[ 96 + str, 97 + Field( 98 + description="The handle of the user to follow (e.g., 'user.bsky.social')" 99 + ), 100 + ], 101 + ) -> FollowResult: 102 + """Follow a user by their handle.""" 103 + return _atproto.follow_user_by_handle(handle) 104 + 105 + 106 + @atproto_mcp.tool 107 + def like( 108 + uri: Annotated[str, Field(description="The AT URI of the post to like")], 109 + ) -> LikeResult: 110 + """Like a post by its AT URI.""" 111 + return _atproto.like_post_by_uri(uri) 112 + 113 + 114 + @atproto_mcp.tool 115 + def repost( 116 + uri: Annotated[str, Field(description="The AT URI of the post to repost")], 117 + ) -> RepostResult: 118 + """Repost a post by its AT URI.""" 119 + return _atproto.repost_by_uri(uri) 120 + 121 + 122 + @atproto_mcp.tool 123 + def search( 124 + query: Annotated[str, Field(description="Search query for posts")], 125 + limit: Annotated[ 126 + int, Field(ge=1, le=100, description="Number of results to return") 127 + ] = settings.atproto_search_default_limit, 128 + ) -> SearchResult: 129 + """Search for posts containing specific text.""" 130 + return _atproto.search_for_posts(query, limit) 131 + 132 + 133 + @atproto_mcp.tool 134 + def create_thread( 135 + posts: Annotated[ 136 + list[ThreadPost], 137 + Field( 138 + description="List of posts to create as a thread. Each post can have text, images, links, mentions, and quotes." 139 + ), 140 + ], 141 + ) -> ThreadResult: 142 + """Create a thread of posts with automatic linking. 143 + 144 + The first post becomes the root of the thread, and each subsequent post 145 + replies to the previous one, maintaining the thread structure. 146 + 147 + Example: 148 + create_thread([ 149 + {"text": "Starting a thread about Python 🧵"}, 150 + {"text": "Python is great for rapid development"}, 151 + {"text": "And the ecosystem is amazing!", "images": ["https://example.com/python.jpg"]} 152 + ]) 153 + """ 154 + return _atproto.create_thread(posts)
+18
src/bot/atproto_mcp/settings.py
··· 1 + from pydantic import Field 2 + from pydantic_settings import BaseSettings, SettingsConfigDict 3 + 4 + 5 + class Settings(BaseSettings): 6 + model_config = SettingsConfigDict(env_file=[".env"], extra="ignore", populate_by_name=True) 7 + 8 + # Use same env var names as main bot config 9 + atproto_handle: str = Field(default=..., alias="bluesky_handle") 10 + atproto_password: str = Field(default=..., alias="bluesky_password") 11 + atproto_pds_url: str = Field(default="https://bsky.social", alias="bluesky_service") 12 + 13 + atproto_notifications_default_limit: int = Field(default=10) 14 + atproto_timeline_default_limit: int = Field(default=10) 15 + atproto_search_default_limit: int = Field(default=10) 16 + 17 + 18 + settings = Settings()
+142
src/bot/atproto_mcp/types.py
··· 1 + """Type definitions for ATProto MCP server.""" 2 + 3 + from typing import TypedDict 4 + 5 + 6 + class ProfileInfo(TypedDict): 7 + """Profile information response.""" 8 + 9 + connected: bool 10 + handle: str | None 11 + display_name: str | None 12 + did: str | None 13 + followers: int | None 14 + following: int | None 15 + posts: int | None 16 + error: str | None 17 + 18 + 19 + class PostResult(TypedDict): 20 + """Result of creating a post.""" 21 + 22 + success: bool 23 + uri: str | None 24 + cid: str | None 25 + text: str | None 26 + created_at: str | None 27 + error: str | None 28 + 29 + 30 + class Post(TypedDict): 31 + """A single post.""" 32 + 33 + author: str 34 + text: str | None 35 + created_at: str | None 36 + likes: int 37 + reposts: int 38 + replies: int 39 + uri: str 40 + cid: str 41 + 42 + 43 + class TimelineResult(TypedDict): 44 + """Timeline fetch result.""" 45 + 46 + success: bool 47 + count: int 48 + posts: list[Post] 49 + error: str | None 50 + 51 + 52 + class SearchResult(TypedDict): 53 + """Search result.""" 54 + 55 + success: bool 56 + query: str 57 + count: int 58 + posts: list[Post] 59 + error: str | None 60 + 61 + 62 + class Notification(TypedDict): 63 + """A single notification.""" 64 + 65 + reason: str 66 + author: str | None 67 + is_read: bool 68 + indexed_at: str 69 + uri: str 70 + cid: str 71 + 72 + 73 + class NotificationsResult(TypedDict): 74 + """Notifications fetch result.""" 75 + 76 + success: bool 77 + count: int 78 + notifications: list[Notification] 79 + error: str | None 80 + 81 + 82 + class FollowResult(TypedDict): 83 + """Result of following a user.""" 84 + 85 + success: bool 86 + handle: str | None 87 + did: str | None 88 + uri: str | None 89 + error: str | None 90 + 91 + 92 + class LikeResult(TypedDict): 93 + """Result of liking a post.""" 94 + 95 + success: bool 96 + liked_uri: str | None 97 + like_uri: str | None 98 + error: str | None 99 + 100 + 101 + class RepostResult(TypedDict): 102 + """Result of reposting.""" 103 + 104 + success: bool 105 + reposted_uri: str | None 106 + repost_uri: str | None 107 + error: str | None 108 + 109 + 110 + class RichTextLink(TypedDict): 111 + """A link in rich text.""" 112 + 113 + text: str 114 + url: str 115 + 116 + 117 + class RichTextMention(TypedDict): 118 + """A mention in rich text.""" 119 + 120 + handle: str 121 + display_text: str | None 122 + 123 + 124 + class ThreadPost(TypedDict, total=False): 125 + """A post in a thread.""" 126 + 127 + text: str # Required 128 + images: list[str] | None 129 + image_alts: list[str] | None 130 + links: list[RichTextLink] | None 131 + mentions: list[RichTextMention] | None 132 + quote: str | None 133 + 134 + 135 + class ThreadResult(TypedDict): 136 + """Result of creating a thread.""" 137 + 138 + success: bool 139 + thread_uri: str | None # URI of the first post 140 + post_uris: list[str] 141 + post_count: int 142 + error: str | None
+173
src/bot/memory.py
··· 1 + """Simple, interpretable SQLite-based memory for phi. 2 + 3 + Design principles: 4 + - Single SQLite database (threads.db) 5 + - Plain text storage (no embeddings, no vector search) 6 + - Interpretable: you can open the db and read everything 7 + - Two types of memory: thread history and user facts 8 + """ 9 + 10 + import json 11 + import sqlite3 12 + from contextlib import contextmanager 13 + from datetime import datetime 14 + from pathlib import Path 15 + 16 + 17 + class Memory: 18 + """Simple memory system using SQLite.""" 19 + 20 + def __init__(self, db_path: Path = Path("threads.db")): 21 + self.db_path = db_path 22 + self._init_db() 23 + 24 + def _init_db(self): 25 + """Initialize database schema.""" 26 + with self._get_connection() as conn: 27 + # Thread messages - full conversation history per thread 28 + conn.execute(""" 29 + CREATE TABLE IF NOT EXISTS threads ( 30 + thread_uri TEXT PRIMARY KEY, 31 + messages TEXT NOT NULL, 32 + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP 33 + ) 34 + """) 35 + 36 + # User memories - simple facts about users 37 + conn.execute(""" 38 + CREATE TABLE IF NOT EXISTS user_memories ( 39 + id INTEGER PRIMARY KEY AUTOINCREMENT, 40 + user_handle TEXT NOT NULL, 41 + memory_text TEXT NOT NULL, 42 + memory_type TEXT NOT NULL, 43 + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP 44 + ) 45 + """) 46 + conn.execute(""" 47 + CREATE INDEX IF NOT EXISTS idx_user_handle 48 + ON user_memories(user_handle) 49 + """) 50 + 51 + @contextmanager 52 + def _get_connection(self): 53 + """Get database connection.""" 54 + conn = sqlite3.connect(self.db_path) 55 + conn.row_factory = sqlite3.Row 56 + try: 57 + yield conn 58 + conn.commit() 59 + finally: 60 + conn.close() 61 + 62 + # Thread memory operations 63 + def add_thread_message( 64 + self, thread_uri: str, author_handle: str, message_text: str 65 + ): 66 + """Add a message to a thread's history.""" 67 + with self._get_connection() as conn: 68 + # Get existing messages 69 + cursor = conn.execute( 70 + "SELECT messages FROM threads WHERE thread_uri = ?", (thread_uri,) 71 + ) 72 + row = cursor.fetchone() 73 + 74 + # Parse existing messages or start fresh 75 + messages = json.loads(row["messages"]) if row else [] 76 + 77 + # Append new message 78 + messages.append( 79 + { 80 + "author": author_handle, 81 + "text": message_text, 82 + "timestamp": datetime.now().isoformat(), 83 + } 84 + ) 85 + 86 + # Update or insert 87 + conn.execute( 88 + """ 89 + INSERT INTO threads (thread_uri, messages, updated_at) 90 + VALUES (?, ?, CURRENT_TIMESTAMP) 91 + ON CONFLICT(thread_uri) DO UPDATE SET 92 + messages = excluded.messages, 93 + updated_at = CURRENT_TIMESTAMP 94 + """, 95 + (thread_uri, json.dumps(messages)), 96 + ) 97 + 98 + def get_thread_context(self, thread_uri: str, limit: int = 10) -> str: 99 + """Get formatted thread context for LLM.""" 100 + with self._get_connection() as conn: 101 + cursor = conn.execute( 102 + "SELECT messages FROM threads WHERE thread_uri = ?", (thread_uri,) 103 + ) 104 + row = cursor.fetchone() 105 + 106 + if not row: 107 + return "No previous messages in this thread." 108 + 109 + messages = json.loads(row["messages"]) 110 + 111 + # Format last N messages 112 + recent = messages[-limit:] if len(messages) > limit else messages 113 + formatted = ["Previous messages in this thread:"] 114 + for msg in recent: 115 + formatted.append(f"@{msg['author']}: {msg['text']}") 116 + 117 + return "\n".join(formatted) 118 + 119 + # User memory operations 120 + def add_user_memory( 121 + self, user_handle: str, memory_text: str, memory_type: str = "fact" 122 + ): 123 + """Store a fact or preference about a user.""" 124 + with self._get_connection() as conn: 125 + conn.execute( 126 + """ 127 + INSERT INTO user_memories (user_handle, memory_text, memory_type) 128 + VALUES (?, ?, ?) 129 + """, 130 + (user_handle, memory_text, memory_type), 131 + ) 132 + 133 + def get_user_context(self, user_handle: str, limit: int = 10) -> str: 134 + """Get formatted user memory context for LLM.""" 135 + with self._get_connection() as conn: 136 + cursor = conn.execute( 137 + """ 138 + SELECT memory_text, memory_type, created_at 139 + FROM user_memories 140 + WHERE user_handle = ? 141 + ORDER BY created_at DESC 142 + LIMIT ? 143 + """, 144 + (user_handle, limit), 145 + ) 146 + memories = cursor.fetchall() 147 + 148 + if not memories: 149 + return f"No previous interactions with @{user_handle}." 150 + 151 + formatted = [f"What I remember about @{user_handle}:"] 152 + for mem in memories: 153 + formatted.append(f"- {mem['memory_text']}") 154 + 155 + return "\n".join(formatted) 156 + 157 + def build_full_context( 158 + self, thread_uri: str, user_handle: str, thread_limit: int = 10 159 + ) -> str: 160 + """Build complete context for a conversation.""" 161 + parts = [] 162 + 163 + # Thread context 164 + thread_ctx = self.get_thread_context(thread_uri, limit=thread_limit) 165 + if thread_ctx != "No previous messages in this thread.": 166 + parts.append(thread_ctx) 167 + 168 + # User context 169 + user_ctx = self.get_user_context(user_handle) 170 + if user_ctx != f"No previous interactions with @{user_handle}.": 171 + parts.append(f"\n{user_ctx}") 172 + 173 + return "\n".join(parts) if parts else "No prior context available."
+380 -152
uv.lock
··· 139 139 140 140 [[package]] 141 141 name = "atproto" 142 - version = "0.0.61" 142 + version = "0.0.1" 143 143 source = { registry = "https://pypi.org/simple" } 144 - dependencies = [ 145 - { name = "click" }, 146 - { name = "cryptography" }, 147 - { name = "dnspython" }, 148 - { name = "httpx" }, 149 - { name = "libipld" }, 150 - { name = "pydantic" }, 151 - { name = "typing-extensions" }, 152 - { name = "websockets" }, 153 - ] 154 - sdist = { url = "https://files.pythonhosted.org/packages/b1/59/6f5074b3a45e0e3c1853544240e9039e86219feb30ff1bb5e8582c791547/atproto-0.0.61.tar.gz", hash = "sha256:98e022daf538d14f134ce7c91d42c4c973f3493ac56e43a84daa4c881f102beb", size = 189208, upload-time = "2025-04-19T00:20:11.918Z" } 144 + sdist = { url = "https://files.pythonhosted.org/packages/da/b4/b0f23adc83dcda376112e7efe449f75b31565ac126d3aca9cae25a712733/atproto-0.0.1.tar.gz", hash = "sha256:cc46914de42b8867069b340e8beced2cfdbf97b412d32bd55c5ce2f866be7ad7", size = 2196, upload-time = "2022-11-23T00:11:59.249Z" } 155 145 wheels = [ 156 - { url = "https://files.pythonhosted.org/packages/bd/b6/da9963bf54d4c0a8a590b6297d8858c395243dbb04cb581fdadb5fe7eac7/atproto-0.0.61-py3-none-any.whl", hash = "sha256:658da5832aaeea4a12a9a74235f9c90c11453e77d596fdccb1f8b39d56245b88", size = 380426, upload-time = "2025-04-19T00:20:10.026Z" }, 146 + { url = "https://files.pythonhosted.org/packages/28/c2/9f37817fbf4df3c3f1431f04cff1c9a68c25f44b5a98192dca49e9401e9b/atproto-0.0.1-py3-none-any.whl", hash = "sha256:d866a599e6c7386c101fd87052723979ac74d9f3c4cacac31f00542c18fd1c66", size = 2141, upload-time = "2022-11-23T00:11:56.501Z" }, 157 147 ] 158 148 159 149 [[package]] ··· 166 156 ] 167 157 168 158 [[package]] 159 + name = "authlib" 160 + version = "1.6.5" 161 + source = { registry = "https://pypi.org/simple" } 162 + dependencies = [ 163 + { name = "cryptography" }, 164 + ] 165 + sdist = { url = "https://files.pythonhosted.org/packages/cd/3f/1d3bbd0bf23bdd99276d4def22f29c27a914067b4cf66f753ff9b8bbd0f3/authlib-1.6.5.tar.gz", hash = "sha256:6aaf9c79b7cc96c900f0b284061691c5d4e61221640a948fe690b556a6d6d10b", size = 164553, upload-time = "2025-10-02T13:36:09.489Z" } 166 + wheels = [ 167 + { url = "https://files.pythonhosted.org/packages/f8/aa/5082412d1ee302e9e7d80b6949bc4d2a8fa1149aaab610c5fc24709605d6/authlib-1.6.5-py2.py3-none-any.whl", hash = "sha256:3e0e0507807f842b02175507bdee8957a1d5707fd4afb17c32fb43fee90b6e3a", size = 243608, upload-time = "2025-10-02T13:36:07.637Z" }, 168 + ] 169 + 170 + [[package]] 169 171 name = "bot" 170 172 source = { editable = "." } 171 173 dependencies = [ 172 174 { name = "anthropic" }, 173 175 { name = "atproto" }, 174 176 { name = "fastapi" }, 177 + { name = "fastmcp" }, 175 178 { name = "httpx" }, 176 - { name = "openai" }, 177 179 { name = "pydantic-ai" }, 178 180 { name = "pydantic-settings" }, 179 181 { name = "rich" }, 180 - { name = "turbopuffer" }, 181 182 { name = "uvicorn" }, 183 + { name = "websockets" }, 182 184 ] 183 185 184 186 [package.dev-dependencies] ··· 194 196 { name = "anthropic" }, 195 197 { name = "atproto" }, 196 198 { name = "fastapi" }, 199 + { name = "fastmcp", specifier = ">=0.8.0" }, 197 200 { name = "httpx" }, 198 - { name = "openai" }, 199 201 { name = "pydantic-ai" }, 200 202 { name = "pydantic-settings" }, 201 203 { name = "rich" }, 202 - { name = "turbopuffer" }, 203 204 { name = "uvicorn" }, 205 + { name = "websockets", specifier = ">=15.0.1" }, 204 206 ] 205 207 206 208 [package.metadata.requires-dev] ··· 402 404 ] 403 405 404 406 [[package]] 407 + name = "cyclopts" 408 + version = "3.24.0" 409 + source = { registry = "https://pypi.org/simple" } 410 + dependencies = [ 411 + { name = "attrs" }, 412 + { name = "docstring-parser", marker = "python_full_version < '4.0'" }, 413 + { name = "rich" }, 414 + { name = "rich-rst" }, 415 + ] 416 + sdist = { url = "https://files.pythonhosted.org/packages/30/ca/7782da3b03242d5f0a16c20371dff99d4bd1fedafe26bc48ff82e42be8c9/cyclopts-3.24.0.tar.gz", hash = "sha256:de6964a041dfb3c57bf043b41e68c43548227a17de1bad246e3a0bfc5c4b7417", size = 76131, upload-time = "2025-09-08T15:40:57.75Z" } 417 + wheels = [ 418 + { url = "https://files.pythonhosted.org/packages/f0/8b/2c95f0645c6f40211896375e6fa51f504b8ccb29c21f6ae661fe87ab044e/cyclopts-3.24.0-py3-none-any.whl", hash = "sha256:809d04cde9108617106091140c3964ee6fceb33cecdd537f7ffa360bde13ed71", size = 86154, upload-time = "2025-09-08T15:40:56.41Z" }, 419 + ] 420 + 421 + [[package]] 405 422 name = "distro" 406 423 version = "1.9.0" 407 424 source = { registry = "https://pypi.org/simple" } ··· 420 437 ] 421 438 422 439 [[package]] 440 + name = "docstring-parser" 441 + version = "0.17.0" 442 + source = { registry = "https://pypi.org/simple" } 443 + sdist = { url = "https://files.pythonhosted.org/packages/b2/9d/c3b43da9515bd270df0f80548d9944e389870713cc1fe2b8fb35fe2bcefd/docstring_parser-0.17.0.tar.gz", hash = "sha256:583de4a309722b3315439bb31d64ba3eebada841f2e2cee23b99df001434c912", size = 27442, upload-time = "2025-07-21T07:35:01.868Z" } 444 + wheels = [ 445 + { url = "https://files.pythonhosted.org/packages/55/e2/2537ebcff11c1ee1ff17d8d0b6f4db75873e3b0fb32c2d4a2ee31ecb310a/docstring_parser-0.17.0-py3-none-any.whl", hash = "sha256:cf2569abd23dce8099b300f9b4fa8191e9582dda731fd533daf54c4551658708", size = 36896, upload-time = "2025-07-21T07:35:00.684Z" }, 446 + ] 447 + 448 + [[package]] 449 + name = "docutils" 450 + version = "0.22.2" 451 + source = { registry = "https://pypi.org/simple" } 452 + sdist = { url = "https://files.pythonhosted.org/packages/4a/c0/89fe6215b443b919cb98a5002e107cb5026854ed1ccb6b5833e0768419d1/docutils-0.22.2.tar.gz", hash = "sha256:9fdb771707c8784c8f2728b67cb2c691305933d68137ef95a75db5f4dfbc213d", size = 2289092, upload-time = "2025-09-20T17:55:47.994Z" } 453 + wheels = [ 454 + { url = "https://files.pythonhosted.org/packages/66/dd/f95350e853a4468ec37478414fc04ae2d61dad7a947b3015c3dcc51a09b9/docutils-0.22.2-py3-none-any.whl", hash = "sha256:b0e98d679283fc3bb0ead8a5da7f501baa632654e7056e9c5846842213d674d8", size = 632667, upload-time = "2025-09-20T17:55:43.052Z" }, 455 + ] 456 + 457 + [[package]] 458 + name = "email-validator" 459 + version = "2.3.0" 460 + source = { registry = "https://pypi.org/simple" } 461 + dependencies = [ 462 + { name = "dnspython" }, 463 + { name = "idna" }, 464 + ] 465 + sdist = { url = "https://files.pythonhosted.org/packages/f5/22/900cb125c76b7aaa450ce02fd727f452243f2e91a61af068b40adba60ea9/email_validator-2.3.0.tar.gz", hash = "sha256:9fc05c37f2f6cf439ff414f8fc46d917929974a82244c20eb10231ba60c54426", size = 51238, upload-time = "2025-08-26T13:09:06.831Z" } 466 + wheels = [ 467 + { url = "https://files.pythonhosted.org/packages/de/15/545e2b6cf2e3be84bc1ed85613edd75b8aea69807a71c26f4ca6a9258e82/email_validator-2.3.0-py3-none-any.whl", hash = "sha256:80f13f623413e6b197ae73bb10bf4eb0908faf509ad8362c5edeb0be7fd450b4", size = 35604, upload-time = "2025-08-26T13:09:05.858Z" }, 468 + ] 469 + 470 + [[package]] 423 471 name = "eval-type-backport" 424 472 version = "0.2.2" 425 473 source = { registry = "https://pypi.org/simple" } ··· 429 477 ] 430 478 431 479 [[package]] 480 + name = "exceptiongroup" 481 + version = "1.3.0" 482 + source = { registry = "https://pypi.org/simple" } 483 + dependencies = [ 484 + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, 485 + ] 486 + sdist = { url = "https://files.pythonhosted.org/packages/0b/9f/a65090624ecf468cdca03533906e7c69ed7588582240cfe7cc9e770b50eb/exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88", size = 29749, upload-time = "2025-05-10T17:42:51.123Z" } 487 + wheels = [ 488 + { url = "https://files.pythonhosted.org/packages/36/f4/c6e662dade71f56cd2f3735141b265c3c79293c109549c1e6933b0651ffc/exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10", size = 16674, upload-time = "2025-05-10T17:42:49.33Z" }, 489 + ] 490 + 491 + [[package]] 432 492 name = "fastapi" 433 493 version = "0.116.1" 434 494 source = { registry = "https://pypi.org/simple" } ··· 465 525 { url = "https://files.pythonhosted.org/packages/be/84/02bceb7518867df84027232a75225db758b9b45f12017c9743f45b73101e/fastavro-1.11.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e0babcd81acceb4c60110af9efa25d890dbb68f7de880f806dadeb1e70fe413", size = 3240658, upload-time = "2025-05-18T04:55:27.633Z" }, 466 526 { url = "https://files.pythonhosted.org/packages/f2/17/508c846c644d39bc432b027112068b8e96e7560468304d4c0757539dd73a/fastavro-1.11.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b2c0cb8063c7208b53b6867983dc6ae7cc80b91116b51d435d2610a5db2fc52f", size = 3372809, upload-time = "2025-05-18T04:55:30.063Z" }, 467 527 { url = "https://files.pythonhosted.org/packages/fe/84/9c2917a70ed570ddbfd1d32ac23200c1d011e36c332e59950d2f6d204941/fastavro-1.11.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:1bc2824e9969c04ab6263d269a1e0e5d40b9bd16ade6b70c29d6ffbc4f3cc102", size = 3387171, upload-time = "2025-05-18T04:55:32.531Z" }, 528 + ] 529 + 530 + [[package]] 531 + name = "fastmcp" 532 + version = "2.12.4" 533 + source = { registry = "https://pypi.org/simple" } 534 + dependencies = [ 535 + { name = "authlib" }, 536 + { name = "cyclopts" }, 537 + { name = "exceptiongroup" }, 538 + { name = "httpx" }, 539 + { name = "mcp" }, 540 + { name = "openapi-core" }, 541 + { name = "openapi-pydantic" }, 542 + { name = "pydantic", extra = ["email"] }, 543 + { name = "pyperclip" }, 544 + { name = "python-dotenv" }, 545 + { name = "rich" }, 546 + ] 547 + sdist = { url = "https://files.pythonhosted.org/packages/a8/b2/57845353a9bc63002995a982e66f3d0be4ec761e7bcb89e7d0638518d42a/fastmcp-2.12.4.tar.gz", hash = "sha256:b55fe89537038f19d0f4476544f9ca5ac171033f61811cc8f12bdeadcbea5016", size = 7167745, upload-time = "2025-09-26T16:43:27.71Z" } 548 + wheels = [ 549 + { url = "https://files.pythonhosted.org/packages/e2/c7/562ff39f25de27caec01e4c1e88cbb5fcae5160802ba3d90be33165df24f/fastmcp-2.12.4-py3-none-any.whl", hash = "sha256:56188fbbc1a9df58c537063f25958c57b5c4d715f73e395c41b51550b247d140", size = 329090, upload-time = "2025-09-26T16:43:25.314Z" }, 468 550 ] 469 551 470 552 [[package]] ··· 723 805 ] 724 806 725 807 [[package]] 808 + name = "isodate" 809 + version = "0.7.2" 810 + source = { registry = "https://pypi.org/simple" } 811 + sdist = { url = "https://files.pythonhosted.org/packages/54/4d/e940025e2ce31a8ce1202635910747e5a87cc3a6a6bb2d00973375014749/isodate-0.7.2.tar.gz", hash = "sha256:4cd1aa0f43ca76f4a6c6c0292a85f40b35ec2e43e315b59f06e6d32171a953e6", size = 29705, upload-time = "2024-10-08T23:04:11.5Z" } 812 + wheels = [ 813 + { url = "https://files.pythonhosted.org/packages/15/aa/0aca39a37d3c7eb941ba736ede56d689e7be91cab5d9ca846bde3999eba6/isodate-0.7.2-py3-none-any.whl", hash = "sha256:28009937d8031054830160fce6d409ed342816b543597cece116d966c6d99e15", size = 22320, upload-time = "2024-10-08T23:04:09.501Z" }, 814 + ] 815 + 816 + [[package]] 726 817 name = "jiter" 727 818 version = "0.10.0" 728 819 source = { registry = "https://pypi.org/simple" } ··· 795 886 ] 796 887 797 888 [[package]] 889 + name = "jsonschema-path" 890 + version = "0.3.4" 891 + source = { registry = "https://pypi.org/simple" } 892 + dependencies = [ 893 + { name = "pathable" }, 894 + { name = "pyyaml" }, 895 + { name = "referencing" }, 896 + { name = "requests" }, 897 + ] 898 + sdist = { url = "https://files.pythonhosted.org/packages/6e/45/41ebc679c2a4fced6a722f624c18d658dee42612b83ea24c1caf7c0eb3a8/jsonschema_path-0.3.4.tar.gz", hash = "sha256:8365356039f16cc65fddffafda5f58766e34bebab7d6d105616ab52bc4297001", size = 11159, upload-time = "2025-01-24T14:33:16.547Z" } 899 + wheels = [ 900 + { url = "https://files.pythonhosted.org/packages/cb/58/3485da8cb93d2f393bce453adeef16896751f14ba3e2024bc21dc9597646/jsonschema_path-0.3.4-py3-none-any.whl", hash = "sha256:f502191fdc2b22050f9a81c9237be9d27145b9001c55842bece5e94e382e52f8", size = 14810, upload-time = "2025-01-24T14:33:14.652Z" }, 901 + ] 902 + 903 + [[package]] 798 904 name = "jsonschema-specifications" 799 905 version = "2025.4.1" 800 906 source = { registry = "https://pypi.org/simple" } ··· 807 913 ] 808 914 809 915 [[package]] 810 - name = "libipld" 811 - version = "3.1.1" 916 + name = "lazy-object-proxy" 917 + version = "1.12.0" 812 918 source = { registry = "https://pypi.org/simple" } 813 - sdist = { url = "https://files.pythonhosted.org/packages/84/ac/21f2b0f9848c9d99a87e3cc626e7af0fc24883911ec5d7578686cc2a09d1/libipld-3.1.1.tar.gz", hash = "sha256:4b9a9da0ea5d848e9fa12c700027619a1e37ecc1da39dbd1424c0e9062f29e44", size = 4380425, upload-time = "2025-06-24T23:12:51.395Z" } 919 + sdist = { url = "https://files.pythonhosted.org/packages/08/a2/69df9c6ba6d316cfd81fe2381e464db3e6de5db45f8c43c6a23504abf8cb/lazy_object_proxy-1.12.0.tar.gz", hash = "sha256:1f5a462d92fd0cfb82f1fab28b51bfb209fabbe6aabf7f0d51472c0c124c0c61", size = 43681, upload-time = "2025-08-22T13:50:06.783Z" } 814 920 wheels = [ 815 - { url = "https://files.pythonhosted.org/packages/fe/07/975b9dde7e27489218c21db4357bd852cd71c388c06abedcff2b86a500ab/libipld-3.1.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:27d2fb2b19a9784a932a41fd1a6942361cfa65e0957871f4bde06c81639a32b1", size = 279659, upload-time = "2025-06-24T23:11:29.139Z" }, 816 - { url = "https://files.pythonhosted.org/packages/4d/db/bd6a9eefa7c90f23ea2ea98678e8f6aac15fedb9645ddaa8af977bcfdf2f/libipld-3.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7f0156a9bf04b7f575b907b7a15b902dde2d8af129aeb161b3ab6940f3fd9c02", size = 276397, upload-time = "2025-06-24T23:11:30.54Z" }, 817 - { url = "https://files.pythonhosted.org/packages/02/a8/09606bc7139173d8543cf8206b3c7ff9238bd4c9b47a71565c50912f0323/libipld-3.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29cf371122648a688f87fe3307bcfe2c6a4aefa184ba44126f066975cfd26b46", size = 297682, upload-time = "2025-06-24T23:11:31.833Z" }, 818 - { url = "https://files.pythonhosted.org/packages/31/ad/a54d62baead5aecc9a2f48ab2b8ac81fbeb8df19c89416735387dd041175/libipld-3.1.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a5463672cd0708d47bc8cfe1cc0dd95c55d5b7f3947027e0e9c6a13b1dc1b6d0", size = 304615, upload-time = "2025-06-24T23:11:32.8Z" }, 819 - { url = "https://files.pythonhosted.org/packages/c5/a2/3c7908d6aa865721e7e9c2f125e315614cee4e4ced4457d7b22cc8d8acc4/libipld-3.1.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:27a1b9b9392679fb494214bfa350adf7447b43bc39e497b669307da1f6dc8dd5", size = 332042, upload-time = "2025-06-24T23:11:33.831Z" }, 820 - { url = "https://files.pythonhosted.org/packages/e1/c0/ecd838e32630439ca3d8ce2274db32c77f31d0265c01b6a3c00fd96367bb/libipld-3.1.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a83d944c71ed50772a7cef3f14e3ef3cf93145c82963b9e49a85cd9ee0ba9878", size = 344326, upload-time = "2025-06-24T23:11:34.768Z" }, 821 - { url = "https://files.pythonhosted.org/packages/98/79/9ef27cd284c66e7e9481e7fe529d1412ea751b4cad1578571bbc02826098/libipld-3.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb9fef573406f7134727e0561e42fd221721800ed01d47f1207916595b72e780", size = 299195, upload-time = "2025-06-24T23:11:35.973Z" }, 822 - { url = "https://files.pythonhosted.org/packages/a7/6e/2db9510cdc410b154169438449277637f35bbc571c330d60d262320e6d77/libipld-3.1.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:485b21bdddbe7a3bb8f33f1d0b9998343bd82a578406e31f85899b031602d34d", size = 323946, upload-time = "2025-06-24T23:11:37.815Z" }, 823 - { url = "https://files.pythonhosted.org/packages/63/fb/ac59473cbc7598db0e194b2b14b10953029813f204555e5c12405b265594/libipld-3.1.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4fe6fa67a242755773f3e960163010bdbc797316ca782d387e6b128e0d3bca19", size = 477366, upload-time = "2025-06-24T23:11:38.798Z" }, 824 - { url = "https://files.pythonhosted.org/packages/f5/75/80915af5dc04785ff7a9468529a96d787723d24a9e76dbc31e0141bbcd23/libipld-3.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:38298cbea4f8308bb848c7f8c3d8e41cd2c9235ef8bca6adefd2a002e94287ff", size = 470106, upload-time = "2025-06-24T23:11:39.786Z" }, 825 - { url = "https://files.pythonhosted.org/packages/9e/17/832f1c91938a0e2d58905e86c7a2f21cd4b6334a3757221563bd9a8beb64/libipld-3.1.1-cp312-cp312-win32.whl", hash = "sha256:1bc228298e249baac85f702da7d1e23ee429529a078a6bdf09570168f53fcb0f", size = 173435, upload-time = "2025-06-24T23:11:41.072Z" }, 826 - { url = "https://files.pythonhosted.org/packages/14/62/1006fa794c6fe18040d06cebe2d593c20208c2a16a5eb01f7d4f48a5a3b5/libipld-3.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:a393e1809c7b1aa67c6f6c5d701787298f507448a601b8ec825b6ae26084fbad", size = 179271, upload-time = "2025-06-24T23:11:42.155Z" }, 827 - { url = "https://files.pythonhosted.org/packages/bc/af/95b2673bd8ab8225a374bde34b4ac21ef9a725c910517e0dadc5ce26d4a7/libipld-3.1.1-cp312-cp312-win_arm64.whl", hash = "sha256:7ad7870d2ee609d74eec4ba6dbc2caef0357861b3e0944226272f0e91f016d37", size = 169727, upload-time = "2025-06-24T23:11:43.164Z" }, 828 - { url = "https://files.pythonhosted.org/packages/e5/25/52f27b9617efb0c2f60e71bbfd4f88167ca7acd3aed413999f16e22b3e54/libipld-3.1.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:8cd7d7b8b2e0a6ab273b697259f291edbd7cb1b9200ed746a41dcd63fb52017a", size = 280260, upload-time = "2025-06-24T23:11:44.376Z" }, 829 - { url = "https://files.pythonhosted.org/packages/bb/14/123450261a35e869732ff610580df39a62164d9e0aab58334c182c9453f8/libipld-3.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0251c6daa8eceee2ce7dc4f03422f3f1acdd31b04ebda39cab5f8af3dae30943", size = 276684, upload-time = "2025-06-24T23:11:45.266Z" }, 830 - { url = "https://files.pythonhosted.org/packages/bd/3e/6dd2daf43ff735a3f53cbeaeac1edb3ba92fa2e48c64257800ede82442e6/libipld-3.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d4598b094286998f770f383eedbfc04c1018ec8ebe6746db0eff5b2059a484a", size = 297845, upload-time = "2025-06-24T23:11:46.143Z" }, 831 - { url = "https://files.pythonhosted.org/packages/83/23/e4f89d9bf854c58a5d6e2f2c667425669ed795956003b28de429b0740e0f/libipld-3.1.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7212411cbce495dfae24c2b6757a5c2f921797fe70ec0c026e1a2d19ae29e59a", size = 305200, upload-time = "2025-06-24T23:11:47.128Z" }, 832 - { url = "https://files.pythonhosted.org/packages/40/43/0b1e871275502e9799589d03a139730c0dfbb36d1922ab213b105ace59ee/libipld-3.1.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ffc2f978adda8a8309b55510ceda9fe5dc2431d4ff202ff77d84eb57c77d072f", size = 332153, upload-time = "2025-06-24T23:11:48.437Z" }, 833 - { url = "https://files.pythonhosted.org/packages/94/18/5e9cff31d9450e98cc7b4025d1c90bde661ee099ea46cfcb1d8a893e6083/libipld-3.1.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:99163cc7822abcb028c55860e5341c77200a3ae90f4c158c27e2118a07e8809d", size = 344391, upload-time = "2025-06-24T23:11:49.786Z" }, 834 - { url = "https://files.pythonhosted.org/packages/63/ca/4d938862912ab2f105710d1cc909ec65c71d0e63a90e3b494920c23a4383/libipld-3.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80f142cbd4fa89ef514a4dd43afbd4ed3c33ae7061f0e1e0763f7c1811dea389", size = 299448, upload-time = "2025-06-24T23:11:50.723Z" }, 835 - { url = "https://files.pythonhosted.org/packages/2a/08/f6020e53abe4c26d57fe29b001ba1a84b5b3ad2d618e135b82877e42b59a/libipld-3.1.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4596a6e2c5e81b14b1432f3a6120b1d785fc4f74402cef39accf0041999905e4", size = 324096, upload-time = "2025-06-24T23:11:51.646Z" }, 836 - { url = "https://files.pythonhosted.org/packages/df/0f/d3d9da8f1001e9856bc5cb171a838ca5102da7d959b870a0c5f5aa9ef82e/libipld-3.1.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:0cd275603ab3cc2394d40455de6976f01b2d85b4095c074c0c1e2692013f5eaa", size = 477593, upload-time = "2025-06-24T23:11:52.565Z" }, 837 - { url = "https://files.pythonhosted.org/packages/59/df/57dcd84e55c02f74bb40a246dd849430994bbb476e91b05179d749993c9a/libipld-3.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:16c999b3af996865004ff2da8280d0c24b672d8a00f9e4cd3a468da8f5e63a5a", size = 470201, upload-time = "2025-06-24T23:11:53.544Z" }, 838 - { url = "https://files.pythonhosted.org/packages/80/af/aee0800b415b63dc5e259675c31a36d6c261afff8e288b56bc2867aa9310/libipld-3.1.1-cp313-cp313-win32.whl", hash = "sha256:5d34c40a27e8755f500277be5268a2f6b6f0d1e20599152d8a34cd34fb3f2700", size = 173730, upload-time = "2025-06-24T23:11:54.5Z" }, 839 - { url = "https://files.pythonhosted.org/packages/54/a3/7e447f27ee896f48332254bb38e1b6c1d3f24b13e5029977646de9408159/libipld-3.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:5edee5f2ea8183bb6a151f149c9798a4f1db69fe16307e860a84f8d41b53665a", size = 179409, upload-time = "2025-06-24T23:11:55.356Z" }, 840 - { url = "https://files.pythonhosted.org/packages/f2/0b/31d6097620c5cfaaaa0acb7760c29186029cd72c6ab81c537cc1ddfb34e5/libipld-3.1.1-cp313-cp313-win_arm64.whl", hash = "sha256:7307876987d9e570dcaf17a15f0ba210f678b323860742d725cf6d8d8baeae1f", size = 169715, upload-time = "2025-06-24T23:11:56.41Z" }, 921 + { url = "https://files.pythonhosted.org/packages/0d/1b/b5f5bd6bda26f1e15cd3232b223892e4498e34ec70a7f4f11c401ac969f1/lazy_object_proxy-1.12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8ee0d6027b760a11cc18281e702c0309dd92da458a74b4c15025d7fc490deede", size = 26746, upload-time = "2025-08-22T13:42:37.572Z" }, 922 + { url = "https://files.pythonhosted.org/packages/55/64/314889b618075c2bfc19293ffa9153ce880ac6153aacfd0a52fcabf21a66/lazy_object_proxy-1.12.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:4ab2c584e3cc8be0dfca422e05ad30a9abe3555ce63e9ab7a559f62f8dbc6ff9", size = 71457, upload-time = "2025-08-22T13:42:38.743Z" }, 923 + { url = "https://files.pythonhosted.org/packages/11/53/857fc2827fc1e13fbdfc0ba2629a7d2579645a06192d5461809540b78913/lazy_object_proxy-1.12.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:14e348185adbd03ec17d051e169ec45686dcd840a3779c9d4c10aabe2ca6e1c0", size = 71036, upload-time = "2025-08-22T13:42:40.184Z" }, 924 + { url = "https://files.pythonhosted.org/packages/2b/24/e581ffed864cd33c1b445b5763d617448ebb880f48675fc9de0471a95cbc/lazy_object_proxy-1.12.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c4fcbe74fb85df8ba7825fa05eddca764138da752904b378f0ae5ab33a36c308", size = 69329, upload-time = "2025-08-22T13:42:41.311Z" }, 925 + { url = "https://files.pythonhosted.org/packages/78/be/15f8f5a0b0b2e668e756a152257d26370132c97f2f1943329b08f057eff0/lazy_object_proxy-1.12.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:563d2ec8e4d4b68ee7848c5ab4d6057a6d703cb7963b342968bb8758dda33a23", size = 70690, upload-time = "2025-08-22T13:42:42.51Z" }, 926 + { url = "https://files.pythonhosted.org/packages/5d/aa/f02be9bbfb270e13ee608c2b28b8771f20a5f64356c6d9317b20043c6129/lazy_object_proxy-1.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:53c7fd99eb156bbb82cbc5d5188891d8fdd805ba6c1e3b92b90092da2a837073", size = 26563, upload-time = "2025-08-22T13:42:43.685Z" }, 927 + { url = "https://files.pythonhosted.org/packages/f4/26/b74c791008841f8ad896c7f293415136c66cc27e7c7577de4ee68040c110/lazy_object_proxy-1.12.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:86fd61cb2ba249b9f436d789d1356deae69ad3231dc3c0f17293ac535162672e", size = 26745, upload-time = "2025-08-22T13:42:44.982Z" }, 928 + { url = "https://files.pythonhosted.org/packages/9b/52/641870d309e5d1fb1ea7d462a818ca727e43bfa431d8c34b173eb090348c/lazy_object_proxy-1.12.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:81d1852fb30fab81696f93db1b1e55a5d1ff7940838191062f5f56987d5fcc3e", size = 71537, upload-time = "2025-08-22T13:42:46.141Z" }, 929 + { url = "https://files.pythonhosted.org/packages/47/b6/919118e99d51c5e76e8bf5a27df406884921c0acf2c7b8a3b38d847ab3e9/lazy_object_proxy-1.12.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:be9045646d83f6c2664c1330904b245ae2371b5c57a3195e4028aedc9f999655", size = 71141, upload-time = "2025-08-22T13:42:47.375Z" }, 930 + { url = "https://files.pythonhosted.org/packages/e5/47/1d20e626567b41de085cf4d4fb3661a56c159feaa73c825917b3b4d4f806/lazy_object_proxy-1.12.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:67f07ab742f1adfb3966c40f630baaa7902be4222a17941f3d85fd1dae5565ff", size = 69449, upload-time = "2025-08-22T13:42:48.49Z" }, 931 + { url = "https://files.pythonhosted.org/packages/58/8d/25c20ff1a1a8426d9af2d0b6f29f6388005fc8cd10d6ee71f48bff86fdd0/lazy_object_proxy-1.12.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:75ba769017b944fcacbf6a80c18b2761a1795b03f8899acdad1f1c39db4409be", size = 70744, upload-time = "2025-08-22T13:42:49.608Z" }, 932 + { url = "https://files.pythonhosted.org/packages/c0/67/8ec9abe15c4f8a4bcc6e65160a2c667240d025cbb6591b879bea55625263/lazy_object_proxy-1.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:7b22c2bbfb155706b928ac4d74c1a63ac8552a55ba7fff4445155523ea4067e1", size = 26568, upload-time = "2025-08-22T13:42:57.719Z" }, 933 + { url = "https://files.pythonhosted.org/packages/23/12/cd2235463f3469fd6c62d41d92b7f120e8134f76e52421413a0ad16d493e/lazy_object_proxy-1.12.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4a79b909aa16bde8ae606f06e6bbc9d3219d2e57fb3e0076e17879072b742c65", size = 27391, upload-time = "2025-08-22T13:42:50.62Z" }, 934 + { url = "https://files.pythonhosted.org/packages/60/9e/f1c53e39bbebad2e8609c67d0830cc275f694d0ea23d78e8f6db526c12d3/lazy_object_proxy-1.12.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:338ab2f132276203e404951205fe80c3fd59429b3a724e7b662b2eb539bb1be9", size = 80552, upload-time = "2025-08-22T13:42:51.731Z" }, 935 + { url = "https://files.pythonhosted.org/packages/4c/b6/6c513693448dcb317d9d8c91d91f47addc09553613379e504435b4cc8b3e/lazy_object_proxy-1.12.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8c40b3c9faee2e32bfce0df4ae63f4e73529766893258eca78548bac801c8f66", size = 82857, upload-time = "2025-08-22T13:42:53.225Z" }, 936 + { url = "https://files.pythonhosted.org/packages/12/1c/d9c4aaa4c75da11eb7c22c43d7c90a53b4fca0e27784a5ab207768debea7/lazy_object_proxy-1.12.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:717484c309df78cedf48396e420fa57fc8a2b1f06ea889df7248fdd156e58847", size = 80833, upload-time = "2025-08-22T13:42:54.391Z" }, 937 + { url = "https://files.pythonhosted.org/packages/0b/ae/29117275aac7d7d78ae4f5a4787f36ff33262499d486ac0bf3e0b97889f6/lazy_object_proxy-1.12.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a6b7ea5ea1ffe15059eb44bcbcb258f97bcb40e139b88152c40d07b1a1dfc9ac", size = 79516, upload-time = "2025-08-22T13:42:55.812Z" }, 938 + { url = "https://files.pythonhosted.org/packages/19/40/b4e48b2c38c69392ae702ae7afa7b6551e0ca5d38263198b7c79de8b3bdf/lazy_object_proxy-1.12.0-cp313-cp313t-win_amd64.whl", hash = "sha256:08c465fb5cd23527512f9bd7b4c7ba6cec33e28aad36fbbe46bf7b858f9f3f7f", size = 27656, upload-time = "2025-08-22T13:42:56.793Z" }, 939 + { url = "https://files.pythonhosted.org/packages/ef/3a/277857b51ae419a1574557c0b12e0d06bf327b758ba94cafc664cb1e2f66/lazy_object_proxy-1.12.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c9defba70ab943f1df98a656247966d7729da2fe9c2d5d85346464bf320820a3", size = 26582, upload-time = "2025-08-22T13:49:49.366Z" }, 940 + { url = "https://files.pythonhosted.org/packages/1a/b6/c5e0fa43535bb9c87880e0ba037cdb1c50e01850b0831e80eb4f4762f270/lazy_object_proxy-1.12.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6763941dbf97eea6b90f5b06eb4da9418cc088fce0e3883f5816090f9afcde4a", size = 71059, upload-time = "2025-08-22T13:49:50.488Z" }, 941 + { url = "https://files.pythonhosted.org/packages/06/8a/7dcad19c685963c652624702f1a968ff10220b16bfcc442257038216bf55/lazy_object_proxy-1.12.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fdc70d81235fc586b9e3d1aeef7d1553259b62ecaae9db2167a5d2550dcc391a", size = 71034, upload-time = "2025-08-22T13:49:54.224Z" }, 942 + { url = "https://files.pythonhosted.org/packages/12/ac/34cbfb433a10e28c7fd830f91c5a348462ba748413cbb950c7f259e67aa7/lazy_object_proxy-1.12.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:0a83c6f7a6b2bfc11ef3ed67f8cbe99f8ff500b05655d8e7df9aab993a6abc95", size = 69529, upload-time = "2025-08-22T13:49:55.29Z" }, 943 + { url = "https://files.pythonhosted.org/packages/6f/6a/11ad7e349307c3ca4c0175db7a77d60ce42a41c60bcb11800aabd6a8acb8/lazy_object_proxy-1.12.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:256262384ebd2a77b023ad02fbcc9326282bcfd16484d5531154b02bc304f4c5", size = 70391, upload-time = "2025-08-22T13:49:56.35Z" }, 944 + { url = "https://files.pythonhosted.org/packages/59/97/9b410ed8fbc6e79c1ee8b13f8777a80137d4bc189caf2c6202358e66192c/lazy_object_proxy-1.12.0-cp314-cp314-win_amd64.whl", hash = "sha256:7601ec171c7e8584f8ff3f4e440aa2eebf93e854f04639263875b8c2971f819f", size = 26988, upload-time = "2025-08-22T13:49:57.302Z" }, 841 945 ] 842 946 843 947 [[package]] ··· 862 966 ] 863 967 864 968 [[package]] 969 + name = "markupsafe" 970 + version = "3.0.3" 971 + source = { registry = "https://pypi.org/simple" } 972 + sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", hash = "sha256:722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698", size = 80313, upload-time = "2025-09-27T18:37:40.426Z" } 973 + wheels = [ 974 + { url = "https://files.pythonhosted.org/packages/5a/72/147da192e38635ada20e0a2e1a51cf8823d2119ce8883f7053879c2199b5/markupsafe-3.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d53197da72cc091b024dd97249dfc7794d6a56530370992a5e1a08983ad9230e", size = 11615, upload-time = "2025-09-27T18:36:30.854Z" }, 975 + { url = "https://files.pythonhosted.org/packages/9a/81/7e4e08678a1f98521201c3079f77db69fb552acd56067661f8c2f534a718/markupsafe-3.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1872df69a4de6aead3491198eaf13810b565bdbeec3ae2dc8780f14458ec73ce", size = 12020, upload-time = "2025-09-27T18:36:31.971Z" }, 976 + { url = "https://files.pythonhosted.org/packages/1e/2c/799f4742efc39633a1b54a92eec4082e4f815314869865d876824c257c1e/markupsafe-3.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a7e8ae81ae39e62a41ec302f972ba6ae23a5c5396c8e60113e9066ef893da0d", size = 24332, upload-time = "2025-09-27T18:36:32.813Z" }, 977 + { url = "https://files.pythonhosted.org/packages/3c/2e/8d0c2ab90a8c1d9a24f0399058ab8519a3279d1bd4289511d74e909f060e/markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6dd0be5b5b189d31db7cda48b91d7e0a9795f31430b7f271219ab30f1d3ac9d", size = 22947, upload-time = "2025-09-27T18:36:33.86Z" }, 978 + { url = "https://files.pythonhosted.org/packages/2c/54/887f3092a85238093a0b2154bd629c89444f395618842e8b0c41783898ea/markupsafe-3.0.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:94c6f0bb423f739146aec64595853541634bde58b2135f27f61c1ffd1cd4d16a", size = 21962, upload-time = "2025-09-27T18:36:35.099Z" }, 979 + { url = "https://files.pythonhosted.org/packages/c9/2f/336b8c7b6f4a4d95e91119dc8521402461b74a485558d8f238a68312f11c/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:be8813b57049a7dc738189df53d69395eba14fb99345e0a5994914a3864c8a4b", size = 23760, upload-time = "2025-09-27T18:36:36.001Z" }, 980 + { url = "https://files.pythonhosted.org/packages/32/43/67935f2b7e4982ffb50a4d169b724d74b62a3964bc1a9a527f5ac4f1ee2b/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:83891d0e9fb81a825d9a6d61e3f07550ca70a076484292a70fde82c4b807286f", size = 21529, upload-time = "2025-09-27T18:36:36.906Z" }, 981 + { url = "https://files.pythonhosted.org/packages/89/e0/4486f11e51bbba8b0c041098859e869e304d1c261e59244baa3d295d47b7/markupsafe-3.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:77f0643abe7495da77fb436f50f8dab76dbc6e5fd25d39589a0f1fe6548bfa2b", size = 23015, upload-time = "2025-09-27T18:36:37.868Z" }, 982 + { url = "https://files.pythonhosted.org/packages/2f/e1/78ee7a023dac597a5825441ebd17170785a9dab23de95d2c7508ade94e0e/markupsafe-3.0.3-cp312-cp312-win32.whl", hash = "sha256:d88b440e37a16e651bda4c7c2b930eb586fd15ca7406cb39e211fcff3bf3017d", size = 14540, upload-time = "2025-09-27T18:36:38.761Z" }, 983 + { url = "https://files.pythonhosted.org/packages/aa/5b/bec5aa9bbbb2c946ca2733ef9c4ca91c91b6a24580193e891b5f7dbe8e1e/markupsafe-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:26a5784ded40c9e318cfc2bdb30fe164bdb8665ded9cd64d500a34fb42067b1c", size = 15105, upload-time = "2025-09-27T18:36:39.701Z" }, 984 + { url = "https://files.pythonhosted.org/packages/e5/f1/216fc1bbfd74011693a4fd837e7026152e89c4bcf3e77b6692fba9923123/markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f", size = 13906, upload-time = "2025-09-27T18:36:40.689Z" }, 985 + { url = "https://files.pythonhosted.org/packages/38/2f/907b9c7bbba283e68f20259574b13d005c121a0fa4c175f9bed27c4597ff/markupsafe-3.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e1cf1972137e83c5d4c136c43ced9ac51d0e124706ee1c8aa8532c1287fa8795", size = 11622, upload-time = "2025-09-27T18:36:41.777Z" }, 986 + { url = "https://files.pythonhosted.org/packages/9c/d9/5f7756922cdd676869eca1c4e3c0cd0df60ed30199ffd775e319089cb3ed/markupsafe-3.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:116bb52f642a37c115f517494ea5feb03889e04df47eeff5b130b1808ce7c219", size = 12029, upload-time = "2025-09-27T18:36:43.257Z" }, 987 + { url = "https://files.pythonhosted.org/packages/00/07/575a68c754943058c78f30db02ee03a64b3c638586fba6a6dd56830b30a3/markupsafe-3.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:133a43e73a802c5562be9bbcd03d090aa5a1fe899db609c29e8c8d815c5f6de6", size = 24374, upload-time = "2025-09-27T18:36:44.508Z" }, 988 + { url = "https://files.pythonhosted.org/packages/a9/21/9b05698b46f218fc0e118e1f8168395c65c8a2c750ae2bab54fc4bd4e0e8/markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ccfcd093f13f0f0b7fdd0f198b90053bf7b2f02a3927a30e63f3ccc9df56b676", size = 22980, upload-time = "2025-09-27T18:36:45.385Z" }, 989 + { url = "https://files.pythonhosted.org/packages/7f/71/544260864f893f18b6827315b988c146b559391e6e7e8f7252839b1b846a/markupsafe-3.0.3-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:509fa21c6deb7a7a273d629cf5ec029bc209d1a51178615ddf718f5918992ab9", size = 21990, upload-time = "2025-09-27T18:36:46.916Z" }, 990 + { url = "https://files.pythonhosted.org/packages/c2/28/b50fc2f74d1ad761af2f5dcce7492648b983d00a65b8c0e0cb457c82ebbe/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4afe79fb3de0b7097d81da19090f4df4f8d3a2b3adaa8764138aac2e44f3af1", size = 23784, upload-time = "2025-09-27T18:36:47.884Z" }, 991 + { url = "https://files.pythonhosted.org/packages/ed/76/104b2aa106a208da8b17a2fb72e033a5a9d7073c68f7e508b94916ed47a9/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:795e7751525cae078558e679d646ae45574b47ed6e7771863fcc079a6171a0fc", size = 21588, upload-time = "2025-09-27T18:36:48.82Z" }, 992 + { url = "https://files.pythonhosted.org/packages/b5/99/16a5eb2d140087ebd97180d95249b00a03aa87e29cc224056274f2e45fd6/markupsafe-3.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8485f406a96febb5140bfeca44a73e3ce5116b2501ac54fe953e488fb1d03b12", size = 23041, upload-time = "2025-09-27T18:36:49.797Z" }, 993 + { url = "https://files.pythonhosted.org/packages/19/bc/e7140ed90c5d61d77cea142eed9f9c303f4c4806f60a1044c13e3f1471d0/markupsafe-3.0.3-cp313-cp313-win32.whl", hash = "sha256:bdd37121970bfd8be76c5fb069c7751683bdf373db1ed6c010162b2a130248ed", size = 14543, upload-time = "2025-09-27T18:36:51.584Z" }, 994 + { url = "https://files.pythonhosted.org/packages/05/73/c4abe620b841b6b791f2edc248f556900667a5a1cf023a6646967ae98335/markupsafe-3.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:9a1abfdc021a164803f4d485104931fb8f8c1efd55bc6b748d2f5774e78b62c5", size = 15113, upload-time = "2025-09-27T18:36:52.537Z" }, 995 + { url = "https://files.pythonhosted.org/packages/f0/3a/fa34a0f7cfef23cf9500d68cb7c32dd64ffd58a12b09225fb03dd37d5b80/markupsafe-3.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:7e68f88e5b8799aa49c85cd116c932a1ac15caaa3f5db09087854d218359e485", size = 13911, upload-time = "2025-09-27T18:36:53.513Z" }, 996 + { url = "https://files.pythonhosted.org/packages/e4/d7/e05cd7efe43a88a17a37b3ae96e79a19e846f3f456fe79c57ca61356ef01/markupsafe-3.0.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:218551f6df4868a8d527e3062d0fb968682fe92054e89978594c28e642c43a73", size = 11658, upload-time = "2025-09-27T18:36:54.819Z" }, 997 + { url = "https://files.pythonhosted.org/packages/99/9e/e412117548182ce2148bdeacdda3bb494260c0b0184360fe0d56389b523b/markupsafe-3.0.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3524b778fe5cfb3452a09d31e7b5adefeea8c5be1d43c4f810ba09f2ceb29d37", size = 12066, upload-time = "2025-09-27T18:36:55.714Z" }, 998 + { url = "https://files.pythonhosted.org/packages/bc/e6/fa0ffcda717ef64a5108eaa7b4f5ed28d56122c9a6d70ab8b72f9f715c80/markupsafe-3.0.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e885a3d1efa2eadc93c894a21770e4bc67899e3543680313b09f139e149ab19", size = 25639, upload-time = "2025-09-27T18:36:56.908Z" }, 999 + { url = "https://files.pythonhosted.org/packages/96/ec/2102e881fe9d25fc16cb4b25d5f5cde50970967ffa5dddafdb771237062d/markupsafe-3.0.3-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8709b08f4a89aa7586de0aadc8da56180242ee0ada3999749b183aa23df95025", size = 23569, upload-time = "2025-09-27T18:36:57.913Z" }, 1000 + { url = "https://files.pythonhosted.org/packages/4b/30/6f2fce1f1f205fc9323255b216ca8a235b15860c34b6798f810f05828e32/markupsafe-3.0.3-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b8512a91625c9b3da6f127803b166b629725e68af71f8184ae7e7d54686a56d6", size = 23284, upload-time = "2025-09-27T18:36:58.833Z" }, 1001 + { url = "https://files.pythonhosted.org/packages/58/47/4a0ccea4ab9f5dcb6f79c0236d954acb382202721e704223a8aafa38b5c8/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9b79b7a16f7fedff2495d684f2b59b0457c3b493778c9eed31111be64d58279f", size = 24801, upload-time = "2025-09-27T18:36:59.739Z" }, 1002 + { url = "https://files.pythonhosted.org/packages/6a/70/3780e9b72180b6fecb83a4814d84c3bf4b4ae4bf0b19c27196104149734c/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:12c63dfb4a98206f045aa9563db46507995f7ef6d83b2f68eda65c307c6829eb", size = 22769, upload-time = "2025-09-27T18:37:00.719Z" }, 1003 + { url = "https://files.pythonhosted.org/packages/98/c5/c03c7f4125180fc215220c035beac6b9cb684bc7a067c84fc69414d315f5/markupsafe-3.0.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8f71bc33915be5186016f675cd83a1e08523649b0e33efdb898db577ef5bb009", size = 23642, upload-time = "2025-09-27T18:37:01.673Z" }, 1004 + { url = "https://files.pythonhosted.org/packages/80/d6/2d1b89f6ca4bff1036499b1e29a1d02d282259f3681540e16563f27ebc23/markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354", size = 14612, upload-time = "2025-09-27T18:37:02.639Z" }, 1005 + { url = "https://files.pythonhosted.org/packages/2b/98/e48a4bfba0a0ffcf9925fe2d69240bfaa19c6f7507b8cd09c70684a53c1e/markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218", size = 15200, upload-time = "2025-09-27T18:37:03.582Z" }, 1006 + { url = "https://files.pythonhosted.org/packages/0e/72/e3cc540f351f316e9ed0f092757459afbc595824ca724cbc5a5d4263713f/markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", size = 13973, upload-time = "2025-09-27T18:37:04.929Z" }, 1007 + { url = "https://files.pythonhosted.org/packages/33/8a/8e42d4838cd89b7dde187011e97fe6c3af66d8c044997d2183fbd6d31352/markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe", size = 11619, upload-time = "2025-09-27T18:37:06.342Z" }, 1008 + { url = "https://files.pythonhosted.org/packages/b5/64/7660f8a4a8e53c924d0fa05dc3a55c9cee10bbd82b11c5afb27d44b096ce/markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026", size = 12029, upload-time = "2025-09-27T18:37:07.213Z" }, 1009 + { url = "https://files.pythonhosted.org/packages/da/ef/e648bfd021127bef5fa12e1720ffed0c6cbb8310c8d9bea7266337ff06de/markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737", size = 24408, upload-time = "2025-09-27T18:37:09.572Z" }, 1010 + { url = "https://files.pythonhosted.org/packages/41/3c/a36c2450754618e62008bf7435ccb0f88053e07592e6028a34776213d877/markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97", size = 23005, upload-time = "2025-09-27T18:37:10.58Z" }, 1011 + { url = "https://files.pythonhosted.org/packages/bc/20/b7fdf89a8456b099837cd1dc21974632a02a999ec9bf7ca3e490aacd98e7/markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d", size = 22048, upload-time = "2025-09-27T18:37:11.547Z" }, 1012 + { url = "https://files.pythonhosted.org/packages/9a/a7/591f592afdc734f47db08a75793a55d7fbcc6902a723ae4cfbab61010cc5/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda", size = 23821, upload-time = "2025-09-27T18:37:12.48Z" }, 1013 + { url = "https://files.pythonhosted.org/packages/7d/33/45b24e4f44195b26521bc6f1a82197118f74df348556594bd2262bda1038/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf", size = 21606, upload-time = "2025-09-27T18:37:13.485Z" }, 1014 + { url = "https://files.pythonhosted.org/packages/ff/0e/53dfaca23a69fbfbbf17a4b64072090e70717344c52eaaaa9c5ddff1e5f0/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe", size = 23043, upload-time = "2025-09-27T18:37:14.408Z" }, 1015 + { url = "https://files.pythonhosted.org/packages/46/11/f333a06fc16236d5238bfe74daccbca41459dcd8d1fa952e8fbd5dccfb70/markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9", size = 14747, upload-time = "2025-09-27T18:37:15.36Z" }, 1016 + { url = "https://files.pythonhosted.org/packages/28/52/182836104b33b444e400b14f797212f720cbc9ed6ba34c800639d154e821/markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", size = 15341, upload-time = "2025-09-27T18:37:16.496Z" }, 1017 + { url = "https://files.pythonhosted.org/packages/6f/18/acf23e91bd94fd7b3031558b1f013adfa21a8e407a3fdb32745538730382/markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4", size = 14073, upload-time = "2025-09-27T18:37:17.476Z" }, 1018 + { url = "https://files.pythonhosted.org/packages/3c/f0/57689aa4076e1b43b15fdfa646b04653969d50cf30c32a102762be2485da/markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab", size = 11661, upload-time = "2025-09-27T18:37:18.453Z" }, 1019 + { url = "https://files.pythonhosted.org/packages/89/c3/2e67a7ca217c6912985ec766c6393b636fb0c2344443ff9d91404dc4c79f/markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175", size = 12069, upload-time = "2025-09-27T18:37:19.332Z" }, 1020 + { url = "https://files.pythonhosted.org/packages/f0/00/be561dce4e6ca66b15276e184ce4b8aec61fe83662cce2f7d72bd3249d28/markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634", size = 25670, upload-time = "2025-09-27T18:37:20.245Z" }, 1021 + { url = "https://files.pythonhosted.org/packages/50/09/c419f6f5a92e5fadde27efd190eca90f05e1261b10dbd8cbcb39cd8ea1dc/markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50", size = 23598, upload-time = "2025-09-27T18:37:21.177Z" }, 1022 + { url = "https://files.pythonhosted.org/packages/22/44/a0681611106e0b2921b3033fc19bc53323e0b50bc70cffdd19f7d679bb66/markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e", size = 23261, upload-time = "2025-09-27T18:37:22.167Z" }, 1023 + { url = "https://files.pythonhosted.org/packages/5f/57/1b0b3f100259dc9fffe780cfb60d4be71375510e435efec3d116b6436d43/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5", size = 24835, upload-time = "2025-09-27T18:37:23.296Z" }, 1024 + { url = "https://files.pythonhosted.org/packages/26/6a/4bf6d0c97c4920f1597cc14dd720705eca0bf7c787aebc6bb4d1bead5388/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523", size = 22733, upload-time = "2025-09-27T18:37:24.237Z" }, 1025 + { url = "https://files.pythonhosted.org/packages/14/c7/ca723101509b518797fedc2fdf79ba57f886b4aca8a7d31857ba3ee8281f/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc", size = 23672, upload-time = "2025-09-27T18:37:25.271Z" }, 1026 + { url = "https://files.pythonhosted.org/packages/fb/df/5bd7a48c256faecd1d36edc13133e51397e41b73bb77e1a69deab746ebac/markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", size = 14819, upload-time = "2025-09-27T18:37:26.285Z" }, 1027 + { url = "https://files.pythonhosted.org/packages/1a/8a/0402ba61a2f16038b48b39bccca271134be00c5c9f0f623208399333c448/markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", size = 15426, upload-time = "2025-09-27T18:37:27.316Z" }, 1028 + { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146, upload-time = "2025-09-27T18:37:28.327Z" }, 1029 + ] 1030 + 1031 + [[package]] 865 1032 name = "mcp" 866 - version = "1.12.0" 1033 + version = "1.16.0" 867 1034 source = { registry = "https://pypi.org/simple" } 868 1035 dependencies = [ 869 1036 { name = "anyio" }, ··· 878 1045 { name = "starlette" }, 879 1046 { name = "uvicorn", marker = "sys_platform != 'emscripten'" }, 880 1047 ] 881 - sdist = { url = "https://files.pythonhosted.org/packages/45/94/caa0f4754e2437f7033068989f13fee784856f95870c786b0b5c2c0f511e/mcp-1.12.0.tar.gz", hash = "sha256:853f6b17a3f31ea6e2f278c2ec7d3b38457bc80c7c2c675260dd7f04a6fd0e70", size = 424678, upload-time = "2025-07-17T19:46:35.522Z" } 1048 + sdist = { url = "https://files.pythonhosted.org/packages/3d/a1/b1f328da3b153683d2ec34f849b4b6eac2790fb240e3aef06ff2fab3df9d/mcp-1.16.0.tar.gz", hash = "sha256:39b8ca25460c578ee2cdad33feeea122694cfdf73eef58bee76c42f6ef0589df", size = 472918, upload-time = "2025-10-02T16:58:20.631Z" } 882 1049 wheels = [ 883 - { url = "https://files.pythonhosted.org/packages/ed/da/c7eaab6a58f1034de115b7902141ad8f81b4f3bbf7dc0cc267594947a4d7/mcp-1.12.0-py3-none-any.whl", hash = "sha256:19a498b2bf273283e463b4dd1ed83f791fbba5c25bfa16b8b34cfd5571673e7f", size = 158470, upload-time = "2025-07-17T19:46:34.166Z" }, 1050 + { url = "https://files.pythonhosted.org/packages/c9/0e/7cebc88e17daf94ebe28c95633af595ccb2864dc2ee7abd75542d98495cc/mcp-1.16.0-py3-none-any.whl", hash = "sha256:ec917be9a5d31b09ba331e1768aa576e0af45470d657a0319996a20a57d7d633", size = 167266, upload-time = "2025-10-02T16:58:19.039Z" }, 884 1051 ] 885 1052 886 1053 [[package]] ··· 909 1076 ] 910 1077 911 1078 [[package]] 1079 + name = "more-itertools" 1080 + version = "10.8.0" 1081 + source = { registry = "https://pypi.org/simple" } 1082 + sdist = { url = "https://files.pythonhosted.org/packages/ea/5d/38b681d3fce7a266dd9ab73c66959406d565b3e85f21d5e66e1181d93721/more_itertools-10.8.0.tar.gz", hash = "sha256:f638ddf8a1a0d134181275fb5d58b086ead7c6a72429ad725c67503f13ba30bd", size = 137431, upload-time = "2025-09-02T15:23:11.018Z" } 1083 + wheels = [ 1084 + { url = "https://files.pythonhosted.org/packages/a4/8e/469e5a4a2f5855992e425f3cb33804cc07bf18d48f2db061aec61ce50270/more_itertools-10.8.0-py3-none-any.whl", hash = "sha256:52d4362373dcf7c52546bc4af9a86ee7c4579df9a8dc268be0a2f949d376cc9b", size = 69667, upload-time = "2025-09-02T15:23:09.635Z" }, 1085 + ] 1086 + 1087 + [[package]] 912 1088 name = "multidict" 913 1089 version = "6.6.3" 914 1090 source = { registry = "https://pypi.org/simple" } ··· 991 1167 ] 992 1168 993 1169 [[package]] 1170 + name = "openapi-core" 1171 + version = "0.19.5" 1172 + source = { registry = "https://pypi.org/simple" } 1173 + dependencies = [ 1174 + { name = "isodate" }, 1175 + { name = "jsonschema" }, 1176 + { name = "jsonschema-path" }, 1177 + { name = "more-itertools" }, 1178 + { name = "openapi-schema-validator" }, 1179 + { name = "openapi-spec-validator" }, 1180 + { name = "parse" }, 1181 + { name = "typing-extensions" }, 1182 + { name = "werkzeug" }, 1183 + ] 1184 + sdist = { url = "https://files.pythonhosted.org/packages/b1/35/1acaa5f2fcc6e54eded34a2ec74b479439c4e469fc4e8d0e803fda0234db/openapi_core-0.19.5.tar.gz", hash = "sha256:421e753da56c391704454e66afe4803a290108590ac8fa6f4a4487f4ec11f2d3", size = 103264, upload-time = "2025-03-20T20:17:28.193Z" } 1185 + wheels = [ 1186 + { url = "https://files.pythonhosted.org/packages/27/6f/83ead0e2e30a90445ee4fc0135f43741aebc30cca5b43f20968b603e30b6/openapi_core-0.19.5-py3-none-any.whl", hash = "sha256:ef7210e83a59394f46ce282639d8d26ad6fc8094aa904c9c16eb1bac8908911f", size = 106595, upload-time = "2025-03-20T20:17:26.77Z" }, 1187 + ] 1188 + 1189 + [[package]] 1190 + name = "openapi-pydantic" 1191 + version = "0.5.1" 1192 + source = { registry = "https://pypi.org/simple" } 1193 + dependencies = [ 1194 + { name = "pydantic" }, 1195 + ] 1196 + sdist = { url = "https://files.pythonhosted.org/packages/02/2e/58d83848dd1a79cb92ed8e63f6ba901ca282c5f09d04af9423ec26c56fd7/openapi_pydantic-0.5.1.tar.gz", hash = "sha256:ff6835af6bde7a459fb93eb93bb92b8749b754fc6e51b2f1590a19dc3005ee0d", size = 60892, upload-time = "2025-01-08T19:29:27.083Z" } 1197 + wheels = [ 1198 + { url = "https://files.pythonhosted.org/packages/12/cf/03675d8bd8ecbf4445504d8071adab19f5f993676795708e36402ab38263/openapi_pydantic-0.5.1-py3-none-any.whl", hash = "sha256:a3a09ef4586f5bd760a8df7f43028b60cafb6d9f61de2acba9574766255ab146", size = 96381, upload-time = "2025-01-08T19:29:25.275Z" }, 1199 + ] 1200 + 1201 + [[package]] 1202 + name = "openapi-schema-validator" 1203 + version = "0.6.3" 1204 + source = { registry = "https://pypi.org/simple" } 1205 + dependencies = [ 1206 + { name = "jsonschema" }, 1207 + { name = "jsonschema-specifications" }, 1208 + { name = "rfc3339-validator" }, 1209 + ] 1210 + sdist = { url = "https://files.pythonhosted.org/packages/8b/f3/5507ad3325169347cd8ced61c232ff3df70e2b250c49f0fe140edb4973c6/openapi_schema_validator-0.6.3.tar.gz", hash = "sha256:f37bace4fc2a5d96692f4f8b31dc0f8d7400fd04f3a937798eaf880d425de6ee", size = 11550, upload-time = "2025-01-10T18:08:22.268Z" } 1211 + wheels = [ 1212 + { url = "https://files.pythonhosted.org/packages/21/c6/ad0fba32775ae749016829dace42ed80f4407b171da41313d1a3a5f102e4/openapi_schema_validator-0.6.3-py3-none-any.whl", hash = "sha256:f3b9870f4e556b5a62a1c39da72a6b4b16f3ad9c73dc80084b1b11e74ba148a3", size = 8755, upload-time = "2025-01-10T18:08:19.758Z" }, 1213 + ] 1214 + 1215 + [[package]] 1216 + name = "openapi-spec-validator" 1217 + version = "0.7.2" 1218 + source = { registry = "https://pypi.org/simple" } 1219 + dependencies = [ 1220 + { name = "jsonschema" }, 1221 + { name = "jsonschema-path" }, 1222 + { name = "lazy-object-proxy" }, 1223 + { name = "openapi-schema-validator" }, 1224 + ] 1225 + sdist = { url = "https://files.pythonhosted.org/packages/82/af/fe2d7618d6eae6fb3a82766a44ed87cd8d6d82b4564ed1c7cfb0f6378e91/openapi_spec_validator-0.7.2.tar.gz", hash = "sha256:cc029309b5c5dbc7859df0372d55e9d1ff43e96d678b9ba087f7c56fc586f734", size = 36855, upload-time = "2025-06-07T14:48:56.299Z" } 1226 + wheels = [ 1227 + { url = "https://files.pythonhosted.org/packages/27/dd/b3fd642260cb17532f66cc1e8250f3507d1e580483e209dc1e9d13bd980d/openapi_spec_validator-0.7.2-py3-none-any.whl", hash = "sha256:4bbdc0894ec85f1d1bea1d6d9c8b2c3c8d7ccaa13577ef40da9c006c9fd0eb60", size = 39713, upload-time = "2025-06-07T14:48:54.077Z" }, 1228 + ] 1229 + 1230 + [[package]] 994 1231 name = "opentelemetry-api" 995 1232 version = "1.35.0" 996 1233 source = { registry = "https://pypi.org/simple" } ··· 1010 1247 sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" } 1011 1248 wheels = [ 1012 1249 { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, 1250 + ] 1251 + 1252 + [[package]] 1253 + name = "parse" 1254 + version = "1.20.2" 1255 + source = { registry = "https://pypi.org/simple" } 1256 + sdist = { url = "https://files.pythonhosted.org/packages/4f/78/d9b09ba24bb36ef8b83b71be547e118d46214735b6dfb39e4bfde0e9b9dd/parse-1.20.2.tar.gz", hash = "sha256:b41d604d16503c79d81af5165155c0b20f6c8d6c559efa66b4b695c3e5a0a0ce", size = 29391, upload-time = "2024-06-11T04:41:57.34Z" } 1257 + wheels = [ 1258 + { url = "https://files.pythonhosted.org/packages/d0/31/ba45bf0b2aa7898d81cbbfac0e88c267befb59ad91a19e36e1bc5578ddb1/parse-1.20.2-py2.py3-none-any.whl", hash = "sha256:967095588cb802add9177d0c0b6133b5ba33b1ea9007ca800e526f42a85af558", size = 20126, upload-time = "2024-06-11T04:41:55.057Z" }, 1259 + ] 1260 + 1261 + [[package]] 1262 + name = "pathable" 1263 + version = "0.4.4" 1264 + source = { registry = "https://pypi.org/simple" } 1265 + sdist = { url = "https://files.pythonhosted.org/packages/67/93/8f2c2075b180c12c1e9f6a09d1a985bc2036906b13dff1d8917e395f2048/pathable-0.4.4.tar.gz", hash = "sha256:6905a3cd17804edfac7875b5f6c9142a218c7caef78693c2dbbbfbac186d88b2", size = 8124, upload-time = "2025-01-10T18:43:13.247Z" } 1266 + wheels = [ 1267 + { url = "https://files.pythonhosted.org/packages/7d/eb/b6260b31b1a96386c0a880edebe26f89669098acea8e0318bff6adb378fd/pathable-0.4.4-py3-none-any.whl", hash = "sha256:5ae9e94793b6ef5a4cbe0a7ce9dbbefc1eec38df253763fd0aeeacf2762dbbc2", size = 9592, upload-time = "2025-01-10T18:43:11.88Z" }, 1013 1268 ] 1014 1269 1015 1270 [[package]] ··· 1112 1367 ] 1113 1368 1114 1369 [[package]] 1115 - name = "pybase64" 1116 - version = "1.4.1" 1117 - source = { registry = "https://pypi.org/simple" } 1118 - sdist = { url = "https://files.pythonhosted.org/packages/38/32/5d25a15256d2e80d1e92be821f19fc49190e65a90ea86733cb5af2285449/pybase64-1.4.1.tar.gz", hash = "sha256:03fc365c601671add4f9e0713c2bc2485fa4ab2b32f0d3bb060bd7e069cdaa43", size = 136836, upload-time = "2025-03-02T11:13:57.109Z" } 1119 - wheels = [ 1120 - { url = "https://files.pythonhosted.org/packages/a6/a9/43bac4f39401f7241d233ddaf9e6561860b2466798cfb83b9e7dbf89bc1b/pybase64-1.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:bbdcf77e424c91389f22bf10158851ce05c602c50a74ccf5943ee3f5ef4ba489", size = 38152, upload-time = "2025-03-02T11:11:07.576Z" }, 1121 - { url = "https://files.pythonhosted.org/packages/1e/bb/d0ae801e31a5052dbb1744a45318f822078dd4ce4cc7f49bfe97e7768f7e/pybase64-1.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:af41e2e6015f980d15eae0df0c365df94c7587790aea236ba0bf48c65a9fa04e", size = 31488, upload-time = "2025-03-02T11:11:09.758Z" }, 1122 - { url = "https://files.pythonhosted.org/packages/be/34/bf4119a88b2ad0536a8ed9d66ce4d70ff8152eac00ef8a27e5ae35da4328/pybase64-1.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9ac21c1943a15552347305943b1d0d6298fb64a98b67c750cb8fb2c190cdefd4", size = 59734, upload-time = "2025-03-02T11:11:11.493Z" }, 1123 - { url = "https://files.pythonhosted.org/packages/99/1c/1901547adc7d4f24bdcb2f75cb7dcd3975bff42f39da37d4bd218c608c60/pybase64-1.4.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:65567e8f4f31cf6e1a8cc570723cc6b18adda79b4387a18f8d93c157ff5f1979", size = 56529, upload-time = "2025-03-02T11:11:12.657Z" }, 1124 - { url = "https://files.pythonhosted.org/packages/c5/1e/1993e4b9a03e94fc53552285e3998079d864fff332798bf30c25afdac8f3/pybase64-1.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:988e987f8cfe2dfde7475baf5f12f82b2f454841aef3a174b694a57a92d5dfb0", size = 59114, upload-time = "2025-03-02T11:11:13.972Z" }, 1125 - { url = "https://files.pythonhosted.org/packages/c5/f6/061fee5b7ba38b8824dd95752ab7115cf183ffbd3330d5fc1734a47b0f9e/pybase64-1.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:92b2305ac2442b451e19d42c4650c3bb090d6aa9abd87c0c4d700267d8fa96b1", size = 60095, upload-time = "2025-03-02T11:11:15.182Z" }, 1126 - { url = "https://files.pythonhosted.org/packages/37/da/ccfe5d1a9f1188cd703390522e96a31045c5b93af84df04a98e69ada5c8b/pybase64-1.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d1ff80e03357b09dab016f41b4c75cf06e9b19cda7f898e4f3681028a3dff29b", size = 68431, upload-time = "2025-03-02T11:11:17.059Z" }, 1127 - { url = "https://files.pythonhosted.org/packages/c3/d3/8ca4b0695876b52c0073a3557a65850b6d5c723333b5a271ab10a1085852/pybase64-1.4.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2cdda297e668e118f6b9ba804e858ff49e3dd945d01fdd147de90445fd08927d", size = 71417, upload-time = "2025-03-02T11:11:19.178Z" }, 1128 - { url = "https://files.pythonhosted.org/packages/94/34/5f8f72d1b7b4ddb64c48d60160f3f4f03cfd0bfd2e7068d4558499d948ed/pybase64-1.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:51a24d21a21a959eb8884f24346a6480c4bd624aa7976c9761504d847a2f9364", size = 58429, upload-time = "2025-03-02T11:11:20.351Z" }, 1129 - { url = "https://files.pythonhosted.org/packages/95/b7/edf53af308c6e8aada1e6d6a0a3789176af8cbae37a2ce084eb9da87bf33/pybase64-1.4.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:b19e169ea1b8a15a03d3a379116eb7b17740803e89bc6eb3efcc74f532323cf7", size = 52228, upload-time = "2025-03-02T11:11:21.632Z" }, 1130 - { url = "https://files.pythonhosted.org/packages/0c/bf/c9df141e24a259f38a38bdda5a3b63206f13e612ecbd3880fa10625e0294/pybase64-1.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:8a9f1b614efd41240c9bb2cf66031aa7a2c3c092c928f9d429511fe18d4a3fd1", size = 68632, upload-time = "2025-03-02T11:11:23.56Z" }, 1131 - { url = "https://files.pythonhosted.org/packages/e9/ae/1aec72325a3c48f7776cc55a3bab8b168eb77aea821253da8b9f09713734/pybase64-1.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:d9947b5e289e2c5b018ddc2aee2b9ed137b8aaaba7edfcb73623e576a2407740", size = 57682, upload-time = "2025-03-02T11:11:25.656Z" }, 1132 - { url = "https://files.pythonhosted.org/packages/4d/7a/7ad2799c0b3c4e2f7b993e1636468445c30870ca5485110b589b8921808d/pybase64-1.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:ba4184ea43aa88a5ab8d6d15db284689765c7487ff3810764d8d823b545158e6", size = 56308, upload-time = "2025-03-02T11:11:26.803Z" }, 1133 - { url = "https://files.pythonhosted.org/packages/be/01/6008a4fbda0c4308dab00b95aedde8748032d7620bd95b686619c66917fe/pybase64-1.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4471257628785296efb2d50077fb9dfdbd4d2732c3487795224dd2644216fb07", size = 70784, upload-time = "2025-03-02T11:11:28.427Z" }, 1134 - { url = "https://files.pythonhosted.org/packages/27/31/913365a4f0e2922ec369ddaa3a1d6c11059acbe54531b003653efa007a48/pybase64-1.4.1-cp312-cp312-win32.whl", hash = "sha256:614561297ad14de315dd27381fd6ec3ea4de0d8206ba4c7678449afaff8a2009", size = 34271, upload-time = "2025-03-02T11:11:30.585Z" }, 1135 - { url = "https://files.pythonhosted.org/packages/d9/98/4d514d3e4c04819d80bccf9ea7b30d1cfc701832fa5ffca168f585004488/pybase64-1.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:35635db0d64fcbe9b3fad265314c052c47dc9bcef8dea17493ea8e3c15b2b972", size = 36496, upload-time = "2025-03-02T11:11:32.552Z" }, 1136 - { url = "https://files.pythonhosted.org/packages/c4/61/01353bc9c461e7b36d692daca3eee9616d8936ea6d8a64255ef7ec9ac307/pybase64-1.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:b4ccb438c4208ff41a260b70994c30a8631051f3b025cdca48be586b068b8f49", size = 29692, upload-time = "2025-03-02T11:11:33.735Z" }, 1137 - { url = "https://files.pythonhosted.org/packages/4b/1a/4e243ba702c07df3df3ba1795cfb02cf7a4242c53fc574b06a2bfa4f8478/pybase64-1.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d1c38d9c4a7c132d45859af8d5364d3ce90975a42bd5995d18d174fb57621973", size = 38149, upload-time = "2025-03-02T11:11:35.537Z" }, 1138 - { url = "https://files.pythonhosted.org/packages/9c/35/3eae81bc8688a83f8b5bb84979d88e2cc3c3279a3b870a506f277d746c56/pybase64-1.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ab0b93ea93cf1f56ca4727d678a9c0144c2653e9de4e93e789a92b4e098c07d9", size = 31485, upload-time = "2025-03-02T11:11:36.656Z" }, 1139 - { url = "https://files.pythonhosted.org/packages/48/55/d99b9ff8083573bbf97fc433bbc20e2efb612792025f3bad0868c96c37ce/pybase64-1.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:644f393e9bb7f3bacc5cbd3534d02e1b660b258fc8315ecae74d2e23265e5c1f", size = 59738, upload-time = "2025-03-02T11:11:38.468Z" }, 1140 - { url = "https://files.pythonhosted.org/packages/63/3c/051512b9e139a11585447b286ede5ac3b284ce5df85de37eb8cff57d90f8/pybase64-1.4.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ff172a4dacbd964e5edcf1c2152dae157aabf856508aed15276f46d04a22128e", size = 56239, upload-time = "2025-03-02T11:11:39.718Z" }, 1141 - { url = "https://files.pythonhosted.org/packages/af/11/f40c5cca587274d50baee88540a7839576204cb425fe2f73a752ea48ae74/pybase64-1.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b2ab7b4535abc72d40114540cae32c9e07d76ffba132bdd5d4fff5fe340c5801", size = 59137, upload-time = "2025-03-02T11:11:41.524Z" }, 1142 - { url = "https://files.pythonhosted.org/packages/1a/a9/ace9f6d0926962c083671d7df247de442ef63cd06bd134f7c8251aab5c51/pybase64-1.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:da66eb7cfb641486944fb0b95ab138e691ab78503115022caf992b6c89b10396", size = 60109, upload-time = "2025-03-02T11:11:42.699Z" }, 1143 - { url = "https://files.pythonhosted.org/packages/88/9c/d4e308b4b4e3b513bc084fc71b4e2dd00d21d4cd245a9a28144d2f6b03c9/pybase64-1.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:678f573ea1d06183b32d0336044fb5db60396333599dffcce28ffa3b68319fc0", size = 68391, upload-time = "2025-03-02T11:11:43.898Z" }, 1144 - { url = "https://files.pythonhosted.org/packages/53/87/e184bf982a3272f1021f417e5a18fac406e042c606950e9082fc3b0cec30/pybase64-1.4.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4bccdf340c2a1d3dd1f41528f192265ddce7f8df1ee4f7b5b9163cdba0fe0ccb", size = 71438, upload-time = "2025-03-02T11:11:45.112Z" }, 1145 - { url = "https://files.pythonhosted.org/packages/2f/7f/d6e6a72db055eb2dc01ab877d8ee39d05cb665403433ff922fb95d1003ad/pybase64-1.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1ddf6366c34eb78931fd8a47c00cb886ba187a5ff8e6dbffe1d9dae4754b6c28", size = 58437, upload-time = "2025-03-02T11:11:47.034Z" }, 1146 - { url = "https://files.pythonhosted.org/packages/71/ef/c9051f2c0128194b861f3cd3b2d211b8d4d21ed2be354aa669fe29a059d8/pybase64-1.4.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:500afcb717a84e262c68f0baf9c56abaf97e2f058ba80c5546a9ed21ff4b705f", size = 52267, upload-time = "2025-03-02T11:11:48.448Z" }, 1147 - { url = "https://files.pythonhosted.org/packages/12/92/ae30a54eaa437989839c4f2404c1f004d7383c0f46d6ebb83546d587d2a7/pybase64-1.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d2de043312a1e7f15ee6d2b7d9e39ee6afe24f144e2248cce942b6be357b70d8", size = 68659, upload-time = "2025-03-02T11:11:49.615Z" }, 1148 - { url = "https://files.pythonhosted.org/packages/2b/65/d94788a35904f21694c4c581bcee2e165bec2408cc6fbed85a7fef5959ae/pybase64-1.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c36e214c25fb8dd4f3ecdaa0ff90073b793056e0065cc0a1e1e5525a6866a1ad", size = 57727, upload-time = "2025-03-02T11:11:50.843Z" }, 1149 - { url = "https://files.pythonhosted.org/packages/d0/97/8db416066b7917909c38346c03a8f3e6d4fc8a1dc98636408156514269ad/pybase64-1.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:8ec003224f6e36e8e607a1bb8df182b367c87ca7135788ffe89173c7d5085005", size = 56302, upload-time = "2025-03-02T11:11:52.547Z" }, 1150 - { url = "https://files.pythonhosted.org/packages/70/0b/98f0601391befe0f19aa8cbda821c62d95056a94cc41d452fe893d205523/pybase64-1.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c536c6ed161e6fb19f6acd6074f29a4c78cb41c9155c841d56aec1a4d20d5894", size = 70779, upload-time = "2025-03-02T11:11:53.735Z" }, 1151 - { url = "https://files.pythonhosted.org/packages/cc/07/116119c5b20688c052697f677cf56f05aa766535ff7691aba38447d4a0d8/pybase64-1.4.1-cp313-cp313-win32.whl", hash = "sha256:1d34872e5aa2eff9dc54cedaf36038bbfbd5a3440fdf0bdc5b3c81c54ef151ea", size = 34266, upload-time = "2025-03-02T11:11:54.892Z" }, 1152 - { url = "https://files.pythonhosted.org/packages/c0/f5/a7eed9f3692209a9869a28bdd92deddf8cbffb06b40954f89f4577e5c96e/pybase64-1.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:8b7765515d7e0a48ddfde914dc2b1782234ac188ce3fab173b078a6e82ec7017", size = 36488, upload-time = "2025-03-02T11:11:56.063Z" }, 1153 - { url = "https://files.pythonhosted.org/packages/5d/8a/0d65c4dcda06487305035f24888ffed219897c03fb7834635d5d5e27dae1/pybase64-1.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:7fb782f3ceb30e24dc4d8d99c1221a381917bffaf85d29542f0f25b51829987c", size = 29690, upload-time = "2025-03-02T11:11:57.702Z" }, 1154 - { url = "https://files.pythonhosted.org/packages/a3/83/646d65fafe5e6edbdaf4c9548efb2e1dd7784caddbde3ff8a843dd942b0f/pybase64-1.4.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2a98d323e97444a38db38e022ccaf1d3e053b1942455790a93f29086c687855f", size = 38506, upload-time = "2025-03-02T11:11:58.936Z" }, 1155 - { url = "https://files.pythonhosted.org/packages/87/14/dbf7fbbe91d71c8044fefe20d22480ad64097e2ba424944de512550e12a4/pybase64-1.4.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:19ef58d36b9b32024768fcedb024f32c05eb464128c75c07cac2b50c9ed47f4a", size = 31894, upload-time = "2025-03-02T11:12:00.762Z" }, 1156 - { url = "https://files.pythonhosted.org/packages/bd/5d/f8a47da2a5f8b599297b307d3bd0293adedc4e135be310620f061906070f/pybase64-1.4.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:04fee0f5c174212868fde97b109db8fac8249b306a00ea323531ee61c7b0f398", size = 65212, upload-time = "2025-03-02T11:12:01.911Z" }, 1157 - { url = "https://files.pythonhosted.org/packages/90/95/ad9869c7cdcce3e8ada619dab5f9f2eff315ffb001704a3718c1597a2119/pybase64-1.4.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:47737ff9eabc14b7553de6bc6395d67c5be80afcdbd25180285d13e089e40888", size = 60300, upload-time = "2025-03-02T11:12:03.071Z" }, 1158 - { url = "https://files.pythonhosted.org/packages/c2/91/4d8268b2488ae10c485cba04ecc23a5a7bdfb47ce9b876017b11ea0249a2/pybase64-1.4.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0d8b5888cc239654fe68a0db196a18575ffc8b1c8c8f670c2971a44e3b7fe682", size = 63773, upload-time = "2025-03-02T11:12:04.231Z" }, 1159 - { url = "https://files.pythonhosted.org/packages/ae/1a/8afd27facc0723b1d69231da8c59a2343feb255f5db16f8b8765ddf1600b/pybase64-1.4.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6a1af8d387dbce05944b65a618639918804b2d4438fed32bb7f06d9c90dbed01", size = 64684, upload-time = "2025-03-02T11:12:05.409Z" }, 1160 - { url = "https://files.pythonhosted.org/packages/cc/cd/422c74397210051125419fc8e425506ff27c04665459e18c8f7b037a754b/pybase64-1.4.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0b0093c52bd099b80e422ad8cddf6f2c1ac1b09cb0922cca04891d736c2ad647", size = 72880, upload-time = "2025-03-02T11:12:06.652Z" }, 1161 - { url = "https://files.pythonhosted.org/packages/04/c1/c4f02f1d5f8e8a3d75715a3dd04196dde9e263e471470d099a26e91ebe2f/pybase64-1.4.1-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15e54f9b2a1686f5bbdc4ac8440b6f6145d9699fd53aa30f347931f3063b0915", size = 75344, upload-time = "2025-03-02T11:12:07.816Z" }, 1162 - { url = "https://files.pythonhosted.org/packages/6e/0b/013006ca984f0472476cf7c0540db2e2b1f997d52977b15842a7681ab79c/pybase64-1.4.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:3a0fdcf13f986c82f7ef04a1cd1163c70f39662d6f02aa4e7b448dacb966b39f", size = 63439, upload-time = "2025-03-02T11:12:09.669Z" }, 1163 - { url = "https://files.pythonhosted.org/packages/8a/d5/7848543b3c8dcc5396be574109acbe16706e6a9b4dbd9fc4e22f211668a9/pybase64-1.4.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:ac03f8eba72dd6da15dc25bb3e1b440ad21f5cb7ee2e6ffbbae4bd1b206bb503", size = 56004, upload-time = "2025-03-02T11:12:10.981Z" }, 1164 - { url = "https://files.pythonhosted.org/packages/63/58/70de1efb1b6f21d7aaea33578868214f82925d969e2091f7de3175a10092/pybase64-1.4.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:ea835272570aa811e08ae17612632b057623a9b27265d44288db666c02b438dc", size = 72460, upload-time = "2025-03-02T11:12:13.122Z" }, 1165 - { url = "https://files.pythonhosted.org/packages/90/0d/aa52dd1b1f25b98b1d94cc0522f864b03de55aa115de67cb6dbbddec4f46/pybase64-1.4.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:8f52c4c29a35381f3ae06d520144a0707132f2cbfb53bc907b74811734bc4ef3", size = 62295, upload-time = "2025-03-02T11:12:15.004Z" }, 1166 - { url = "https://files.pythonhosted.org/packages/39/cf/4d378a330249c937676ee8eab7992ec700ade362f35db36c15922b33b1c8/pybase64-1.4.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:fa5cdabcb4d21b7e56d0b2edd7ed6fa933ac3535be30c2a9cf0a2e270c5369c8", size = 60604, upload-time = "2025-03-02T11:12:16.23Z" }, 1167 - { url = "https://files.pythonhosted.org/packages/15/45/e3f23929018d0aada84246ddd398843050971af614da67450bb20f45f880/pybase64-1.4.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8db9acf239bb71a888748bc9ffc12c97c1079393a38bc180c0548330746ece94", size = 74500, upload-time = "2025-03-02T11:12:17.48Z" }, 1168 - { url = "https://files.pythonhosted.org/packages/8d/98/6d2adaec318cae6ee968a10df0a7e870f17ee385ef623bcb2ab63fa11b59/pybase64-1.4.1-cp313-cp313t-win32.whl", hash = "sha256:bc06186cfa9a43e871fdca47c1379bdf1cfe964bd94a47f0919a1ffab195b39e", size = 34543, upload-time = "2025-03-02T11:12:18.625Z" }, 1169 - { url = "https://files.pythonhosted.org/packages/8e/e7/1823de02d2c23324cf1142e9dce53b032085cee06c3f982806040f975ce7/pybase64-1.4.1-cp313-cp313t-win_amd64.whl", hash = "sha256:02c3647d270af1a3edd35e485bb7ccfe82180b8347c49e09973466165c03d7aa", size = 36909, upload-time = "2025-03-02T11:12:20.122Z" }, 1170 - { url = "https://files.pythonhosted.org/packages/43/6a/8ec0e4461bf89ef0499ef6c746b081f3520a1e710aeb58730bae693e0681/pybase64-1.4.1-cp313-cp313t-win_arm64.whl", hash = "sha256:4b3635e5873707906e72963c447a67969cfc6bac055432a57a91d7a4d5164fdf", size = 29961, upload-time = "2025-03-02T11:12:21.908Z" }, 1171 - ] 1172 - 1173 - [[package]] 1174 1370 name = "pycparser" 1175 1371 version = "2.22" 1176 1372 source = { registry = "https://pypi.org/simple" } ··· 1194 1390 { url = "https://files.pythonhosted.org/packages/6a/c0/ec2b1c8712ca690e5d61979dee872603e92b8a32f94cc1b72d53beab008a/pydantic-2.11.7-py3-none-any.whl", hash = "sha256:dde5df002701f6de26248661f6835bbe296a47bf73990135c7d07ce741b9623b", size = 444782, upload-time = "2025-06-14T08:33:14.905Z" }, 1195 1391 ] 1196 1392 1393 + [package.optional-dependencies] 1394 + email = [ 1395 + { name = "email-validator" }, 1396 + ] 1397 + 1197 1398 [[package]] 1198 1399 name = "pydantic-ai" 1199 1400 version = "0.4.4" ··· 1367 1568 ] 1368 1569 1369 1570 [[package]] 1571 + name = "pyperclip" 1572 + version = "1.11.0" 1573 + source = { registry = "https://pypi.org/simple" } 1574 + sdist = { url = "https://files.pythonhosted.org/packages/e8/52/d87eba7cb129b81563019d1679026e7a112ef76855d6159d24754dbd2a51/pyperclip-1.11.0.tar.gz", hash = "sha256:244035963e4428530d9e3a6101a1ef97209c6825edab1567beac148ccc1db1b6", size = 12185, upload-time = "2025-09-26T14:40:37.245Z" } 1575 + wheels = [ 1576 + { url = "https://files.pythonhosted.org/packages/df/80/fc9d01d5ed37ba4c42ca2b55b4339ae6e200b456be3a1aaddf4a9fa99b8c/pyperclip-1.11.0-py3-none-any.whl", hash = "sha256:299403e9ff44581cb9ba2ffeed69c7aa96a008622ad0c46cb575ca75b5b84273", size = 11063, upload-time = "2025-09-26T14:40:36.069Z" }, 1577 + ] 1578 + 1579 + [[package]] 1370 1580 name = "pytest" 1371 1581 version = "8.4.1" 1372 1582 source = { registry = "https://pypi.org/simple" } ··· 1510 1720 ] 1511 1721 1512 1722 [[package]] 1723 + name = "rfc3339-validator" 1724 + version = "0.1.4" 1725 + source = { registry = "https://pypi.org/simple" } 1726 + dependencies = [ 1727 + { name = "six" }, 1728 + ] 1729 + sdist = { url = "https://files.pythonhosted.org/packages/28/ea/a9387748e2d111c3c2b275ba970b735e04e15cdb1eb30693b6b5708c4dbd/rfc3339_validator-0.1.4.tar.gz", hash = "sha256:138a2abdf93304ad60530167e51d2dfb9549521a836871b88d7f4695d0022f6b", size = 5513, upload-time = "2021-05-12T16:37:54.178Z" } 1730 + wheels = [ 1731 + { url = "https://files.pythonhosted.org/packages/7b/44/4e421b96b67b2daff264473f7465db72fbdf36a07e05494f50300cc7b0c6/rfc3339_validator-0.1.4-py2.py3-none-any.whl", hash = "sha256:24f6ec1eda14ef823da9e36ec7113124b39c04d50a4d3d3a3c2859577e7791fa", size = 3490, upload-time = "2021-05-12T16:37:52.536Z" }, 1732 + ] 1733 + 1734 + [[package]] 1513 1735 name = "rich" 1514 1736 version = "14.0.0" 1515 1737 source = { registry = "https://pypi.org/simple" } ··· 1520 1742 sdist = { url = "https://files.pythonhosted.org/packages/a1/53/830aa4c3066a8ab0ae9a9955976fb770fe9c6102117c8ec4ab3ea62d89e8/rich-14.0.0.tar.gz", hash = "sha256:82f1bc23a6a21ebca4ae0c45af9bdbc492ed20231dcb63f297d6d1021a9d5725", size = 224078, upload-time = "2025-03-30T14:15:14.23Z" } 1521 1743 wheels = [ 1522 1744 { url = "https://files.pythonhosted.org/packages/0d/9b/63f4c7ebc259242c89b3acafdb37b41d1185c07ff0011164674e9076b491/rich-14.0.0-py3-none-any.whl", hash = "sha256:1c9491e1951aac09caffd42f448ee3d04e58923ffe14993f6e83068dc395d7e0", size = 243229, upload-time = "2025-03-30T14:15:12.283Z" }, 1745 + ] 1746 + 1747 + [[package]] 1748 + name = "rich-rst" 1749 + version = "1.3.1" 1750 + source = { registry = "https://pypi.org/simple" } 1751 + dependencies = [ 1752 + { name = "docutils" }, 1753 + { name = "rich" }, 1754 + ] 1755 + sdist = { url = "https://files.pythonhosted.org/packages/b0/69/5514c3a87b5f10f09a34bb011bc0927bc12c596c8dae5915604e71abc386/rich_rst-1.3.1.tar.gz", hash = "sha256:fad46e3ba42785ea8c1785e2ceaa56e0ffa32dbe5410dec432f37e4107c4f383", size = 13839, upload-time = "2024-04-30T04:40:38.125Z" } 1756 + wheels = [ 1757 + { url = "https://files.pythonhosted.org/packages/fd/bc/cc4e3dbc5e7992398dcb7a8eda0cbcf4fb792a0cdb93f857b478bf3cf884/rich_rst-1.3.1-py3-none-any.whl", hash = "sha256:498a74e3896507ab04492d326e794c3ef76e7cda078703aa592d1853d91098c1", size = 11621, upload-time = "2024-04-30T04:40:32.619Z" }, 1523 1758 ] 1524 1759 1525 1760 [[package]] ··· 1746 1981 ] 1747 1982 1748 1983 [[package]] 1749 - name = "turbopuffer" 1750 - version = "0.5.13" 1751 - source = { registry = "https://pypi.org/simple" } 1752 - dependencies = [ 1753 - { name = "aiohttp" }, 1754 - { name = "anyio" }, 1755 - { name = "distro" }, 1756 - { name = "httpx" }, 1757 - { name = "pybase64" }, 1758 - { name = "pydantic" }, 1759 - { name = "sniffio" }, 1760 - { name = "typing-extensions" }, 1761 - ] 1762 - sdist = { url = "https://files.pythonhosted.org/packages/79/a2/59f6dbfcc43eb08c91bf77670ade5ca3ddc293c518db2b29703643799273/turbopuffer-0.5.13.tar.gz", hash = "sha256:e48ead6af4d493201ec6c9dfaaa6dca9bc96322f9a12f84d6866159a76eb6c27", size = 134367, upload-time = "2025-07-18T21:34:34.793Z" } 1763 - wheels = [ 1764 - { url = "https://files.pythonhosted.org/packages/35/fd/e27b0fc9b9bebf92dc24cb54ff3862aae2b6280d98704b8eff5e98e84ccd/turbopuffer-0.5.13-py3-none-any.whl", hash = "sha256:d48263aab236d697ab3321c00870ba1104cdddcd315d67f85d1bd150621e9ae8", size = 101727, upload-time = "2025-07-18T21:34:33.27Z" }, 1765 - ] 1766 - 1767 - [[package]] 1768 1984 name = "ty" 1769 1985 version = "0.0.1a15" 1770 1986 source = { registry = "https://pypi.org/simple" } ··· 1855 2071 1856 2072 [[package]] 1857 2073 name = "websockets" 1858 - version = "13.1" 2074 + version = "15.0.1" 1859 2075 source = { registry = "https://pypi.org/simple" } 1860 - sdist = { url = "https://files.pythonhosted.org/packages/e2/73/9223dbc7be3dcaf2a7bbf756c351ec8da04b1fa573edaf545b95f6b0c7fd/websockets-13.1.tar.gz", hash = "sha256:a3b3366087c1bc0a2795111edcadddb8b3b59509d5db5d7ea3fdd69f954a8878", size = 158549, upload-time = "2024-09-21T17:34:21.54Z" } 2076 + sdist = { url = "https://files.pythonhosted.org/packages/21/e6/26d09fab466b7ca9c7737474c52be4f76a40301b08362eb2dbc19dcc16c1/websockets-15.0.1.tar.gz", hash = "sha256:82544de02076bafba038ce055ee6412d68da13ab47f0c60cab827346de828dee", size = 177016, upload-time = "2025-03-05T20:03:41.606Z" } 1861 2077 wheels = [ 1862 - { url = "https://files.pythonhosted.org/packages/df/46/c426282f543b3c0296cf964aa5a7bb17e984f58dde23460c3d39b3148fcf/websockets-13.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:9d75baf00138f80b48f1eac72ad1535aac0b6461265a0bcad391fc5aba875cfc", size = 157821, upload-time = "2024-09-21T17:32:56.442Z" }, 1863 - { url = "https://files.pythonhosted.org/packages/aa/85/22529867010baac258da7c45848f9415e6cf37fef00a43856627806ffd04/websockets-13.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:9b6f347deb3dcfbfde1c20baa21c2ac0751afaa73e64e5b693bb2b848efeaa49", size = 155480, upload-time = "2024-09-21T17:32:57.698Z" }, 1864 - { url = "https://files.pythonhosted.org/packages/29/2c/bdb339bfbde0119a6e84af43ebf6275278698a2241c2719afc0d8b0bdbf2/websockets-13.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:de58647e3f9c42f13f90ac7e5f58900c80a39019848c5547bc691693098ae1bd", size = 155715, upload-time = "2024-09-21T17:32:59.429Z" }, 1865 - { url = "https://files.pythonhosted.org/packages/9f/d0/8612029ea04c5c22bf7af2fd3d63876c4eaeef9b97e86c11972a43aa0e6c/websockets-13.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1b54689e38d1279a51d11e3467dd2f3a50f5f2e879012ce8f2d6943f00e83f0", size = 165647, upload-time = "2024-09-21T17:33:00.495Z" }, 1866 - { url = "https://files.pythonhosted.org/packages/56/04/1681ed516fa19ca9083f26d3f3a302257e0911ba75009533ed60fbb7b8d1/websockets-13.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf1781ef73c073e6b0f90af841aaf98501f975d306bbf6221683dd594ccc52b6", size = 164592, upload-time = "2024-09-21T17:33:02.223Z" }, 1867 - { url = "https://files.pythonhosted.org/packages/38/6f/a96417a49c0ed132bb6087e8e39a37db851c70974f5c724a4b2a70066996/websockets-13.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d23b88b9388ed85c6faf0e74d8dec4f4d3baf3ecf20a65a47b836d56260d4b9", size = 165012, upload-time = "2024-09-21T17:33:03.288Z" }, 1868 - { url = "https://files.pythonhosted.org/packages/40/8b/fccf294919a1b37d190e86042e1a907b8f66cff2b61e9befdbce03783e25/websockets-13.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3c78383585f47ccb0fcf186dcb8a43f5438bd7d8f47d69e0b56f71bf431a0a68", size = 165311, upload-time = "2024-09-21T17:33:04.728Z" }, 1869 - { url = "https://files.pythonhosted.org/packages/c1/61/f8615cf7ce5fe538476ab6b4defff52beb7262ff8a73d5ef386322d9761d/websockets-13.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:d6d300f8ec35c24025ceb9b9019ae9040c1ab2f01cddc2bcc0b518af31c75c14", size = 164692, upload-time = "2024-09-21T17:33:05.829Z" }, 1870 - { url = "https://files.pythonhosted.org/packages/5c/f1/a29dd6046d3a722d26f182b783a7997d25298873a14028c4760347974ea3/websockets-13.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a9dcaf8b0cc72a392760bb8755922c03e17a5a54e08cca58e8b74f6902b433cf", size = 164686, upload-time = "2024-09-21T17:33:06.823Z" }, 1871 - { url = "https://files.pythonhosted.org/packages/0f/99/ab1cdb282f7e595391226f03f9b498f52109d25a2ba03832e21614967dfa/websockets-13.1-cp312-cp312-win32.whl", hash = "sha256:2f85cf4f2a1ba8f602298a853cec8526c2ca42a9a4b947ec236eaedb8f2dc80c", size = 158712, upload-time = "2024-09-21T17:33:07.877Z" }, 1872 - { url = "https://files.pythonhosted.org/packages/46/93/e19160db48b5581feac8468330aa11b7292880a94a37d7030478596cc14e/websockets-13.1-cp312-cp312-win_amd64.whl", hash = "sha256:38377f8b0cdeee97c552d20cf1865695fcd56aba155ad1b4ca8779a5b6ef4ac3", size = 159145, upload-time = "2024-09-21T17:33:09.202Z" }, 1873 - { url = "https://files.pythonhosted.org/packages/51/20/2b99ca918e1cbd33c53db2cace5f0c0cd8296fc77558e1908799c712e1cd/websockets-13.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a9ab1e71d3d2e54a0aa646ab6d4eebfaa5f416fe78dfe4da2839525dc5d765c6", size = 157828, upload-time = "2024-09-21T17:33:10.987Z" }, 1874 - { url = "https://files.pythonhosted.org/packages/b8/47/0932a71d3d9c0e9483174f60713c84cee58d62839a143f21a2bcdbd2d205/websockets-13.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b9d7439d7fab4dce00570bb906875734df13d9faa4b48e261c440a5fec6d9708", size = 155487, upload-time = "2024-09-21T17:33:12.153Z" }, 1875 - { url = "https://files.pythonhosted.org/packages/a9/60/f1711eb59ac7a6c5e98e5637fef5302f45b6f76a2c9d64fd83bbb341377a/websockets-13.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:327b74e915cf13c5931334c61e1a41040e365d380f812513a255aa804b183418", size = 155721, upload-time = "2024-09-21T17:33:13.909Z" }, 1876 - { url = "https://files.pythonhosted.org/packages/6a/e6/ba9a8db7f9d9b0e5f829cf626ff32677f39824968317223605a6b419d445/websockets-13.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:325b1ccdbf5e5725fdcb1b0e9ad4d2545056479d0eee392c291c1bf76206435a", size = 165609, upload-time = "2024-09-21T17:33:14.967Z" }, 1877 - { url = "https://files.pythonhosted.org/packages/c1/22/4ec80f1b9c27a0aebd84ccd857252eda8418ab9681eb571b37ca4c5e1305/websockets-13.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:346bee67a65f189e0e33f520f253d5147ab76ae42493804319b5716e46dddf0f", size = 164556, upload-time = "2024-09-21T17:33:17.113Z" }, 1878 - { url = "https://files.pythonhosted.org/packages/27/ac/35f423cb6bb15600438db80755609d27eda36d4c0b3c9d745ea12766c45e/websockets-13.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:91a0fa841646320ec0d3accdff5b757b06e2e5c86ba32af2e0815c96c7a603c5", size = 164993, upload-time = "2024-09-21T17:33:18.168Z" }, 1879 - { url = "https://files.pythonhosted.org/packages/31/4e/98db4fd267f8be9e52e86b6ee4e9aa7c42b83452ea0ea0672f176224b977/websockets-13.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:18503d2c5f3943e93819238bf20df71982d193f73dcecd26c94514f417f6b135", size = 165360, upload-time = "2024-09-21T17:33:19.233Z" }, 1880 - { url = "https://files.pythonhosted.org/packages/3f/15/3f0de7cda70ffc94b7e7024544072bc5b26e2c1eb36545291abb755d8cdb/websockets-13.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:a9cd1af7e18e5221d2878378fbc287a14cd527fdd5939ed56a18df8a31136bb2", size = 164745, upload-time = "2024-09-21T17:33:20.361Z" }, 1881 - { url = "https://files.pythonhosted.org/packages/a1/6e/66b6b756aebbd680b934c8bdbb6dcb9ce45aad72cde5f8a7208dbb00dd36/websockets-13.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:70c5be9f416aa72aab7a2a76c90ae0a4fe2755c1816c153c1a2bcc3333ce4ce6", size = 164732, upload-time = "2024-09-21T17:33:23.103Z" }, 1882 - { url = "https://files.pythonhosted.org/packages/35/c6/12e3aab52c11aeb289e3dbbc05929e7a9d90d7a9173958477d3ef4f8ce2d/websockets-13.1-cp313-cp313-win32.whl", hash = "sha256:624459daabeb310d3815b276c1adef475b3e6804abaf2d9d2c061c319f7f187d", size = 158709, upload-time = "2024-09-21T17:33:24.196Z" }, 1883 - { url = "https://files.pythonhosted.org/packages/41/d8/63d6194aae711d7263df4498200c690a9c39fb437ede10f3e157a6343e0d/websockets-13.1-cp313-cp313-win_amd64.whl", hash = "sha256:c518e84bb59c2baae725accd355c8dc517b4a3ed8db88b4bc93c78dae2974bf2", size = 159144, upload-time = "2024-09-21T17:33:25.96Z" }, 1884 - { url = "https://files.pythonhosted.org/packages/56/27/96a5cd2626d11c8280656c6c71d8ab50fe006490ef9971ccd154e0c42cd2/websockets-13.1-py3-none-any.whl", hash = "sha256:a9a396a6ad26130cdae92ae10c36af09d9bfe6cafe69670fd3b6da9b07b4044f", size = 152134, upload-time = "2024-09-21T17:34:19.904Z" }, 2078 + { url = "https://files.pythonhosted.org/packages/51/6b/4545a0d843594f5d0771e86463606a3988b5a09ca5123136f8a76580dd63/websockets-15.0.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:3e90baa811a5d73f3ca0bcbf32064d663ed81318ab225ee4f427ad4e26e5aff3", size = 175437, upload-time = "2025-03-05T20:02:16.706Z" }, 2079 + { url = "https://files.pythonhosted.org/packages/f4/71/809a0f5f6a06522af902e0f2ea2757f71ead94610010cf570ab5c98e99ed/websockets-15.0.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:592f1a9fe869c778694f0aa806ba0374e97648ab57936f092fd9d87f8bc03665", size = 173096, upload-time = "2025-03-05T20:02:18.832Z" }, 2080 + { url = "https://files.pythonhosted.org/packages/3d/69/1a681dd6f02180916f116894181eab8b2e25b31e484c5d0eae637ec01f7c/websockets-15.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0701bc3cfcb9164d04a14b149fd74be7347a530ad3bbf15ab2c678a2cd3dd9a2", size = 173332, upload-time = "2025-03-05T20:02:20.187Z" }, 2081 + { url = "https://files.pythonhosted.org/packages/a6/02/0073b3952f5bce97eafbb35757f8d0d54812b6174ed8dd952aa08429bcc3/websockets-15.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8b56bdcdb4505c8078cb6c7157d9811a85790f2f2b3632c7d1462ab5783d215", size = 183152, upload-time = "2025-03-05T20:02:22.286Z" }, 2082 + { url = "https://files.pythonhosted.org/packages/74/45/c205c8480eafd114b428284840da0b1be9ffd0e4f87338dc95dc6ff961a1/websockets-15.0.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0af68c55afbd5f07986df82831c7bff04846928ea8d1fd7f30052638788bc9b5", size = 182096, upload-time = "2025-03-05T20:02:24.368Z" }, 2083 + { url = "https://files.pythonhosted.org/packages/14/8f/aa61f528fba38578ec553c145857a181384c72b98156f858ca5c8e82d9d3/websockets-15.0.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64dee438fed052b52e4f98f76c5790513235efaa1ef7f3f2192c392cd7c91b65", size = 182523, upload-time = "2025-03-05T20:02:25.669Z" }, 2084 + { url = "https://files.pythonhosted.org/packages/ec/6d/0267396610add5bc0d0d3e77f546d4cd287200804fe02323797de77dbce9/websockets-15.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d5f6b181bb38171a8ad1d6aa58a67a6aa9d4b38d0f8c5f496b9e42561dfc62fe", size = 182790, upload-time = "2025-03-05T20:02:26.99Z" }, 2085 + { url = "https://files.pythonhosted.org/packages/02/05/c68c5adbf679cf610ae2f74a9b871ae84564462955d991178f95a1ddb7dd/websockets-15.0.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5d54b09eba2bada6011aea5375542a157637b91029687eb4fdb2dab11059c1b4", size = 182165, upload-time = "2025-03-05T20:02:30.291Z" }, 2086 + { url = "https://files.pythonhosted.org/packages/29/93/bb672df7b2f5faac89761cb5fa34f5cec45a4026c383a4b5761c6cea5c16/websockets-15.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3be571a8b5afed347da347bfcf27ba12b069d9d7f42cb8c7028b5e98bbb12597", size = 182160, upload-time = "2025-03-05T20:02:31.634Z" }, 2087 + { url = "https://files.pythonhosted.org/packages/ff/83/de1f7709376dc3ca9b7eeb4b9a07b4526b14876b6d372a4dc62312bebee0/websockets-15.0.1-cp312-cp312-win32.whl", hash = "sha256:c338ffa0520bdb12fbc527265235639fb76e7bc7faafbb93f6ba80d9c06578a9", size = 176395, upload-time = "2025-03-05T20:02:33.017Z" }, 2088 + { url = "https://files.pythonhosted.org/packages/7d/71/abf2ebc3bbfa40f391ce1428c7168fb20582d0ff57019b69ea20fa698043/websockets-15.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:fcd5cf9e305d7b8338754470cf69cf81f420459dbae8a3b40cee57417f4614a7", size = 176841, upload-time = "2025-03-05T20:02:34.498Z" }, 2089 + { url = "https://files.pythonhosted.org/packages/cb/9f/51f0cf64471a9d2b4d0fc6c534f323b664e7095640c34562f5182e5a7195/websockets-15.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ee443ef070bb3b6ed74514f5efaa37a252af57c90eb33b956d35c8e9c10a1931", size = 175440, upload-time = "2025-03-05T20:02:36.695Z" }, 2090 + { url = "https://files.pythonhosted.org/packages/8a/05/aa116ec9943c718905997412c5989f7ed671bc0188ee2ba89520e8765d7b/websockets-15.0.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5a939de6b7b4e18ca683218320fc67ea886038265fd1ed30173f5ce3f8e85675", size = 173098, upload-time = "2025-03-05T20:02:37.985Z" }, 2091 + { url = "https://files.pythonhosted.org/packages/ff/0b/33cef55ff24f2d92924923c99926dcce78e7bd922d649467f0eda8368923/websockets-15.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:746ee8dba912cd6fc889a8147168991d50ed70447bf18bcda7039f7d2e3d9151", size = 173329, upload-time = "2025-03-05T20:02:39.298Z" }, 2092 + { url = "https://files.pythonhosted.org/packages/31/1d/063b25dcc01faa8fada1469bdf769de3768b7044eac9d41f734fd7b6ad6d/websockets-15.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:595b6c3969023ecf9041b2936ac3827e4623bfa3ccf007575f04c5a6aa318c22", size = 183111, upload-time = "2025-03-05T20:02:40.595Z" }, 2093 + { url = "https://files.pythonhosted.org/packages/93/53/9a87ee494a51bf63e4ec9241c1ccc4f7c2f45fff85d5bde2ff74fcb68b9e/websockets-15.0.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c714d2fc58b5ca3e285461a4cc0c9a66bd0e24c5da9911e30158286c9b5be7f", size = 182054, upload-time = "2025-03-05T20:02:41.926Z" }, 2094 + { url = "https://files.pythonhosted.org/packages/ff/b2/83a6ddf56cdcbad4e3d841fcc55d6ba7d19aeb89c50f24dd7e859ec0805f/websockets-15.0.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f3c1e2ab208db911594ae5b4f79addeb3501604a165019dd221c0bdcabe4db8", size = 182496, upload-time = "2025-03-05T20:02:43.304Z" }, 2095 + { url = "https://files.pythonhosted.org/packages/98/41/e7038944ed0abf34c45aa4635ba28136f06052e08fc2168520bb8b25149f/websockets-15.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:229cf1d3ca6c1804400b0a9790dc66528e08a6a1feec0d5040e8b9eb14422375", size = 182829, upload-time = "2025-03-05T20:02:48.812Z" }, 2096 + { url = "https://files.pythonhosted.org/packages/e0/17/de15b6158680c7623c6ef0db361da965ab25d813ae54fcfeae2e5b9ef910/websockets-15.0.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:756c56e867a90fb00177d530dca4b097dd753cde348448a1012ed6c5131f8b7d", size = 182217, upload-time = "2025-03-05T20:02:50.14Z" }, 2097 + { url = "https://files.pythonhosted.org/packages/33/2b/1f168cb6041853eef0362fb9554c3824367c5560cbdaad89ac40f8c2edfc/websockets-15.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:558d023b3df0bffe50a04e710bc87742de35060580a293c2a984299ed83bc4e4", size = 182195, upload-time = "2025-03-05T20:02:51.561Z" }, 2098 + { url = "https://files.pythonhosted.org/packages/86/eb/20b6cdf273913d0ad05a6a14aed4b9a85591c18a987a3d47f20fa13dcc47/websockets-15.0.1-cp313-cp313-win32.whl", hash = "sha256:ba9e56e8ceeeedb2e080147ba85ffcd5cd0711b89576b83784d8605a7df455fa", size = 176393, upload-time = "2025-03-05T20:02:53.814Z" }, 2099 + { url = "https://files.pythonhosted.org/packages/1b/6c/c65773d6cab416a64d191d6ee8a8b1c68a09970ea6909d16965d26bfed1e/websockets-15.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:e09473f095a819042ecb2ab9465aee615bd9c2028e4ef7d933600a8401c79561", size = 176837, upload-time = "2025-03-05T20:02:55.237Z" }, 2100 + { url = "https://files.pythonhosted.org/packages/fa/a8/5b41e0da817d64113292ab1f8247140aac61cbf6cfd085d6a0fa77f4984f/websockets-15.0.1-py3-none-any.whl", hash = "sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f", size = 169743, upload-time = "2025-03-05T20:03:39.41Z" }, 2101 + ] 2102 + 2103 + [[package]] 2104 + name = "werkzeug" 2105 + version = "3.1.1" 2106 + source = { registry = "https://pypi.org/simple" } 2107 + dependencies = [ 2108 + { name = "markupsafe" }, 2109 + ] 2110 + sdist = { url = "https://files.pythonhosted.org/packages/32/af/d4502dc713b4ccea7175d764718d5183caf8d0867a4f0190d5d4a45cea49/werkzeug-3.1.1.tar.gz", hash = "sha256:8cd39dfbdfc1e051965f156163e2974e52c210f130810e9ad36858f0fd3edad4", size = 806453, upload-time = "2024-11-01T16:40:45.462Z" } 2111 + wheels = [ 2112 + { url = "https://files.pythonhosted.org/packages/ee/ea/c67e1dee1ba208ed22c06d1d547ae5e293374bfc43e0eb0ef5e262b68561/werkzeug-3.1.1-py3-none-any.whl", hash = "sha256:a71124d1ef06008baafa3d266c02f56e1836a5984afd6dd6c9230669d60d9fb5", size = 224371, upload-time = "2024-11-01T16:40:43.994Z" }, 1885 2113 ] 1886 2114 1887 2115 [[package]]