xenfra-sdk 0.1.3__tar.gz → 0.1.4__tar.gz

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.
Files changed (32) hide show
  1. {xenfra_sdk-0.1.3 → xenfra_sdk-0.1.4}/PKG-INFO +1 -1
  2. {xenfra_sdk-0.1.3 → xenfra_sdk-0.1.4}/pyproject.toml +1 -1
  3. xenfra_sdk-0.1.4/src/xenfra_sdk/resources/deployments.py +185 -0
  4. xenfra_sdk-0.1.3/src/xenfra_sdk/resources/deployments.py +0 -89
  5. {xenfra_sdk-0.1.3 → xenfra_sdk-0.1.4}/README.md +0 -0
  6. {xenfra_sdk-0.1.3 → xenfra_sdk-0.1.4}/src/xenfra_sdk/__init__.py +0 -0
  7. {xenfra_sdk-0.1.3 → xenfra_sdk-0.1.4}/src/xenfra_sdk/cli/__init__.py +0 -0
  8. {xenfra_sdk-0.1.3 → xenfra_sdk-0.1.4}/src/xenfra_sdk/cli/main.py +0 -0
  9. {xenfra_sdk-0.1.3 → xenfra_sdk-0.1.4}/src/xenfra_sdk/client.py +0 -0
  10. {xenfra_sdk-0.1.3 → xenfra_sdk-0.1.4}/src/xenfra_sdk/client_with_hooks.py +0 -0
  11. {xenfra_sdk-0.1.3 → xenfra_sdk-0.1.4}/src/xenfra_sdk/config.py +0 -0
  12. {xenfra_sdk-0.1.3 → xenfra_sdk-0.1.4}/src/xenfra_sdk/db/__init__.py +0 -0
  13. {xenfra_sdk-0.1.3 → xenfra_sdk-0.1.4}/src/xenfra_sdk/db/models.py +0 -0
  14. {xenfra_sdk-0.1.3 → xenfra_sdk-0.1.4}/src/xenfra_sdk/db/session.py +0 -0
  15. {xenfra_sdk-0.1.3 → xenfra_sdk-0.1.4}/src/xenfra_sdk/dependencies.py +0 -0
  16. {xenfra_sdk-0.1.3 → xenfra_sdk-0.1.4}/src/xenfra_sdk/dockerizer.py +0 -0
  17. {xenfra_sdk-0.1.3 → xenfra_sdk-0.1.4}/src/xenfra_sdk/engine.py +0 -0
  18. {xenfra_sdk-0.1.3 → xenfra_sdk-0.1.4}/src/xenfra_sdk/exceptions.py +0 -0
  19. {xenfra_sdk-0.1.3 → xenfra_sdk-0.1.4}/src/xenfra_sdk/mcp_client.py +0 -0
  20. {xenfra_sdk-0.1.3 → xenfra_sdk-0.1.4}/src/xenfra_sdk/models.py +0 -0
  21. {xenfra_sdk-0.1.3 → xenfra_sdk-0.1.4}/src/xenfra_sdk/patterns.json +0 -0
  22. {xenfra_sdk-0.1.3 → xenfra_sdk-0.1.4}/src/xenfra_sdk/privacy.py +0 -0
  23. {xenfra_sdk-0.1.3 → xenfra_sdk-0.1.4}/src/xenfra_sdk/recipes.py +0 -0
  24. {xenfra_sdk-0.1.3 → xenfra_sdk-0.1.4}/src/xenfra_sdk/resources/__init__.py +0 -0
  25. {xenfra_sdk-0.1.3 → xenfra_sdk-0.1.4}/src/xenfra_sdk/resources/base.py +0 -0
  26. {xenfra_sdk-0.1.3 → xenfra_sdk-0.1.4}/src/xenfra_sdk/resources/intelligence.py +0 -0
  27. {xenfra_sdk-0.1.3 → xenfra_sdk-0.1.4}/src/xenfra_sdk/resources/projects.py +0 -0
  28. {xenfra_sdk-0.1.3 → xenfra_sdk-0.1.4}/src/xenfra_sdk/security.py +0 -0
  29. {xenfra_sdk-0.1.3 → xenfra_sdk-0.1.4}/src/xenfra_sdk/templates/Dockerfile.j2 +0 -0
  30. {xenfra_sdk-0.1.3 → xenfra_sdk-0.1.4}/src/xenfra_sdk/templates/cloud-init.sh.j2 +0 -0
  31. {xenfra_sdk-0.1.3 → xenfra_sdk-0.1.4}/src/xenfra_sdk/templates/docker-compose.yml.j2 +0 -0
  32. {xenfra_sdk-0.1.3 → xenfra_sdk-0.1.4}/src/xenfra_sdk/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: xenfra-sdk
3
- Version: 0.1.3
3
+ Version: 0.1.4
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,6 +1,6 @@
1
1
  [project]
2
2
  name = "xenfra-sdk"
