xenfra-sdk 0.1.0__py3-none-any.whl → 0.1.2__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.
xenfra_sdk/client.py CHANGED
@@ -9,7 +9,11 @@ from .resources.projects import ProjectsManager
9
9
 
10
10
 
11
11
  class XenfraClient:
12
- def __init__(self, token: str = None, api_url: str = "http://localhost:8000"):
12
+ def __init__(self, token: str = None, api_url: str = None):
13
+ # Use provided URL, or fall back to env var, or default to production
14
+ if api_url is None:
15
+ api_url = os.getenv("XENFRA_API_URL", "https://api.xenfra.tech")
16
+
13
17
  self.api_url = api_url
14
18
  self._token = token or os.getenv("XENFRA_TOKEN")
15
19
  if not self._token:
@@ -42,7 +46,21 @@ class XenfraClient:
42
46
  return response
43
47
  except httpx.HTTPStatusError as e:
44
48
  # Convert httpx error to our custom SDK error
45
- detail = e.response.json().get("detail", e.response.text)
49
+ # Safe JSON parsing with fallback
50
+ try:
51
+ content_type = e.response.headers.get("content-type", "")
52
+ if "application/json" in content_type:
53
+ try:
54
+ error_data = e.response.json()
55
+ detail = error_data.get(
56
+ "detail", e.response.text[:500] if e.response.text else "Unknown error"
57
+ )
58
+ except (ValueError, TypeError):
59
+ detail = e.response.text[:500] if e.response.text else "Unknown error"
60
+ else:
61
+ detail = e.response.text[:500] if e.response.text else "Unknown error"
62
+ except Exception:
63
+ detail = "Unknown error"
46
64
  raise XenfraAPIError(status_code=e.response.status_code, detail=detail) from e
47
65
  except httpx.RequestError as e:
48
66
  # Handle connection errors, timeouts, etc.
@@ -65,5 +83,5 @@ class XenfraClient:
65
83
 
66
84
  def __del__(self):
67
85
  """Destructor - cleanup if not already closed."""
68
- if hasattr(self, '_closed') and not self._closed:
86
+ if hasattr(self, "_closed") and not self._closed:
69
87
  self.close()
@@ -1,9 +1,10 @@
1
1
  """
2
2
  Enhanced XenfraClient with context management and lifecycle hooks.
3
3
  """
4
+
4
5
  import logging
5
6
  import os
6
- from typing import Any, Callable, Optional
7
+ from typing import Callable
7
8
 
8
9
  import httpx
9
10
 
@@ -102,7 +103,7 @@ class XenfraClient:
102
103
  headers={
103
104
  "Authorization": f"Bearer {self._token}",
104
105
  "Content-Type": "application/json",
105
- "User-Agent": "Xenfra-SDK/0.2.0",
106
+ "User-Agent": "Xenfra-SDK/0.2.4",
106
107
  },
107
108
  timeout=timeout,
108
109
  transport=transport,
@@ -178,7 +179,25 @@ class XenfraClient:
178
179
 
179
180
  except httpx.HTTPStatusError as e:
180
181
  # API error (4xx, 5xx)
181
- detail = e.response.json().get("detail", e.response.text) if e.response else str(e)
182
+ # Safe JSON parsing with fallback
183
+ if e.response:
184
+ try:
185
+ content_type = e.response.headers.get("content-type", "")
186
+ if "application/json" in content_type:
187
+ try:
188
+ error_data = e.response.json()
189
+ detail = error_data.get(
190
+ "detail",
191
+ e.response.text[:500] if e.response.text else "Unknown error",
192
+ )
193
+ except (ValueError, TypeError):
194
+ detail = e.response.text[:500] if e.response.text else "Unknown error"
195
+ else:
196
+ detail = e.response.text[:500] if e.response.text else "Unknown error"
197
+ except Exception:
198
+ detail = "Unknown error"
199
+ else:
200
+ detail = str(e)
182
201
 
183
202
  # Run error hooks
184
203
  error_context = {**request_context, "error": e, "response": e.response}
@@ -189,11 +208,12 @@ class XenfraClient:
189
208
  logger.warning(f"on_error hook failed: {hook_error}")
