xache 5.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
xache/utils/http.py ADDED
@@ -0,0 +1,209 @@
1
+ """
2
+ HTTP client utilities with retry logic
3
+ """
4
+
5
+ import asyncio
6
+ import json
7
+ from typing import Dict, Optional, Any, Literal
8
+ from dataclasses import dataclass
9
+
10
+ import aiohttp
11
+
12
+ from ..errors import (
13
+ create_error_from_response,
14
+ PaymentRequiredError,
15
+ NetworkError,
16
+ XacheError,
17
+ )
18
+ from ..types import APIResponse
19
+
20
+
21
+ @dataclass
22
+ class RetryConfig:
23
+ """Retry configuration"""
24
+ max_retries: int = 3
25
+ initial_delay: float = 1.0
26
+ max_delay: float = 10.0
27
+ backoff_multiplier: float = 2.0
28
+ retryable_status_codes: list = None
29
+
30
+ def __post_init__(self):
31
+ if self.retryable_status_codes is None:
32
+ self.retryable_status_codes = [408, 429, 500, 502, 503, 504]
33
+
34
+
35
+ class HttpClient:
36
+ """HTTP client with retry logic"""
37
+
38
+ def __init__(
39
+ self,
40
+ timeout: int = 30,
41
+ retry_config: Optional[RetryConfig] = None,
42
+ debug: bool = False,
43
+ ):
44
+ self.timeout = timeout
45
+ self.retry_config = retry_config or RetryConfig()
46
+ self.debug = debug
47
+ self._session: Optional[aiohttp.ClientSession] = None
48
+
49
+ async def __aenter__(self):
50
+ """Async context manager entry"""
51
+ await self._ensure_session()
52
+ return self
53
+
54
+ async def __aexit__(self, exc_type, exc_val, exc_tb):
55
+ """Async context manager exit"""
56
+ await self.close()
57
+
58
+ async def _ensure_session(self):
59
+ """Ensure aiohttp session exists"""
60
+ if self._session is None or self._session.closed:
61
+ timeout = aiohttp.ClientTimeout(total=self.timeout)
62
+ self._session = aiohttp.ClientSession(timeout=timeout)
63
+
64
+ async def close(self):
65
+ """Close HTTP session"""
66
+ if self._session and not self._session.closed:
67
+ await self._session.close()
68
+
69
+ async def request(
70
+ self,
71
+ method: Literal["GET", "POST", "PUT", "PATCH", "DELETE"],
72
+ url: str,
73
+ headers: Optional[Dict[str, str]] = None,
74
+ body: Optional[str] = None,
75
+ ) -> APIResponse:
76
+ """
77
+ Make HTTP request with retry logic
78
+
79
+ Args:
80
+ method: HTTP method
81
+ url: Full URL
82
+ headers: Request headers
83
+ body: Request body (JSON string)
84
+
85
+ Returns:
86
+ API response
87
+
88
+ Raises:
89
+ XacheError: API error
90
+ NetworkError: Network error
91
+ """
92
+ await self._ensure_session()
93
+
94
+ headers = headers or {}
95
+ headers["Content-Type"] = "application/json"
96
+
97
+ last_error: Optional[Exception] = None
98
+ attempt = 0
99
+
100
+ while attempt <= self.retry_config.max_retries:
101
+ try:
102
+ if self.debug and attempt > 0:
103
+ print(f"Retry attempt {attempt}/{self.retry_config.max_retries} for {method} {url}")
104
+
105
+ response = await self._make_request(method, url, headers, body)
106
+ return response
107
+
108
+ except (aiohttp.ClientError, asyncio.TimeoutError) as e:
109
+ last_error = e
110
+ attempt += 1
111
+
112
+ if attempt <= self.retry_config.max_retries:
113
+ delay = self._calculate_delay(attempt)
114
+ await asyncio.sleep(delay)
115
+ continue
116
+
117
+ except PaymentRequiredError:
118
+ # Don't retry 402 errors
119
+ raise
120
+
121
+ except XacheError:
122
+ # Don't retry API errors (400, 401, 403, 404, 409, etc.)
123
+ raise
124
+
125
+ except Exception as e:
126
+ last_error = e
127
+ attempt += 1
128
+
129
+ if attempt <= self.retry_config.max_retries:
130
+ delay = self._calculate_delay(attempt)
131
+ await asyncio.sleep(delay)
132
+ continue
133
+
134
+ # All retries exhausted
135
+ raise NetworkError(
136
+ f"Request failed after {self.retry_config.max_retries} retries",
137
+ last_error,
138
+ )
139
+
140
+ async def _make_request(
141
+ self,
142
+ method: str,
143
+ url: str,
144
+ headers: Dict[str, str],
145
+ body: Optional[str],
146
+ ) -> APIResponse:
147
+ """Make single HTTP request"""
148
+ async with self._session.request(
149
+ method,
150
+ url,
151
+ headers=headers,
152
+ data=body,
153
+ ) as response:
154
+ # Parse response
155
+ try:
156
+ response_json = await response.json()
157
+ except json.JSONDecodeError as e:
158
+ raise NetworkError("Failed to parse response JSON", e)
159
+
160
+ # Handle 402 Payment Required
161
+ if response.status == 402:
162
+ payment_data = response_json.get("payment", {})
163
+ meta = response_json.get("meta", {})
164
+ raise PaymentRequiredError(
165
+ response_json.get("error", {}).get("message", "Payment required"),
166
+ payment_data.get("challengeId", ""),
167
+ payment_data.get("amount", ""),
168
+ payment_data.get("chainHint", ""),
169
+ payment_data.get("payTo", ""),
170
+ payment_data.get("description", ""),
171
+ meta.get("requestId"),
172
+ )
173
+
174
+ # Handle API error responses
175
+ if not response_json.get("success") and response_json.get("error"):
176
+ error = response_json["error"]
177
+ meta = response_json.get("meta", {})
178
+ raise create_error_from_response(
179
+ error.get("code", "INTERNAL"),
180
+ error.get("message", "Unknown error"),
181
+ response.status,
182
+ error.get("details"),
183
+ meta.get("requestId"),
184
+ )
185
+
186
+ # Check if retryable status code
187
+ if response.status in self.retry_config.retryable_status_codes:
188
+ raise NetworkError(f"HTTP {response.status}: {response.reason}")
189
+
190
+ # Success response
191
+ return APIResponse(
192
+ success=response_json.get("success", True),
193
+ data=response_json.get("data"),
194
+ error=None,
195
+ meta=None,
196
+ )
197
+
198
+ def _calculate_delay(self, attempt: int) -> float:
199
+ """Calculate exponential backoff delay with jitter"""
200
+ import random
201
+
202
+ delay = min(
203
+ self.retry_config.initial_delay * (self.retry_config.backoff_multiplier ** (attempt - 1)),
204
+ self.retry_config.max_delay,
205
+ )
206
+
207
+ # Add jitter (±25%)
208
+ jitter = delay * 0.25 * (random.random() * 2 - 1)
209
+ return delay + jitter
xache/utils/retry.py ADDED
@@ -0,0 +1,101 @@
1
+ """
2
+ Retry utility with exponential backoff
3
+ Production-ready automatic error recovery
4
+ """
5
+
6
+ import time
7
+ from typing import Callable, TypeVar, List, Optional, Any
8
+ from ..types import ErrorCode
9
+
10
+ T = TypeVar('T')
11
+
12
+ class RetryPolicy:
13
+ """Configuration for retry behavior"""
14
+
15
+ def __init__(
16
+ self,
17
+ max_retries: int = 3,
18
+ backoff_ms: Optional[List[int]] = None,
19
+ retryable_errors: Optional[List[ErrorCode]] = None,
20
+ timeout: int = 60000
21
+ ):
22
+ self.max_retries = max_retries
23
+ self.backoff_ms = backoff_ms or [1000, 2000, 4000]
24
+ self.retryable_errors = retryable_errors or ['RETRY_LATER', 'INTERNAL']
25
+ self.timeout = timeout
26
+
27
+
28
+ def is_retryable_error(error: Exception, retryable_errors: List[ErrorCode]) -> bool:
29
+ """Check if error is retryable"""
30
+ if hasattr(error, 'code'):
31
+ return error.code in retryable_errors
32
+ return False
33
+
34
+
35
+ def with_retry(
36
+ fn: Callable[[], T],
37
+ policy: Optional[RetryPolicy] = None,
38
+ debug: bool = False
39
+ ) -> T:
40
+ """
41
+ Execute function with automatic retry logic
42
+
43
+ Args:
44
+ fn: Function to execute
45
+ policy: Retry policy configuration
46
+ debug: Enable debug logging
47
+
48
+ Returns:
49
+ Result from successful function execution
50
+
51
+ Raises:
52
+ Last exception if all retries exhausted
53
+
54
+ Example:
55
+ >>> def fetch_data():
56
+ ... return api.get('/data')
57
+ >>> result = with_retry(fetch_data, RetryPolicy(max_retries=5))
58
+ """
59
+ if policy is None:
60
+ policy = RetryPolicy()
61
+
62
+ start_time = time.time() * 1000 # Convert to milliseconds
63
+ last_error: Optional[Exception] = None
64
+
65
+ for attempt in range(policy.max_retries + 1):
66
+ # Check timeout
67
+ if (time.time() * 1000 - start_time) > policy.timeout:
68
+ raise TimeoutError(
69
+ f"Operation timed out after {policy.timeout}ms ({attempt} attempts)"
70
+ )
71
+
72
+ try:
73
+ return fn()
74
+ except Exception as error:
75
+ last_error = error
76
+
77
+ # If this is the last attempt, don't retry
78
+ if attempt == policy.max_retries:
79
+ break
80
+
81
+ # Check if error is retryable
82
+ if not is_retryable_error(error, policy.retryable_errors):
83
+ raise error
84
+
85
+ # Calculate delay
86
+ if attempt < len(policy.backoff_ms):
87
+ delay = policy.backoff_ms[attempt]
88
+ else:
89
+ delay = policy.backoff_ms[-1]
90
+
91
+ if debug:
92
+ print(f"Retry attempt {attempt + 1}/{policy.max_retries} after {delay}ms delay")
93
+
94
+ # Sleep for delay (convert ms to seconds)
95
+ time.sleep(delay / 1000)
96
+
97
+ # All retries exhausted, raise last error
98
+ if last_error:
99
+ raise last_error
100
+ else:
101
+ raise RuntimeError("Unexpected error in retry logic")
@@ -0,0 +1,337 @@
1
+ Metadata-Version: 2.4
2
+ Name: xache
3
+ Version: 5.0.0
4
+ Summary: Official Python SDK for Xache Protocol
5
+ Home-page: https://github.com/xache-ai/xache-protocol
6
+ Author: Xache Protocol
7
+ Author-email: Xache Protocol <dev@xache.xyz>
8
+ License: MIT
9
+ Project-URL: Homepage, https://xache.xyz
10
+ Project-URL: Documentation, https://docs.xache.xyz
11
+ Project-URL: Repository, https://github.com/oliveskin/xache
12
+ Project-URL: Bug Reports, https://github.com/oliveskin/xache/issues
13
+ Keywords: xache,ai,agent,memory,blockchain,decentralized
14
+ Classifier: Development Status :: 4 - Beta
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.8
19
+ Classifier: Programming Language :: Python :: 3.9
20
+ Classifier: Programming Language :: Python :: 3.10
21
+ Classifier: Programming Language :: Python :: 3.11
22
+ Classifier: Programming Language :: Python :: 3.12
23
+ Requires-Python: >=3.8
24
+ Description-Content-Type: text/markdown
25
+ Requires-Dist: aiohttp>=3.9.0
26
+ Requires-Dist: typing-extensions>=4.0.0
27
+ Requires-Dist: mnemonic>=0.20
28
+ Requires-Dist: eth-account>=0.10.0
29
+ Requires-Dist: bip-utils>=2.9.0
30
+ Requires-Dist: PyNaCl>=1.5.0
31
+ Requires-Dist: base58>=2.1.1
32
+ Provides-Extra: dev
33
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
34
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
35
+ Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
36
+ Requires-Dist: black>=23.0.0; extra == "dev"
37
+ Requires-Dist: mypy>=1.0.0; extra == "dev"
38
+ Requires-Dist: pylint>=2.17.0; extra == "dev"
39
+ Provides-Extra: encryption
40
+ Requires-Dist: PyNaCl>=1.5.0; extra == "encryption"
41
+ Dynamic: author
42
+ Dynamic: home-page
43
+ Dynamic: requires-python
44
+
45
+ # Xache Protocol Python SDK
46
+
47
+ Official Python SDK for [Xache Protocol](https://xache.ai) - decentralized agent memory and collective intelligence marketplace.
48
+
49
+ ## Features
50
+
51
+ ✅ **Async/Await** - Full asyncio support for concurrent operations
52
+ ✅ **Type Hints** - Complete type annotations for better IDE support
53
+ ✅ **Authentication** - Automatic request signing per protocol spec
54
+ ✅ **Payment Flow** - Built-in 402 payment handling (manual or Coinbase Commerce)
55
+ ✅ **Encryption** - Client-side encryption for memory storage
56
+ ✅ **Error Handling** - Typed exceptions with automatic retry logic
57
+ ✅ **Budget Management** - Track and control spending limits
58
+
59
+ ## Installation
60
+
61
+ ```bash
62
+ pip install xache
63
+ ```
64
+
65
+ With encryption support:
66
+
67
+ ```bash
68
+ pip install xache[encryption]
69
+ ```
70
+
71
+ ## Quick Start
72
+
73
+ ```python
74
+ import asyncio
75
+ from xache import XacheClient
76
+
77
+ async def main():
78
+ # Initialize client
79
+ async with XacheClient(
80
+ api_url="https://api.xache.xyz",
81
+ did="did:agent:evm:0xYourWalletAddress",
82
+ private_key="0x...",
83
+ ) as client:
84
+ # Register identity
85
+ identity = await client.identity.register(
86
+ wallet_address="0xYourWalletAddress",
87
+ key_type="evm",
88
+ chain="base",
89
+ )
90
+ print(f"DID: {identity.did}")
91
+
92
+ asyncio.run(main())
93
+ ```
94
+
95
+ ## Usage Examples
96
+
97
+ ### Memory Storage
98
+
99
+ ```python
100
+ # Store encrypted memory (automatic encryption + 402 payment)
101
+ memory = await client.memory.store(
102
+ data={
103
+ "context": "user preferences",
104
+ "theme": "dark",
105
+ "language": "en",
106
+ },
107
+ storage_tier="hot",
108
+ )
109
+ print(f"Memory ID: {memory.memory_id}")
110
+
111
+ # Retrieve memory (automatic decryption + 402 payment)
112
+ retrieved = await client.memory.retrieve(memory_id=memory.memory_id)
113
+ print(f"Data: {retrieved.data}")
114
+
115
+ # Delete memory (free)
116
+ await client.memory.delete(memory.memory_id)
117
+ ```
118
+
119
+ ### Collective Intelligence
120
+
121
+ ```python
122
+ # Contribute a heuristic (automatic 402 payment)
123
+ heuristic = await client.collective.contribute(
124
+ pattern="Use async/await for cleaner async code in Python",
125
+ domain="python",
126
+ tags=["async", "best-practices", "readability"],
127
+ context_type="code-review",
128
+ )
129
+ print(f"Heuristic ID: {heuristic.heuristic_id}")
130
+
131
+ # Query collective (automatic 402 payment)
132
+ results = await client.collective.query(
133
+ query_text="How to optimize database queries in Python",
134
+ domain="python",
135
+ limit=10,
136
+ )
137
+
138
+ for match in results.matches:
139
+ print(f"Pattern: {match.pattern}")
140
+ print(f"Score: {match.relevance_score}")
141
+ print(f"Royalty: ${match.royalty_amount}")
142
+ ```
143
+
144
+ ### Budget Management
145
+
146
+ ```python
147
+ # Check budget status
148
+ budget = await client.budget.get_status()
149
+ print(f"Limit: ${budget.limit_cents / 100}")
150
+ print(f"Spent: ${budget.spent_cents / 100}")
151
+ print(f"Remaining: ${budget.remaining_cents / 100}")
152
+ print(f"Usage: {budget.percentage_used:.1f}%")
153
+
154
+ # Update budget limit
155
+ await client.budget.update_limit(5000) # $50/month
156
+
157
+ # Check if you can afford an operation
158
+ can_afford = await client.budget.can_afford(100) # 100 cents = $1
159
+ ```
160
+
161
+ ### Receipts & Analytics
162
+
163
+ ```python
164
+ # List receipts
165
+ result = await client.receipts.list(limit=20, offset=0)
166
+ for receipt in result["receipts"]:
167
+ print(f"{receipt.operation}: ${receipt.amount_usd}")
168
+
169
+ # Get Merkle proof for verification
170
+ proof = await client.receipts.get_proof("receipt_abc123")
171
+ print(f"Merkle Root: {proof.merkle_root}")
172
+
173
+ # Get usage analytics
174
+ analytics = await client.receipts.get_analytics(
175
+ start_date="2024-01-01",
176
+ end_date="2024-01-31",
177
+ )
178
+ print(f"Total spent: ${analytics.total_spent}")
179
+ ```
180
+
181
+ ## Configuration
182
+
183
+ ### Basic Configuration
184
+
185
+ ```python
186
+ client = XacheClient(
187
+ api_url="https://api.xache.xyz",
188
+ did="did:agent:evm:0xYourWalletAddress",
189
+ private_key="0x...",
190
+ timeout=30, # Optional: request timeout in seconds
191
+ debug=False, # Optional: enable debug logging
192
+ )
193
+ ```
194
+
195
+ ### Payment Configuration
196
+
197
+ #### Manual Payment (Default)
198
+
199
+ ```python
200
+ client = XacheClient(
201
+ # ... basic config
202
+ payment_provider={
203
+ "type": "manual",
204
+ },
205
+ )
206
+
207
+ # When payment is required, SDK will prompt you in console
208
+ ```
209
+
210
+ #### Coinbase Commerce
211
+
212
+ ```python
213
+ client = XacheClient(
214
+ # ... basic config
215
+ payment_provider={
216
+ "type": "coinbase-commerce",
217
+ "api_key": "YOUR_COINBASE_API_KEY",
218
+ },
219
+ )
220
+
221
+ # Payments will be handled automatically via Coinbase Commerce
222
+ ```
223
+
224
+ ## Error Handling
225
+
226
+ The SDK provides typed errors for all API error codes:
227
+
228
+ ```python
229
+ from xache import (
230
+ XacheError,
231
+ UnauthenticatedError,
232
+ PaymentRequiredError,
233
+ RateLimitedError,
234
+ BudgetExceededError,
235
+ InvalidInputError,
236
+ )
237
+
238
+ try:
239
+ await client.memory.store(data=data, storage_tier="hot")
240
+ except PaymentRequiredError as e:
241
+ print(f"Payment required: ${e.amount}")
242
+ print(f"Challenge ID: {e.challenge_id}")
243
+ except RateLimitedError as e:
244
+ print(f"Rate limited. Retry at: {e.reset_at}")
245
+ except BudgetExceededError as e:
246
+ print("Budget exceeded")
247
+ except InvalidInputError as e:
248
+ print(f"Invalid input: {e.message}")
249
+ ```
250
+
251
+ ## Context Manager
252
+
253
+ Always use the client as an async context manager to ensure proper cleanup:
254
+
255
+ ```python
256
+ async with XacheClient(...) as client:
257
+ # Your code here
258
+ pass
259
+ # HTTP session automatically closed
260
+ ```
261
+
262
+ Or manually manage the lifecycle:
263
+
264
+ ```python
265
+ client = XacheClient(...)
266
+ try:
267
+ # Your code here
268
+ pass
269
+ finally:
270
+ await client.close()
271
+ ```
272
+
273
+ ## API Reference
274
+
275
+ ### XacheClient
276
+
277
+ Main client class for interacting with Xache Protocol.
278
+
279
+ #### Properties
280
+
281
+ - `client.identity` - Identity registration
282
+ - `client.memory` - Memory storage and retrieval
283
+ - `client.collective` - Collective intelligence marketplace
284
+ - `client.budget` - Budget management
285
+ - `client.receipts` - Receipt access and analytics
286
+
287
+ ### Types
288
+
289
+ All request/response types are available:
290
+
291
+ ```python
292
+ from xache import (
293
+ RegisterIdentityRequest,
294
+ RegisterIdentityResponse,
295
+ StoreMemoryRequest,
296
+ StoreMemoryResponse,
297
+ QueryCollectiveRequest,
298
+ BudgetStatus,
299
+ Receipt,
300
+ )
301
+ ```
302
+
303
+ ## Development
304
+
305
+ ```bash
306
+ # Install with dev dependencies
307
+ pip install -e ".[dev]"
308
+
309
+ # Run tests
310
+ pytest
311
+
312
+ # Run type checking
313
+ mypy xache
314
+
315
+ # Format code
316
+ black xache
317
+
318
+ # Lint
319
+ pylint xache
320
+ ```
321
+
322
+ ## Requirements
323
+
324
+ - Python 3.8+
325
+ - aiohttp
326
+ - typing-extensions
327
+
328
+ ## License
329
+
330
+ MIT
331
+
332
+ ## Links
333
+
334
+ - [Documentation](https://docs.xache.ai)
335
+ - [Protocol Specification](https://github.com/xache-ai/xache-protocol)
336
+ - [API Reference](https://api.xache.xyz/docs)
337
+ - [Discord](https://discord.gg/xache)
@@ -0,0 +1,30 @@
1
+ xache/__init__.py,sha256=RFenUVyM4Mt5ycz_OvqYNg2oZVwrOF9EogK3jZ_yEpE,2989
2
+ xache/client.py,sha256=ABjFJFnrv4KmY5ihBXK5YC8sg50s48tGdDUSKq7Vd8g,10217
3
+ xache/errors.py,sha256=sHLjfKGw1JxN3TdV_bswG9erH2vVAOFPzvOkWb1k0EI,5007
4
+ xache/types.py,sha256=YpepxdWdWbkCRzRWQX9wckCL1R6zjHswrWTrI3CnrB4,9479
5
+ xache/crypto/__init__.py,sha256=uG6liKoXEY0gdRW9uh_LDNfIp-Cs5ZHE80d_FRKu9jk,308
6
+ xache/crypto/signing.py,sha256=lUHXJ6ySp7hqx7cuX91ooRt2w6CfeHURP-RScNx0Gd8,6851
7
+ xache/crypto/wallet.py,sha256=-VWcxZwjeK1saNgp5qJTukj62yi9BsAisYhWyNwL7qY,6983
8
+ xache/payment/__init__.py,sha256=ggjkHACtiZwVmnt-OW4makx0RAzrNqlZNsdVgSf3VH0,90
9
+ xache/payment/handler.py,sha256=X3RL1mvjHCn5FbloOjiNUv_CythRlJU0Vo524lx4DLg,8901
10
+ xache/services/__init__.py,sha256=ERuZXa7R-Iv0oFBknxJiBO5nJC-oCtIut_4VITw_uOE,775
11
+ xache/services/budget.py,sha256=6sZ3nojb888a3k774XPnJHfdkqIC2Y1BxJKt8zy76V0,10599
12
+ xache/services/collective.py,sha256=utYLIHv3tuOYHbdYJYXqIy1hS-aYIRems744CsCUIUw,6229
13
+ xache/services/extraction.py,sha256=5KElr4ldBR3kcOofvz4d3NwAuTvKVskZ-NTQ7BI2ChY,5317
14
+ xache/services/facilitator.py,sha256=ikZNTqEkmcQixGSMJYGlspmbKfivMBFO3sg5Tzy7si8,9412
15
+ xache/services/identity.py,sha256=gOs5fN9juyoBfXQVm-G4whyUMJ6Oha2VmP_i3mQw0G0,13478
16
+ xache/services/memory.py,sha256=ng9_cwL4jE4c3gdlwQDZyqaBdQgwtqApEwj0LkZYWRY,13290
17
+ xache/services/owner.py,sha256=2ASJFjApS3AiQEpoS2oM9M3sisi0Y6xSjmU1fwUM0rA,8912
18
+ xache/services/receipts.py,sha256=LkVBZ-2KExWXEuA0f6GYreVPrIo_UnP1CDesSnJ9pNo,6729
19
+ xache/services/reputation.py,sha256=dwOTSUKxOo0ajeIZwZYEpzAkmswsqGF1q6vwq9S3Jno,9733
20
+ xache/services/royalty.py,sha256=uIf6q4BQ_VS-7alum1P7jexMzjtRUPxDIV6Lg7L1eLU,8595
21
+ xache/services/sessions.py,sha256=9f_sLkp6QTFsP7wnZVlImFxPEkIHSitPkjR6UDSdNIk,8025
22
+ xache/services/workspaces.py,sha256=bDu8VGXIhAX3l8Y1ReX5JH8ocNIaY3BF_Nc6QJeKlqc,13685
23
+ xache/utils/__init__.py,sha256=8VrQm0QnyqxdplpCG7BDRiAVdBGWrjUs9ipH2zsJOBM,106
24
+ xache/utils/cache.py,sha256=9zhE9dIXFTofj7jz1TX-FkAqmclqoYXTe4FwwGLeKT4,5479
25
+ xache/utils/http.py,sha256=rIQCYvYrziNrNfEbOnIKbCOGGf7bcdTvZrrU_W6CcZA,6547
26
+ xache/utils/retry.py,sha256=OJYBGozKIoteCvKw50dqd4ThhOo-WisorcKa8Tr6mnE,2860
27
+ xache-5.0.0.dist-info/METADATA,sha256=Xk9jdBWRacl2aVkn89dGbdLr9xHNmi8FWbK-B2BHyQg,8469
28
+ xache-5.0.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
29
+ xache-5.0.0.dist-info/top_level.txt,sha256=FBWE4IVb7zoLS9arsdrl97QVETlwFvYGAx6xEJZOEUU,6
30
+ xache-5.0.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.10.2)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ xache