3
- version = "0.1.3"
3
+ version = "0.1.4"
4
4
  description = "Xenfra SDK: Core engine and utilities for the Xenfra platform."
5
5
  readme = "README.md"
6
6
  authors = [
@@ -0,0 +1,185 @@
1
+ import json
2
+ import logging
3
+ from typing import Iterator
4
+
5
+ # Import Deployment model when it's defined in models.py
6
+ # from ..models import Deployment
7
+ from ..exceptions import XenfraAPIError, XenfraError # Add XenfraError
8
+ from ..utils import safe_get_json_field, safe_json_parse
9
+ from .base import BaseManager
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+
14
+ class DeploymentsManager(BaseManager):
15
+ def create(self, project_name: str, git_repo: str, branch: str, framework: str, region: str = None, size_slug: str = None) -> dict:
16
+ """Creates a new deployment."""
17
+ try:
18
+ payload = {
19
+ "project_name": project_name,
20
+ "git_repo": git_repo,
21
+ "branch": branch,
22
+ "framework": framework,
23
+ }
24
+ if region:
25
+ payload["region"] = region
26
+ if size_slug:
27
+ payload["size_slug"] = size_slug
28
+
29
+ response = self._client._request("POST", "/deployments", json=payload)
30
+ # Safe JSON parsing
31
+ return safe_json_parse(response)
32
+ except XenfraAPIError:
33
+ raise
34
+ except Exception as e:
35
+ raise XenfraError(f"Failed to create deployment: {e}")
36
+
37
+ def get_status(self, deployment_id: str) -> dict:
38
+ """Get status for a specific deployment.
39
+
40
+ Args:
41
+ deployment_id: The unique identifier for the deployment.
42
+
43
+ Returns:
44
+ dict: Deployment status information including state, progress, etc.
45
+
46
+ Raises:
47
+ XenfraAPIError: If the API returns an error (e.g., 404 not found).
48
+ XenfraError: If there's a network or parsing error.
49
+ """
50
+ try:
51
+ response = self._client._request("GET", f"/deployments/{deployment_id}/status")
52
+ logger.debug(
53
+ f"DeploymentsManager.get_status({deployment_id}) response: {response.status_code}"
54
+ )
55
+ # Safe JSON parsing - _request() already handles status codes
56
+ return safe_json_parse(response)
57
+ except XenfraAPIError:
58
+ raise # Re-raise API errors
59
+ except Exception as e:
60
+ raise XenfraError(f"Failed to get status for deployment {deployment_id}: {e}")
61
+
62
+ def get_logs(self, deployment_id: str) -> str:
63
+ """Get logs for a specific deployment.
64
+
65
+ Args:
66
+ deployment_id: The unique identifier for the deployment.
67
+
68
+ Returns:
69
+ str: The deployment logs as plain text.
70
+
71
+ Raises:
72
+ XenfraAPIError: If the API returns an error (e.g., 404 not found).
73
+ XenfraError: If there's a network or parsing error.
74
+ """
75
+ try:
76
+ response = self._client._request("GET", f"/deployments/{deployment_id}/logs")
77
+ logger.debug(
78
+ f"DeploymentsManager.get_logs({deployment_id}) response: {response.status_code}"
79
+ )
80
+
81
+ # Safe JSON parsing with structure validation - _request() already handles status codes
82
+ data = safe_json_parse(response)
83
+ if not isinstance(data, dict):
84
+ raise XenfraError(f"Expected dictionary response, got {type(data).__name__}")
85
+
86
+ logs = safe_get_json_field(data, "logs", "")
87
+
88
+ if not logs:
89
+ logger.warning(f"No logs found for deployment {deployment_id}")
90
+
91
+ return logs
92
+
93
+ except XenfraAPIError:
94
+ raise # Re-raise API errors
95
+ except Exception as e:
96
+ raise XenfraError(f"Failed to get logs for deployment {deployment_id}: {e}")
97
+
98
+ def create_stream(self, project_name: str, git_repo: str, branch: str, framework: str, region: str = None, size_slug: str = None) -> Iterator[dict]:
99
+ """
100
+ Creates a new deployment with real-time SSE log streaming.
101
+
102
+ Yields SSE events as dictionaries with 'event' and 'data' keys.
103
+
104
+ Args:
105
+ project_name: Name of the project
106
+ git_repo: Git repository URL
107
+ branch: Git branch to deploy
108
+ framework: Framework type (fastapi, flask, django)
109
+ region: DigitalOcean region (optional)
110
+ size_slug: DigitalOcean droplet size (optional)
111
+
112
+ Yields:
113
+ dict: SSE events with 'event' and 'data' fields
114
+
115
+ Example:
116
+ for event in client.deployments.create_stream(...):
117
+ if event['event'] == 'log':
118
+ print(event['data'])
119
+ elif event['event'] == 'deployment_complete':
120
+ print("Done!")
121
+ """
122
+ payload = {
123
+ "project_name": project_name,
124
+ "git_repo": git_repo,
125
+ "branch": branch,
126
+ "framework": framework,
127
+ }
128
+ if region:
129
+ payload["region"] = region
130
+ if size_slug:
131
+ payload["size_slug"] = size_slug
132
+
133
+ try:
134
+ # Use httpx to stream the SSE response
135
+ import httpx
136
+
137
+ headers = {
138
+ "Authorization": f"Bearer {self._client.token}",
139
+ "Accept": "text/event-stream",
140
+ "Content-Type": "application/json",
141
+ }
142
+
143
+ url = f"{self._client.api_url}/deployments/stream"
144
+
145
+ with httpx.stream(
146
+ "POST",
147
+ url,
148
+ json=payload,
149
+ headers=headers,
150
+ timeout=600.0, # 10 minute timeout for deployments
151
+ ) as response:
152
+ if response.status_code not in [200, 201, 202]:
153
+ error_text = response.text
154
+ raise XenfraAPIError(
155
+ status_code=response.status_code,
156
+ detail=f"Deployment failed: {error_text}"
157
+ )
158
+
159
+ # Parse SSE events
160
+ for line in response.iter_lines():
161
+ line = line.strip()
162
+ if not line:
163
+ continue
164
+
165
+ # SSE format: "event: eventname" or "data: eventdata"
166
+ if line.startswith("event:"):
167
+ current_event = line[6:].strip()
168
+ elif line.startswith("data:"):
169
+ data = line[5:].strip()
170
+ try:
171
+ # Try to parse as JSON
172
+ data_parsed = json.loads(data)
173
+ yield {"event": current_event if 'current_event' in locals() else "message", "data": data_parsed}
174
+ except json.JSONDecodeError:
175
+ # If not JSON, yield as plain text
176
+ yield {"event": current_event if 'current_event' in locals() else "message", "data": data}
177
+
178
+ # Reset current_event
179
+ if 'current_event' in locals():
180
+ del current_event
181
+
182
+ except httpx.HTTPError as e:
183
+ raise XenfraError(f"HTTP error during streaming deployment: {e}")
184
+ except Exception as e:
185
+ raise XenfraError(f"Failed to create streaming deployment: {e}")
@@ -1,89 +0,0 @@
1
- import logging
2
-
3
- # Import Deployment model when it's defined in models.py
4
- # from ..models import Deployment
5
- from ..exceptions import XenfraAPIError, XenfraError # Add XenfraError
6
- from ..utils import safe_get_json_field, safe_json_parse
7
- from .base import BaseManager
8
-
9
- logger = logging.getLogger(__name__)
10
-
11
-
12
- class DeploymentsManager(BaseManager):
13
- def create(self, project_name: str, git_repo: str, branch: str, framework: str) -> dict:
14
- """Creates a new deployment."""
15
- try:
16
- payload = {
17
- "project_name": project_name,
18
- "git_repo": git_repo,
19
- "branch": branch,
20
- "framework": framework,
21
- }
22
- response = self._client._request("POST", "/deployments", json=payload)
23
- # Safe JSON parsing
24
- return safe_json_parse(response)
25
- except XenfraAPIError:
26
- raise
27
- except Exception as e:
28
- raise XenfraError(f"Failed to create deployment: {e}")
29
-
30
- def get_status(self, deployment_id: str) -> dict:
31
- """Get status for a specific deployment.
32
-
33
- Args:
34
- deployment_id: The unique identifier for the deployment.
35
-
36
- Returns:
37
- dict: Deployment status information including state, progress, etc.
38
-
39
- Raises:
40
- XenfraAPIError: If the API returns an error (e.g., 404 not found).
41
- XenfraError: If there's a network or parsing error.
42
- """
43
- try:
44
- response = self._client._request("GET", f"/deployments/{deployment_id}/status")
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)
50
- except XenfraAPIError:
51
- raise # Re-raise API errors
52
- except Exception as e:
53
- raise XenfraError(f"Failed to get status for deployment {deployment_id}: {e}")
54
-
55
- def get_logs(self, deployment_id: str) -> str:
56
- """Get logs for a specific deployment.
57
-
58
- Args:
59
- deployment_id: The unique identifier for the deployment.
60
-
61
- Returns:
62
- str: The deployment logs as plain text.
63
-
64
- Raises:
65
- XenfraAPIError: If the API returns an error (e.g., 404 not found).
66
- XenfraError: If there's a network or parsing error.
67
- """
68
- try:
69
- response = self._client._request("GET", f"/deployments/{deployment_id}/logs")
70
- logger.debug(
71
- f"DeploymentsManager.get_logs({deployment_id}) response: {response.status_code}"
72
- )
73
-
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", "")
80
-
81
- if not logs:
82
- logger.warning(f"No logs found for deployment {deployment_id}")
83
-
84
- return logs
85
-
86
- except XenfraAPIError:
87
- raise # Re-raise API errors
88
- except Exception as e:
89
- raise XenfraError(f"Failed to get logs for deployment {deployment_id}: {e}")
File without changes