190
209
 
191
210
  # Log error
192
- logger.error(f"{method} {path} failed: {e.response.status_code if e.response else 'unknown'}")
211
+ logger.error(
212
+ f"{method} {path} failed: {e.response.status_code if e.response else 'unknown'}"
213
+ )
193
214
 
194
215
  raise XenfraAPIError(
195
- status_code=e.response.status_code if e.response else 500,
196
- detail=detail
216
+ status_code=e.response.status_code if e.response else 500, detail=detail
197
217
  ) from e
198
218
 
199
219
  except httpx.RequestError as e:
@@ -227,7 +247,9 @@ class XenfraClient:
227
247
  def __del__(self):
228
248
  """Destructor - cleanup if not already closed."""
229
249
  if not self._closed:
230
- logger.warning("XenfraClient was not properly closed. Use 'with' statement or call close().")
250
+ logger.warning(
251
+ "XenfraClient was not properly closed. Use 'with' statement or call close()."
252
+ )
231
253
  self.close()
232
254
 
233
255
  def __repr__(self):
@@ -238,6 +260,7 @@ class XenfraClient:
238
260
 
239
261
  # Example hooks for common use cases
240
262
 
263
+
241
264
  def logging_hook_before(request_context):
242
265
  """Example: Log all requests."""
243
266
  print(f"→ {request_context['method']} {request_context['url']}")
@@ -264,12 +287,14 @@ def rate_limit_tracker_hook(request_context, response):
264
287
  def request_timing_hook(request_context):
265
288
  """Example: Track request timing."""
266
289
  import time
290
+
267
291
  request_context["start_time"] = time.time()
268
292
 
269
293
 
270
294
  def response_timing_hook(request_context, response):
271
295
  """Example: Calculate request duration."""
272
296
  import time
297
+
273
298
  if "start_time" in request_context:
274
299
  duration = time.time() - request_context["start_time"]
275
300
  print(f"Request took {duration:.3f}s")
xenfra_sdk/config.py CHANGED
@@ -10,8 +10,8 @@ class Settings(BaseSettings):
10
10
  SECRET_KEY: str
11
11
  ENCRYPTION_KEY: str
12
12
 
13
- GITHUB_CLIENT_ID: str
14
- GITHUB_CLIENT_SECRET: str
13
+ GH_CLIENT_ID: str
14
+ GH_CLIENT_SECRET: str
15
15
  GITHUB_REDIRECT_URI: str
16
16
  GITHUB_WEBHOOK_SECRET: str
17
17
 
xenfra_sdk/db/models.py CHANGED
@@ -15,6 +15,7 @@ class Project(SQLModel, table=True):
15
15
  In a microservices architecture, we store the ID but don't enforce
16
16
  a foreign key constraint across service boundaries.
17
17
  """
18
+
18
19
  id: Optional[int] = Field(default=None, primary_key=True)
19
20
  droplet_id: int = Field(unique=True, index=True)
20
21
  name: str
@@ -3,6 +3,7 @@
3
3
  from fastapi import Depends, HTTPException, status
4
4
  from fastapi.security import OAuth2PasswordBearer
5
5
  from sqlmodel import Session, select
6
+
6
7
  from xenfra.db.models import User
7
8
  from xenfra.db.session import get_session
8
9
  from xenfra.security import decode_token
xenfra_sdk/models.py CHANGED
@@ -119,12 +119,15 @@ class ProjectRead(BaseModel):
119
119
 
120
120
  # Intelligence Service Models
121
121
 
122
+
122
123
  class PatchObject(BaseModel):
123
124
  """
124
125
  Represents a structured patch for a configuration file.
