xenfra-sdk 0.1.1__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
@@ -6,7 +6,6 @@ from .exceptions import AuthenticationError, XenfraAPIError, XenfraError
6
6
  from .resources.deployments import DeploymentsManager
7
7
  from .resources.intelligence import IntelligenceManager
8
8
  from .resources.projects import ProjectsManager
9
- from .utils import safe_json_parse
10
9
 
11
10
 
12
11
  class XenfraClient:
@@ -53,7 +52,9 @@ class XenfraClient:
53
52
  if "application/json" in content_type:
54
53
  try:
55
54
  error_data = e.response.json()
56
- detail = error_data.get("detail", e.response.text[:500] if e.response.text else "Unknown error")
55
+ detail = error_data.get(
56
+ "detail", e.response.text[:500] if e.response.text else "Unknown error"
57
+ )
57
58
  except (ValueError, TypeError):
58
59
  detail = e.response.text[:500] if e.response.text else "Unknown error"
59
60
  else:
@@ -82,5 +83,5 @@ class XenfraClient:
82
83
 
83
84
  def __del__(self):
84
85
  """Destructor - cleanup if not already closed."""
85
- if hasattr(self, '_closed') and not self._closed:
86
+ if hasattr(self, "_closed") and not self._closed:
86
87
  self.close()
@@ -12,7 +12,6 @@ from .exceptions import AuthenticationError, XenfraAPIError, XenfraError
12
12
  from .resources.deployments import DeploymentsManager
13
13
  from .resources.intelligence import IntelligenceManager
14
14
  from .resources.projects import ProjectsManager
15
- from .utils import safe_json_parse
16
15
 
17
16
  logger = logging.getLogger(__name__)
18
17
 
@@ -187,7 +186,10 @@ class XenfraClient:
187
186
  if "application/json" in content_type:
188
187
  try:
189
188
  error_data = e.response.json()
190
- detail = error_data.get("detail", e.response.text[:500] if e.response.text else "Unknown error")
189
+ detail = error_data.get(
190
+ "detail",
191
+ e.response.text[:500] if e.response.text else "Unknown error",
192
+ )
191
193
  except (ValueError, TypeError):
192
194
  detail = e.response.text[:500] if e.response.text else "Unknown error"
193
195
  else:
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
@@ -45,11 +45,11 @@ async def _refresh_patterns_from_url(url: str) -> Optional[List[str]]:
45
45
  # Configure timeout from environment or default to 30 seconds
46
46
  timeout_seconds = float(os.getenv("XENFRA_SDK_TIMEOUT", "30.0"))
47
47
  timeout = httpx.Timeout(timeout_seconds, connect=10.0)
48
-
48
+
49
49
  async with httpx.AsyncClient(timeout=timeout) as client:
50
50
  response = await client.get(url)
51
51
  response.raise_for_status()
52
-
52
+
53
53
  # Safe JSON parsing with content-type check
54
54
  content_type = response.headers.get("content-type", "")
55
55
  if "application/json" not in content_type:
@@ -58,17 +58,19 @@ async def _refresh_patterns_from_url(url: str) -> Optional[List[str]]:
58
58
  "Skipping pattern refresh."
59
59
  )
60
60
  return None
61
-
61
+
62
62
  try:
63
63
  config = response.json()
64
64
  except (ValueError, TypeError) as e:
65
65
  logger.error(f"Failed to parse JSON from patterns URL {url}: {e}")
66
66
  return None
67
-
67
+
68
68
  if not isinstance(config, dict):
69
- logger.error(f"Expected dictionary from patterns URL {url}, got {type(config).__name__}")
69
+ logger.error(
70
+ f"Expected dictionary from patterns URL {url}, got {type(config).__name__}"
71
+ )
70
72
  return None
71
-
73
+
72
74
  return config.get("redaction_patterns", [])
73
75
  except httpx.TimeoutException as e:
74
76
  logger.warning(f"Timeout fetching patterns from {url}: {e}")
@@ -42,7 +42,9 @@ 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}")
45
+ logger.debug(
46
+ f"DeploymentsManager.get_status({deployment_id}) response: {response.status_code}"
47
+ )
46
48
  # Safe JSON parsing - _request() already handles status codes
47
49
  return safe_json_parse(response)
48
50
  except XenfraAPIError:
@@ -65,13 +67,15 @@ 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}")
70
+ logger.debug(
71
+ f"DeploymentsManager.get_logs({deployment_id}) response: {response.status_code}"
72
+ )
69
73
 
70
74
  # Safe JSON parsing with structure validation - _request() already handles status codes
71
75
  data = safe_json_parse(response)
72
76
  if not isinstance(data, dict):
73
77
  raise XenfraError(f"Expected dictionary response, got {type(data).__name__}")
74
-
78
+
75
79
  logs = safe_get_json_field(data, "logs", "")
76
80
 
77
81
  if not logs:
@@ -2,6 +2,7 @@
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
@@ -22,10 +23,7 @@ class IntelligenceManager(BaseManager):
22
23
  """
23
24
 
24
25
  def diagnose(
25
- self,
26
- logs: str,
27
- package_manager: str | None = None,
28
- dependency_file: str | None = None
26
+ self, logs: str, package_manager: str | None = None, dependency_file: str | None = None
29
27
  ) -> DiagnosisResponse:
30
28
  """
31
29
  Diagnose deployment failure from logs using AI.