125
126
  """
126
127
 
127
- file: str | None = Field(None, description="The name of the file to be patched (e.g., 'requirements.txt')")
128
+ file: str | None = Field(
129
+ None, description="The name of the file to be patched (e.g., 'requirements.txt')"
130
+ )
128
131
  operation: str | None = Field(None, description="The patch operation (e.g., 'add', 'replace')")
129
132
  path: str | None = Field(None, description="A JSON-like path to the field to be changed")
130
133
  value: str | None = Field(None, description="The new value to apply")
@@ -160,11 +163,20 @@ class CodebaseAnalysisResponse(BaseModel):
160
163
  cache: str | None = Field(None, description="Detected cache (redis, memcached, none)")
161
164
  workers: list[str] | None = Field(None, description="Detected background workers (celery, rq)")
162
165
  env_vars: list[str] | None = Field(None, description="Required environment variables")
163
- package_manager: str = Field(..., description="Detected package manager (uv, pip, poetry, npm, pnpm, yarn, go, bundler)")
164
- dependency_file: str = Field(..., description="Dependency manifest file (pyproject.toml, requirements.txt, package.json, go.mod, Gemfile)")
166
+ package_manager: str = Field(
167
+ ..., description="Detected package manager (uv, pip, poetry, npm, pnpm, yarn, go, bundler)"
168
+ )
169
+ dependency_file: str = Field(
170
+ ...,
171
+ description="Dependency manifest file (pyproject.toml, requirements.txt, package.json, go.mod, Gemfile)",
172
+ )
165
173
  has_conflict: bool = Field(False, description="True if multiple package managers detected")
166
- detected_package_managers: list[PackageManagerOption] | None = Field(None, description="All detected package managers (if conflict)")
167
- instance_size: str = Field(..., description="Recommended instance size (basic, standard, premium)")
174
+ detected_package_managers: list[PackageManagerOption] | None = Field(
175
+ None, description="All detected package managers (if conflict)"
176
+ )
177
+ instance_size: str = Field(
178
+ ..., description="Recommended instance size (basic, standard, premium)"
179
+ )
168
180
  estimated_cost_monthly: float = Field(..., description="Estimated monthly cost in USD")
169
181
  confidence: float = Field(..., description="Confidence score (0.0-1.0)")
170
182
  notes: str | None = Field(None, description="Additional observations")
xenfra_sdk/privacy.py CHANGED
@@ -5,12 +5,16 @@ before it is sent to diagnostic endpoints, upholding privacy-first principles.
5
5
  """
6
6
 
7
7
  import json
8
+ import logging
9
+ import os
8
10
  import re
9
11
  from pathlib import Path
10
12
  from typing import List, Optional
11
13
 
12
14
  import httpx # For fetching patterns from URL
13
15
 
16
+ logger = logging.getLogger(__name__)
17
+
14
18
  # Path to the patterns file within the SDK
15
19
  _PATTERNS_FILE_PATH = Path(__file__).parent / "patterns.json"
16
20
  _REDACTION_PLACEHOLDER = "[REDACTED]"
@@ -20,8 +24,8 @@ _CACHED_PATTERNS: List[re.Pattern] = []
20
24
  def _load_patterns_from_file(file_path: Path) -> List[str]:
21
25
  """Loads raw regex patterns from a JSON file."""
22
26
  if not file_path.exists():
23
- print(
24
- f"Warning: Patterns file not found at {file_path}. No patterns will be used for scrubbing."
27
+ logger.warning(
28
+ f"Patterns file not found at {file_path}. No patterns will be used for scrubbing."
25
29
  )
26
30
  return []
27
31
  try:
@@ -29,7 +33,7 @@ def _load_patterns_from_file(file_path: Path) -> List[str]:
29
33
  config = json.load(f)
30
34
  return config.get("redaction_patterns", [])
31
35
  except json.JSONDecodeError as e:
32
- print(f"Error decoding patterns.json: {e}. Falling back to empty patterns.")
36
+ logger.error(f"Error decoding patterns.json: {e}. Falling back to empty patterns.")
33
37
  return []
34
38
 
35
39
 
@@ -38,16 +42,47 @@ async def _refresh_patterns_from_url(url: str) -> Optional[List[str]]:
38
42
  Fetches updated patterns from a URL asynchronously.
39
43
  """
40
44
  try:
41
- async with httpx.AsyncClient() as client:
42
- response = await client.get(url, timeout=5.0)
45
+ # Configure timeout from environment or default to 30 seconds
46
+ timeout_seconds = float(os.getenv("XENFRA_SDK_TIMEOUT", "30.0"))
47
+ timeout = httpx.Timeout(timeout_seconds, connect=10.0)
48
+
49
+ async with httpx.AsyncClient(timeout=timeout) as client:
50
+ response = await client.get(url)
43
51
  response.raise_for_status()
44
- config = response.json()
52
+
53
+ # Safe JSON parsing with content-type check
54
+ content_type = response.headers.get("content-type", "")
55
+ if "application/json" not in content_type:
56
+ logger.warning(
57
+ f"Expected JSON response from {url}, got {content_type}. "
58
+ "Skipping pattern refresh."
59
+ )
60
+ return None
61
+
62
+ try:
63
+ config = response.json()
64
+ except (ValueError, TypeError) as e:
65
+ logger.error(f"Failed to parse JSON from patterns URL {url}: {e}")
66
+ return None
67
+
68
+ if not isinstance(config, dict):
69
+ logger.error(
70
+ f"Expected dictionary from patterns URL {url}, got {type(config).__name__}"
71
+ )
72
+ return None
73
+
45
74
  return config.get("redaction_patterns", [])
75
+ except httpx.TimeoutException as e:
76
+ logger.warning(f"Timeout fetching patterns from {url}: {e}")
77
+ return None
46
78
  except httpx.RequestError as e:
47
- print(f"Error fetching patterns from {url}: {e}")
79
+ logger.warning(f"Error fetching patterns from {url}: {e}")
48
80
  return None
49
81
  except json.JSONDecodeError as e:
50
- print(f"Error decoding JSON from patterns URL {url}: {e}")
82
+ logger.error(f"Error decoding JSON from patterns URL {url}: {e}")
83
+ return None
84
+ except Exception as e:
85
+ logger.error(f"Unexpected error fetching patterns from {url}: {e}")
51
86
  return None
52
87
 
53
88
 
@@ -3,6 +3,7 @@ import logging
3
3
  # Import Deployment model when it's defined in models.py
4
4
  # from ..models import Deployment
5
5
  from ..exceptions import XenfraAPIError, XenfraError # Add XenfraError
6
+ from ..utils import safe_get_json_field, safe_json_parse
6
7
  from .base import BaseManager
7
8
 
8
9
  logger = logging.getLogger(__name__)
@@ -19,9 +20,8 @@ class DeploymentsManager(BaseManager):
19
20
  "framework": framework,
20
21
  }
21
22
  response = self._client._request("POST", "/deployments", json=payload)
22
- response.raise_for_status()
23
- # Assuming the API returns a dict, which will be parsed into a Deployment model
24
- return response.json()
23
+ # Safe JSON parsing
24
+ return safe_json_parse(response)
25
25
  except XenfraAPIError:
26
26
  raise
27
27
  except Exception as e:
@@ -42,9 +42,11 @@ class DeploymentsManager(BaseManager):
42
42
  """
43
43
  try:
44
44
  response = self._client._request("GET", f"/deployments/{deployment_id}/status")
45
- logger.debug(f"DeploymentsManager.get_status({deployment_id}) response: {response.status_code}")
46
- response.raise_for_status()
47
- return response.json()
45
+ logger.debug(
46
+ f"DeploymentsManager.get_status({deployment_id}) response: {response.status_code}"
47
+ )
48
+ # Safe JSON parsing - _request() already handles status codes
49
+ return safe_json_parse(response)
48
50
  except XenfraAPIError:
49
51
  raise # Re-raise API errors
50
52
  except Exception as e:
@@ -65,12 +67,16 @@ class DeploymentsManager(BaseManager):
65
67
  """
66
68
  try:
67
69
  response = self._client._request("GET", f"/deployments/{deployment_id}/logs")
68
- logger.debug(f"DeploymentsManager.get_logs({deployment_id}) response: {response.status_code}")
69
- response.raise_for_status()
70
+ logger.debug(
71
+ f"DeploymentsManager.get_logs({deployment_id}) response: {response.status_code}"
72
+ )
70
73
 
71
- # Parse response - API should return {"logs": "log content"}
72
- data = response.json()
73
- logs = data.get("logs", "")
74
+ # Safe JSON parsing with structure validation - _request() already handles status codes
75
+ data = safe_json_parse(response)
76
+ if not isinstance(data, dict):
77
+ raise XenfraError(f"Expected dictionary response, got {type(data).__name__}")
78
+
79
+ logs = safe_get_json_field(data, "logs", "")
74
80
 
75
81
  if not logs:
76
82
  logger.warning(f"No logs found for deployment {deployment_id}")
@@ -2,10 +2,12 @@
2
2
  Intelligence resource manager for Xenfra SDK.
3
3
  Provides AI-powered deployment diagnosis and codebase analysis.
4
4
  """
5
+
5
6
  import logging
6
7
 
7
8
  from ..exceptions import XenfraAPIError, XenfraError
8
9
  from ..models import CodebaseAnalysisResponse, DiagnosisResponse
10
+ from ..utils import safe_json_parse
9
11
  from .base import BaseManager
10
12
 
11
13
  logger = logging.getLogger(__name__)
@@ -21,10 +23,7 @@ class IntelligenceManager(BaseManager):
21
23
  """
22
24
 
23
25
  def diagnose(
24
- self,
25
- logs: str,
26
- package_manager: str | None = None,
27
- dependency_file: str | None = None
26
+ self, logs: str, package_manager: str | None = None, dependency_file: str | None = None
28
27
  ) -> DiagnosisResponse:
29
28
  """
30
29
  Diagnose deployment failure from logs using AI.
@@ -51,18 +50,13 @@ class IntelligenceManager(BaseManager):
51
50
  if dependency_file:
52
51
  payload["dependency_file"] = dependency_file
53
52
 
54
- response = self._client._request(
55
- "POST",
56
- "/intelligence/diagnose",
57
- json=payload
58
- )
53
+ response = self._client._request("POST", "/intelligence/diagnose", json=payload)
59
54
 
60
- logger.debug(
61
- f"IntelligenceManager.diagnose response: status={response.status_code}"
62
- )
55
+ logger.debug(f"IntelligenceManager.diagnose response: status={response.status_code}")
63
56
 
64
- response.raise_for_status()
65
- return DiagnosisResponse(**response.json())
57
+ # Safe JSON parsing
58
+ data = safe_json_parse(response)
59
+ return DiagnosisResponse(**data)
66
60
  except XenfraAPIError:
67
61
  raise
68
62
  except Exception as e:
@@ -85,17 +79,16 @@ class IntelligenceManager(BaseManager):
85
79
  """
86
80
  try:
87
81
  response = self._client._request(
88
- "POST",
89
- "/intelligence/analyze-codebase",
90
- json={"code_snippets": code_snippets}
82
+ "POST", "/intelligence/analyze-codebase", json={"code_snippets": code_snippets}
91
83
  )
92
84
 
93
85
  logger.debug(
94
86
  f"IntelligenceManager.analyze_codebase response: status={response.status_code}"
95
87
  )
96
88
 
97
- response.raise_for_status()
98
- return CodebaseAnalysisResponse(**response.json())
89
+ # Safe JSON parsing
90
+ data = safe_json_parse(response)
91
+ return CodebaseAnalysisResponse(**data)
99
92
  except XenfraAPIError:
100
93
  raise
101
94
  except Exception as e:
@@ -2,6 +2,7 @@ import logging
2
2
 
3
3
  from ..exceptions import XenfraAPIError, XenfraError # Add XenfraError
4
4
  from ..models import ProjectRead
5
+ from ..utils import safe_get_json_field, safe_json_parse
5
6
  from .base import BaseManager
6
7
 
7
8
  logger = logging.getLogger(__name__)
@@ -18,8 +19,16 @@ class ProjectsManager(BaseManager):
18
19
  f"body={response.text[:200]}..." # Truncate long responses
19
20
  )
20
21
 
21
- response.raise_for_status()
22
- return [ProjectRead(**p) for p in response.json()["projects"]]
22
+ # Safe JSON parsing with structure validation
23
+ data = safe_json_parse(response)
24
+ projects = safe_get_json_field(data, "projects", [])
25
+
26
+ if not isinstance(projects, list):
27
+ raise XenfraError(
28
+ f"Expected 'projects' to be a list, got {type(projects).__name__}"
29
+ )
30
+
31
+ return [ProjectRead(**p) for p in projects]
23
32
  except XenfraAPIError:
24
33
  raise # Re-raise API errors
25
34
  except Exception as e:
@@ -42,18 +51,16 @@ class ProjectsManager(BaseManager):
42
51
  try:
43
52
  response = self._client._request("GET", f"/projects/{project_id}")
44
53
  logger.debug(f"ProjectsManager.show({project_id}) response: {response.status_code}")
45
- response.raise_for_status()
46
- return ProjectRead(**response.json())
54
+ # Safe JSON parsing
55
+ data = safe_json_parse(response)
56
+ return ProjectRead(**data)
47
57
  except XenfraAPIError:
48
58
  raise # Re-raise API errors
49
59
  except Exception as e:
50
60
  raise XenfraError(f"Failed to get project {project_id}: {e}")
51
61
 
52
62
  def create(
53
- self,
54
- name: str,
55
- region: str = "nyc3",
56
- size_slug: str = "s-1vcpu-1gb"
63
+ self, name: str, region: str = "nyc3", size_slug: str = "s-1vcpu-1gb"
57
64
  ) -> ProjectRead:
58
65
  """Create a new project.
59
66
 
@@ -70,15 +77,12 @@ class ProjectsManager(BaseManager):
70
77
  XenfraError: If there's a network or parsing error.
71
78
  """
72
79
  try:
73
- payload = {
74
- "name": name,
75
- "region": region,
76
- "size_slug": size_slug
77
- }
80
+ payload = {"name": name, "region": region, "size_slug": size_slug}
78
81
  logger.debug(f"ProjectsManager.create payload: {payload}")
79
82
  response = self._client._request("POST", "/projects/", json=payload)
80
- response.raise_for_status()
81
- return ProjectRead(**response.json())
83
+ # Safe JSON parsing
84
+ data = safe_json_parse(response)
85
+ return ProjectRead(**data)
82
86
  except XenfraAPIError:
83
87
  raise
84
88
  except Exception as e:
@@ -87,8 +91,8 @@ class ProjectsManager(BaseManager):
87
91
  def delete(self, project_id: str) -> None:
88
92
  """Deletes a project."""
89
93
  try:
90
- response = self._client._request("DELETE", f"/projects/{project_id}")
91
- response.raise_for_status()
94
+ # _request() already handles status codes and raises XenfraAPIError for non-2xx
95
+ self._client._request("DELETE", f"/projects/{project_id}")
92
96
  except XenfraAPIError:
93
97
  raise
94
98
  except Exception as e:
xenfra_sdk/utils.py CHANGED
@@ -1,5 +1,10 @@
1
1
  import os
2
2
  import tomllib # Python 3.11+
3
+ from typing import Any, Dict
4
+
5
+ import httpx
6
+
7
+ from .exceptions import XenfraError
3
8
 
4
9
 
5
10
  def get_project_context():
@@ -68,3 +73,47 @@ def get_project_context():
68
73
  }
69
74
 
70
75
  return context
76
+
77
+
78
+ def safe_json_parse(response: httpx.Response) -> Dict[str, Any]:
79
+ """
80
+ Safely parse JSON from HTTP response with content-type validation and error handling.
81
+
82
+ Args:
83
+ response: HTTP response object
84
+
85
+ Returns:
86
+ Parsed JSON dictionary
87
+
88
+ Raises:
89
+ XenfraError: If response is not JSON or parsing fails
90
+ """
91
+ content_type = response.headers.get("content-type", "")
92
+ if "application/json" not in content_type:
93
+ # Try to get error text for better error messages
94
+ error_text = response.text[:500] if response.text else "Unknown error"
95
+ raise XenfraError(f"Expected JSON response, got {content_type}. Response: {error_text}")
96
+
97
+ try:
98
+ return response.json()
99
+ except (ValueError, TypeError) as e:
100
+ error_text = response.text[:500] if response.text else "Unknown error"
101
+ raise XenfraError(f"Failed to parse JSON response: {e}. Response: {error_text}")
102
+
103
+
104
+ def safe_get_json_field(data: Dict[str, Any], field: str, default: Any = None) -> Any:
105
+ """
106
+ Safely get a field from JSON data with validation.
107
+
108
+ Args:
109
+ data: JSON dictionary
110
+ field: Field name to retrieve
111
+ default: Default value if field is missing
112
+
113
+ Returns:
114
+ Field value or default
115
+ """
116
+ if not isinstance(data, dict):
117
+ raise XenfraError(f"Expected dictionary, got {type(data).__name__}")
118
+
119
+ return data.get(field, default)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: xenfra-sdk
3
- Version: 0.1.0
3
+ Version: 0.1.2
4
4
  Summary: Xenfra SDK: Core engine and utilities for the Xenfra platform.
5
5
  Author: xenfra-cloud
6
6
  Author-email: xenfra-cloud <xenfracloud@gmail.com>
@@ -1,31 +1,31 @@
1
1
  xenfra_sdk/__init__.py,sha256=lk9xo2msYvs_JgBePTIuxRb2sBW-egJS_EAOq4w4xQo,467
2
2
  xenfra_sdk/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
3
  xenfra_sdk/cli/main.py,sha256=541nlIUYFFeu4h1sCXivaHMC7SqpskazI0YocM8ylh4,7958
4
- xenfra_sdk/client.py,sha256=vFtfAof8X270-qs6-gD1Qo8LGjpQKxCfwbEe5GWd6nE,2641
5
- xenfra_sdk/client_with_hooks.py,sha256=ViAWF4lnUml4gjVapTwC7UUQesJn5ZaeGHluZlRC-50,9165
6
- xenfra_sdk/config.py,sha256=5dlmpxzM9OXJvfhiwG7DsU2XXf1yte4-mISDJdbpzPk,596
4
+ xenfra_sdk/client.py,sha256=ufxjjVY3UG1roR0q8LYBmCHOJ13uRQuycqTqYuHgqTs,3514
5
+ xenfra_sdk/client_with_hooks.py,sha256=iN-xTGdeSPizktM6UG-aZEsuwQc5OgosNYUf1_Tq4Dc,10046
6
+ xenfra_sdk/config.py,sha256=gaT4k5iJgW9guNVmlnYkCFGDSU1_er4LRZA2CgfKmJ0,588
7
7
  xenfra_sdk/db/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
- xenfra_sdk/db/models.py,sha256=H29uHzXizkim5KnEAdmfMO8mytkCDCQl2NPzTgMMD7w,874
8
+ xenfra_sdk/db/models.py,sha256=LqnpUbtSZ21jdPVzdyFyxpYqOXUkBVl5JGXq85ASI7g,875
9
9
  xenfra_sdk/db/session.py,sha256=LoTKFO3FTsx5AtZ-0ZsplxXjAdzOgcr3Yk-dkeJsz5U,823
10
- xenfra_sdk/dependencies.py,sha256=eDS7W8Dp5ucNvH9KGlRch-cUB348zRqjs3Odn_d9MH4,1210
10
+ xenfra_sdk/dependencies.py,sha256=WHGfIrEYkss5yuBd_uOFrB6lPBWM2X0mEuG26ILAjXI,1211
11
11
  xenfra_sdk/dockerizer.py,sha256=73CFwztKogBmlX00tek0M8evncnZRDThQKmg7auw6mc,3059
12
12
  xenfra_sdk/engine.py,sha256=5qrtDc8qXC9xuZGPUwcUeGEtVuVksWY64sCxh-Wqa7o,15517
13
13
  xenfra_sdk/exceptions.py,sha256=aMVtDVlzG7-FT2G_b-pJSuuey22B4YvC-b-L37GaImM,477
14
14
  xenfra_sdk/mcp_client.py,sha256=NZtQz_qK_8i504rVPXlE1vPdzt75hg8Lkp4d8BA8dk0,5777
15
- xenfra_sdk/models.py,sha256=73rZMHP2iPRcX9PC8KUgkmNEVgDozD4Tj09UneQhYJU,6923
15
+ xenfra_sdk/models.py,sha256=o03GUy4qcgAJa9KzR9UXWKLPWTR5gvE8GF6KSAj80eM,7003
16
16
  xenfra_sdk/patterns.json,sha256=xHxbc0ogHDwysMczi30_hW1Ylfdsf-nsQdAom7RZ4KI,446
17
- xenfra_sdk/privacy.py,sha256=-KmDyFDGmFTg4vS68qtVnDgXxbl9xJmKwPKDG1zPaFU,4176
17
+ xenfra_sdk/privacy.py,sha256=Bscv7bopCQTeJhvU8Z2jxdBfc5tCZDY5kuODonTcgSk,5509
18
18
  xenfra_sdk/recipes.py,sha256=J6-7tDWVXv8IelkdBta5pDRhD0o5VgAM_jWDKvPvm5g,852
19
19
  xenfra_sdk/resources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
20
  xenfra_sdk/resources/base.py,sha256=5n-HTKAnIX2lTgXwio0xtwoaBn-nksjdm8qRTpe3iDk,81
21
- xenfra_sdk/resources/deployments.py,sha256=qE9hPW0skt7w6QaqalVGaBAIFu6z4M97crvEn0xzMG4,3116
22
- xenfra_sdk/resources/intelligence.py,sha256=8W_XjOJSLH9qFNBepc6jgS9wer0NuML4BG8DU6LfXjY,3382
23
- xenfra_sdk/resources/projects.py,sha256=PNhLhu3-8jR1ucSGtKDRH5fvpNb1IQSzUNXEcojhmHY,3350
21
+ xenfra_sdk/resources/deployments.py,sha256=QIOd-9L1m-FW5I6eXZEtQSteg_gkIzk2nnqr_hQs5gc,3339
22
+ xenfra_sdk/resources/intelligence.py,sha256=ihkhv8jj4FDB4pbSezAnUL-5syZ77lLXPI6X38THcnQ,3323
23
+ xenfra_sdk/resources/projects.py,sha256=EsCVXmqkhWl_Guz_8WDQDi3kAm1Wyg1rjXcyAigPD6E,3712
24
24
  xenfra_sdk/security.py,sha256=6vMZpbglhkRGBVVj4RCTu45-MCnQ15wt94-996zmaT8,1199
25
25
  xenfra_sdk/templates/Dockerfile.j2,sha256=apWts895OOoUYwj_fOa6OiylFB5m8zFEYvJ1Nki32YM,664
26
26
  xenfra_sdk/templates/cloud-init.sh.j2,sha256=QCWG8hL1V05bAQ7BQ70QfuhIvS4tnsL8ZTCVtyi9F0A,2222
27
27
  xenfra_sdk/templates/docker-compose.yml.j2,sha256=zKUT2cd_FrxXvRxE-vAAjuQk3-nLNQjRe-StkhAWRQA,860
28
- xenfra_sdk/utils.py,sha256=J2q2ZATQxgrtoJXrbDCPRNyD7PFuv7ZAybcE99O2cCc,2400
29
- xenfra_sdk-0.1.0.dist-info/WHEEL,sha256=ZyFSCYkV2BrxH6-HRVRg3R9Fo7MALzer9KiPYqNxSbo,79
30
- xenfra_sdk-0.1.0.dist-info/METADATA,sha256=UiaTkJ03P59x9D4MT-ONOk1rx3uhojR1VJ914kiRACQ,3889
31
- xenfra_sdk-0.1.0.dist-info/RECORD,,
28
+ xenfra_sdk/utils.py,sha256=d8eCjjV32QwqoJa759CEcETnnsjG5qVKDLQ84yYtlus,3898
29
+ xenfra_sdk-0.1.2.dist-info/WHEEL,sha256=ZyFSCYkV2BrxH6-HRVRg3R9Fo7MALzer9KiPYqNxSbo,79
30
+ xenfra_sdk-0.1.2.dist-info/METADATA,sha256=sFTH52KuMEL0mWE3gFhKAYoOE_rS6iX_FOFJp19bhtU,3889
31
+ xenfra_sdk-0.1.2.dist-info/RECORD,,