@@ -52,15 +50,9 @@ class IntelligenceManager(BaseManager):
52
50
  if dependency_file:
53
51
  payload["dependency_file"] = dependency_file
54
52
 
55
- response = self._client._request(
56
- "POST",
57
- "/intelligence/diagnose",
58
- json=payload
59
- )
53
+ response = self._client._request("POST", "/intelligence/diagnose", json=payload)
60
54
 
61
- logger.debug(
62
- f"IntelligenceManager.diagnose response: status={response.status_code}"
63
- )
55
+ logger.debug(f"IntelligenceManager.diagnose response: status={response.status_code}")
64
56
 
65
57
  # Safe JSON parsing
66
58
  data = safe_json_parse(response)
@@ -87,9 +79,7 @@ class IntelligenceManager(BaseManager):
87
79
  """
88
80
  try:
89
81
  response = self._client._request(
90
- "POST",
91
- "/intelligence/analyze-codebase",
92
- json={"code_snippets": code_snippets}
82
+ "POST", "/intelligence/analyze-codebase", json={"code_snippets": code_snippets}
93
83
  )
94
84
 
95
85
  logger.debug(
@@ -24,7 +24,9 @@ class ProjectsManager(BaseManager):
24
24
  projects = safe_get_json_field(data, "projects", [])
25
25
 
26
26
  if not isinstance(projects, list):
27
- raise XenfraError(f"Expected 'projects' to be a list, got {type(projects).__name__}")
27
+ raise XenfraError(
28
+ f"Expected 'projects' to be a list, got {type(projects).__name__}"
29
+ )
28
30
 
29
31
  return [ProjectRead(**p) for p in projects]
30
32
  except XenfraAPIError:
@@ -58,10 +60,7 @@ class ProjectsManager(BaseManager):
58
60
  raise XenfraError(f"Failed to get project {project_id}: {e}")
59
61
 
60
62
  def create(
61
- self,
62
- name: str,
63
- region: str = "nyc3",
64
- size_slug: str = "s-1vcpu-1gb"
63
+ self, name: str, region: str = "nyc3", size_slug: str = "s-1vcpu-1gb"
65
64
  ) -> ProjectRead:
66
65
  """Create a new project.
67
66
 
@@ -78,11 +77,7 @@ class ProjectsManager(BaseManager):
78
77
  XenfraError: If there's a network or parsing error.
79
78
  """
80
79
  try:
81
- payload = {
82
- "name": name,
83
- "region": region,
84
- "size_slug": size_slug
85
- }
80
+ payload = {"name": name, "region": region, "size_slug": size_slug}
86
81
  logger.debug(f"ProjectsManager.create payload: {payload}")
87
82
  response = self._client._request("POST", "/projects/", json=payload)
88
83
  # Safe JSON parsing
xenfra_sdk/utils.py CHANGED
@@ -1,6 +1,6 @@
1
1
  import os
2
2
  import tomllib # Python 3.11+
3
- from typing import Any, Dict, Optional
3
+ from typing import Any, Dict
4
4
 
5
5
  import httpx
6
6
 
@@ -92,9 +92,7 @@ def safe_json_parse(response: httpx.Response) -> Dict[str, Any]:
92
92
  if "application/json" not in content_type:
93
93
  # Try to get error text for better error messages
94
94
  error_text = response.text[:500] if response.text else "Unknown error"
95
- raise XenfraError(
96
- f"Expected JSON response, got {content_type}. Response: {error_text}"
97
- )
95
+ raise XenfraError(f"Expected JSON response, got {content_type}. Response: {error_text}")
98
96
 
99
97
  try:
100
98
  return response.json()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: xenfra-sdk
3
- Version: 0.1.1
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=pnmdmmTtECyV7MmT2_DViXeaQP0q1W1eOhcqOTdmi80,3495
5
- xenfra_sdk/client_with_hooks.py,sha256=5mKIF3rbN1hlZKCCNOlMyFBbuwugBeCU9_8ZcQ_8uOs,9986
4
+ xenfra_sdk/client.py,sha256=ufxjjVY3UG1roR0q8LYBmCHOJ13uRQuycqTqYuHgqTs,3514
5
+ xenfra_sdk/client_with_hooks.py,sha256=iN-xTGdeSPizktM6UG-aZEsuwQc5OgosNYUf1_Tq4Dc,10046
6
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=XXNLCrPZzqbYj8qrcootygE2WiDYyMVdMO7h0ow7vBQ,5527
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=ybIV4ViNhnjWRkPaynp0wQjEKBjKby0GkAPd3_ohu-w,3291
22
- xenfra_sdk/resources/intelligence.py,sha256=W1J2tr4W3xKB1v8Wya0edXB07Vr3zO0JZ7mIDk_dU1w,3470
23
- xenfra_sdk/resources/projects.py,sha256=ZMox7siqSXWuXHsUBYkUb10Qik7wNvgPBZWN3C4gQnA,3760
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=uLlDb-kNRlr6RJvU8vJ6vXfgUEZeWZDfN2UtlrQoxfs,3930
29
- xenfra_sdk-0.1.1.dist-info/WHEEL,sha256=ZyFSCYkV2BrxH6-HRVRg3R9Fo7MALzer9KiPYqNxSbo,79
30
- xenfra_sdk-0.1.1.dist-info/METADATA,sha256=jg_JTeKLR_SzyMovzvjYub641lc3nSObkPy7-V9GEe0,3889
31
- xenfra_sdk-0.1.1.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,,