xenfra-sdk 0.2.2__py3-none-any.whl → 0.2.3__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.
@@ -1,101 +1,101 @@
1
- """
2
- Files resource manager for delta uploads.
3
-
4
- Provides methods to check file cache status and upload files to the server.
5
- """
6
-
7
- from typing import Dict, List
8
-
9
-
10
- class FilesManager:
11
- """Manager for file upload operations."""
12
-
13
- def __init__(self, client):
14
- """
15
- Initialize the FilesManager.
16
-
17
- Args:
18
- client: The XenfraClient instance.
19
- """
20
- self._client = client
21
-
22
- def check(self, files: List[Dict]) -> Dict:
23
- """
24
- Check which files are missing from server cache.
25
-
26
- Args:
27
- files: List of file info dicts with keys: path, sha, size
28
-
29
- Returns:
30
- Dict with keys:
31
- - missing: List of SHA hashes that need to be uploaded
32
- - cached: Number of files already cached on server
33
- """
34
- payload = {
35
- "files": [
36
- {"path": f["path"], "sha": f["sha"], "size": f["size"]}
37
- for f in files
38
- ]
39
- }
40
-
41
- response = self._client._request("POST", "/files/check", json=payload)
42
- return response.json()
43
-
44
- def upload(self, content: bytes, sha: str, path: str) -> Dict:
45
- """
46
- Upload a single file to the server.
47
-
48
- Args:
49
- content: Raw file content as bytes
50
- sha: SHA256 hash of the content
51
- path: Relative file path
52
-
53
- Returns:
54
- Dict with keys: sha, size, stored
55
- """
56
- import httpx
57
-
58
- headers = {
59
- "Authorization": f"Bearer {self._client._token}",
60
- "Content-Type": "application/octet-stream",
61
- "X-Xenfra-Sha": sha,
62
- "X-Xenfra-Path": path,
63
- }
64
-
65
- response = httpx.post(
66
- f"{self._client.api_url}/files/upload",
67
- content=content,
68
- headers=headers,
69
- timeout=120.0, # 2 minutes for large files
70
- )
71
- response.raise_for_status()
72
- return response.json()
73
-
74
- def upload_files(self, files: List[Dict], missing_shas: List[str], progress_callback=None) -> int:
75
- """
76
- Upload multiple files that are missing from the server.
77
-
78
- Args:
79
- files: List of file info dicts with keys: path, sha, size, abs_path
80
- missing_shas: List of SHA hashes that need to be uploaded
81
- progress_callback: Optional callback(uploaded_count, total_count)
82
-
83
- Returns:
84
- Number of files uploaded
85
- """
86
- missing_set = set(missing_shas)
87
- files_to_upload = [f for f in files if f["sha"] in missing_set]
88
- total = len(files_to_upload)
89
- uploaded = 0
90
-
91
- for file_info in files_to_upload:
92
- with open(file_info["abs_path"], "rb") as f:
93
- content = f.read()
94
-
95
- self.upload(content, file_info["sha"], file_info["path"])
96
- uploaded += 1
97
-
98
- if progress_callback:
99
- progress_callback(uploaded, total)
100
-
101
- return uploaded
1
+ """
2
+ Files resource manager for delta uploads.
3
+
4
+ Provides methods to check file cache status and upload files to the server.
5
+ """
6
+
7
+ from typing import Dict, List
8
+
9
+
10
+ class FilesManager:
11
+ """Manager for file upload operations."""
12
+
13
+ def __init__(self, client):
14
+ """
15
+ Initialize the FilesManager.
16
+
17
+ Args:
18
+ client: The XenfraClient instance.
19
+ """
20
+ self._client = client
21
+
22
+ def check(self, files: List[Dict]) -> Dict:
23
+ """
24
+ Check which files are missing from server cache.
25
+
26
+ Args:
27
+ files: List of file info dicts with keys: path, sha, size
28
+
29
+ Returns:
30
+ Dict with keys:
31
+ - missing: List of SHA hashes that need to be uploaded
32
+ - cached: Number of files already cached on server
33
+ """
34
+ payload = {
35
+ "files": [
36
+ {"path": f["path"], "sha": f["sha"], "size": f["size"]}
37
+ for f in files
38
+ ]
39
+ }
40
+
41
+ response = self._client._request("POST", "/files/check", json=payload)
42
+ return response.json()
43
+
44
+ def upload(self, content: bytes, sha: str, path: str) -> Dict:
45
+ """
46
+ Upload a single file to the server.
47
+
48
+ Args:
49
+ content: Raw file content as bytes
50
+ sha: SHA256 hash of the content
51
+ path: Relative file path
52
+
53
+ Returns:
54
+ Dict with keys: sha, size, stored
55
+ """
56
+ import httpx
57
+
58
+ headers = {
59
+ "Authorization": f"Bearer {self._client._token}",
60
+ "Content-Type": "application/octet-stream",
61
+ "X-Xenfra-Sha": sha,
62
+ "X-Xenfra-Path": path,
63
+ }
64
+
65
+ response = httpx.post(
66
+ f"{self._client.api_url}/files/upload",
67
+ content=content,
68
+ headers=headers,
69
+ timeout=120.0, # 2 minutes for large files
70
+ )
71
+ response.raise_for_status()
72
+ return response.json()
73
+
74
+ def upload_files(self, files: List[Dict], missing_shas: List[str], progress_callback=None) -> int:
75
+ """
76
+ Upload multiple files that are missing from the server.
77
+
78
+ Args:
79
+ files: List of file info dicts with keys: path, sha, size, abs_path
80
+ missing_shas: List of SHA hashes that need to be uploaded
81
+ progress_callback: Optional callback(uploaded_count, total_count)
82
+
83
+ Returns:
84
+ Number of files uploaded
85
+ """
86
+ missing_set = set(missing_shas)
87
+ files_to_upload = [f for f in files if f["sha"] in missing_set]
88
+ total = len(files_to_upload)
89
+ uploaded = 0
90
+
91
+ for file_info in files_to_upload:
92
+ with open(file_info["abs_path"], "rb") as f:
93
+ content = f.read()
94
+
95
+ self.upload(content, file_info["sha"], file_info["path"])
96
+ uploaded += 1
97
+
98
+ if progress_callback:
99
+ progress_callback(uploaded, total)
100
+
101
+ return uploaded
@@ -1,95 +1,102 @@
1
- """
2
- Intelligence resource manager for Xenfra SDK.
3
- Provides AI-powered deployment diagnosis and codebase analysis.
4
- """
5
-
6
- import logging
7
-
8
- from ..exceptions import XenfraAPIError, XenfraError
9
- from ..models import CodebaseAnalysisResponse, DiagnosisResponse
10
- from ..utils import safe_json_parse
11
- from .base import BaseManager
12
-
13
- logger = logging.getLogger(__name__)
14
-
15
-
16
- class IntelligenceManager(BaseManager):
17
- """
18
- Manager for AI-powered intelligence operations.
19
-
20
- Provides:
21
- - Deployment failure diagnosis (Zen Nod)
22
- - Codebase analysis for zero-config init (Zen Init)
23
- """
24
-
25
- def diagnose(
26
- self, logs: str, package_manager: str | None = None, dependency_file: str | None = None
27
- ) -> DiagnosisResponse:
28
- """
29
- Diagnose deployment failure from logs using AI.
30
-
31
- Args:
32
- logs: The deployment logs to analyze
33
- package_manager: Optional package manager context (uv, pip, poetry, npm, etc.)
34
- If provided, AI will target this manager's dependency file
35
- dependency_file: Optional dependency file context (pyproject.toml, requirements.txt, etc.)
36
- If provided, AI will suggest patches for this file
37
-
38
- Returns:
39
- DiagnosisResponse with diagnosis, suggestion, and optional patch
40
-
41
- Raises:
42
- XenfraAPIError: If the API request fails
43
- XenfraError: If parsing the response fails
44
- """
45
- try:
46
- # Build request payload
47
- payload = {"logs": logs}
48
- if package_manager:
49
- payload["package_manager"] = package_manager
50
- if dependency_file:
51
- payload["dependency_file"] = dependency_file
52
-
53
- response = self._client._request("POST", "/intelligence/diagnose", json=payload)
54
-
55
- logger.debug(f"IntelligenceManager.diagnose response: status={response.status_code}")
56
-
57
- # Safe JSON parsing
58
- data = safe_json_parse(response)
59
- return DiagnosisResponse(**data)
60
- except XenfraAPIError:
61
- raise
62
- except Exception as e:
63
- raise XenfraError(f"Failed to diagnose logs: {e}")
64
-
65
- def analyze_codebase(self, code_snippets: dict[str, str]) -> CodebaseAnalysisResponse:
66
- """
67
- Analyze codebase to detect framework, dependencies, and deployment config.
68
-
69
- Args:
70
- code_snippets: Dictionary of filename -> content
71
- e.g., {"main.py": "...", "requirements.txt": "..."}
72
-
73
- Returns:
74
- CodebaseAnalysisResponse with detected configuration
75
-
76
- Raises:
77
- XenfraAPIError: If the API request fails
78
- XenfraError: If parsing the response fails
79
- """
80
- try:
81
- response = self._client._request(
82
- "POST", "/intelligence/analyze-codebase", json={"code_snippets": code_snippets}
83
- )
84
-
85
- logger.debug(
86
- f"IntelligenceManager.analyze_codebase response: status={response.status_code}"
87
- )
88
-
89
- # Safe JSON parsing
90
- data = safe_json_parse(response)
91
- return CodebaseAnalysisResponse(**data)
92
- except XenfraAPIError:
93
- raise
94
- except Exception as e:
95
- raise XenfraError(f"Failed to analyze codebase: {e}")
1
+ """
2
+ Intelligence resource manager for Xenfra SDK.
3
+ Provides AI-powered deployment diagnosis and codebase analysis.
4
+ """
5
+
6
+ import logging
7
+
8
+ from ..exceptions import XenfraAPIError, XenfraError
9
+ from ..models import CodebaseAnalysisResponse, DiagnosisResponse
10
+ from ..utils import safe_json_parse
11
+ from .base import BaseManager
12
+
13
+ logger = logging.getLogger(__name__)
14
+
15
+
16
+ class IntelligenceManager(BaseManager):
17
+ """
18
+ Manager for AI-powered intelligence operations.
19
+
20
+ Provides:
21
+ - Deployment failure diagnosis (Zen Nod)
22
+ - Codebase analysis for zero-config init (Zen Init)
23
+ """
24
+
25
+ def diagnose(
26
+ self,
27
+ logs: str,
28
+ package_manager: str | None = None,
29
+ dependency_file: str | None = None,
30
+ services: list | None = None
31
+ ) -> DiagnosisResponse:
32
+ """
33
+ Diagnose deployment failure from logs using AI.
34
+
35
+ Args:
36
+ logs: The deployment logs to analyze
37
+ package_manager: Optional package manager context (uv, pip, poetry, npm, etc.)
38
+ If provided, AI will target this manager's dependency file
39
+ dependency_file: Optional dependency file context (pyproject.toml, requirements.txt, etc.)
40
+ If provided, AI will suggest patches for this file
41
+ services: Optional list of service definitions for project structure context (Zen Mode)
42
+
43
+ Returns:
44
+ DiagnosisResponse with diagnosis, suggestion, and optional patch
45
+
46
+ Raises:
47
+ XenfraAPIError: If the API request fails
48
+ XenfraError: If parsing the response fails
49
+ """
50
+ try:
51
+ # Build request payload
52
+ payload = {"logs": logs}
53
+ if package_manager:
54
+ payload["package_manager"] = package_manager
55
+ if dependency_file:
56
+ payload["dependency_file"] = dependency_file
57
+ if services:
58
+ payload["services"] = services
59
+
60
+ response = self._client._request("POST", "/intelligence/diagnose", json=payload)
61
+
62
+ logger.debug(f"IntelligenceManager.diagnose response: status={response.status_code}")
63
+
64
+ # Safe JSON parsing
65
+ data = safe_json_parse(response)
66
+ return DiagnosisResponse(**data)
67
+ except XenfraAPIError:
68
+ raise
69
+ except Exception as e:
70
+ raise XenfraError(f"Failed to diagnose logs: {e}")
71
+
72
+ def analyze_codebase(self, code_snippets: dict[str, str]) -> CodebaseAnalysisResponse:
73
+ """
74
+ Analyze codebase to detect framework, dependencies, and deployment config.
75
+
76
+ Args:
77
+ code_snippets: Dictionary of filename -> content
78
+ e.g., {"main.py": "...", "requirements.txt": "..."}
79
+
80
+ Returns:
81
+ CodebaseAnalysisResponse with detected configuration
82
+
83
+ Raises:
84
+ XenfraAPIError: If the API request fails
85
+ XenfraError: If parsing the response fails
86
+ """
87
+ try:
88
+ response = self._client._request(
89
+ "POST", "/intelligence/analyze-codebase", json={"code_snippets": code_snippets}
90
+ )
91
+
92
+ logger.debug(
93
+ f"IntelligenceManager.analyze_codebase response: status={response.status_code}"
94
+ )
95
+
96
+ # Safe JSON parsing
97
+ data = safe_json_parse(response)
98
+ return CodebaseAnalysisResponse(**data)
99
+ except XenfraAPIError:
100
+ raise
101
+ except Exception as e:
102
+ raise XenfraError(f"Failed to analyze codebase: {e}")
xenfra_sdk/security.py CHANGED
@@ -1,41 +1,41 @@
1
- # src/xenfra_sdk/security.py
2
- """
3
- Security utilities for the Xenfra SDK.
4
- Provides token encryption/decryption for storing OAuth credentials.
5
- """
6
-
7
- import os
8
- from typing import Optional
9
-
10
- from cryptography.fernet import Fernet
11
-
12
- # --- Configuration from Environment ---
13
- # These should be set in the service's environment
14
- ENCRYPTION_KEY = os.getenv("ENCRYPTION_KEY", "")
15
-
16
-
17
- def _get_fernet() -> Optional[Fernet]:
18
- """Get Fernet instance for encryption/decryption."""
19
- if not ENCRYPTION_KEY:
20
- return None
21
- try:
22
- return Fernet(ENCRYPTION_KEY.encode())
23
- except Exception:
24
- return None
25
-
26
-
27
- # --- Token Encryption ---
28
- def encrypt_token(token: str) -> str:
29
- """Encrypts a token using Fernet symmetric encryption."""
30
- fernet = _get_fernet()
31
- if fernet is None:
32
- raise ValueError("ENCRYPTION_KEY environment variable is not set or invalid")
33
- return fernet.encrypt(token.encode()).decode()
34
-
35
-
36
- def decrypt_token(encrypted_token: str) -> str:
37
- """Decrypts a token."""
38
- fernet = _get_fernet()
39
- if fernet is None:
40
- raise ValueError("ENCRYPTION_KEY environment variable is not set or invalid")
41
- return fernet.decrypt(encrypted_token.encode()).decode()
1
+ # src/xenfra_sdk/security.py
2
+ """
3
+ Security utilities for the Xenfra SDK.
4
+ Provides token encryption/decryption for storing OAuth credentials.
5
+ """
6
+
7
+ import os
8
+ from typing import Optional
9
+
10
+ from cryptography.fernet import Fernet
11
+
12
+ # --- Configuration from Environment ---
13
+ # These should be set in the service's environment
14
+ ENCRYPTION_KEY = os.getenv("ENCRYPTION_KEY", "")
15
+
16
+
17
+ def _get_fernet() -> Optional[Fernet]:
18
+ """Get Fernet instance for encryption/decryption."""
19
+ if not ENCRYPTION_KEY:
20
+ return None
21
+ try:
22
+ return Fernet(ENCRYPTION_KEY.encode())
23
+ except Exception:
24
+ return None
25
+
26
+
27
+ # --- Token Encryption ---
28
+ def encrypt_token(token: str) -> str:
29
+ """Encrypts a token using Fernet symmetric encryption."""
30
+ fernet = _get_fernet()
31
+ if fernet is None:
32
+ raise ValueError("ENCRYPTION_KEY environment variable is not set or invalid")
33
+ return fernet.encrypt(token.encode()).decode()
34
+
35
+
36
+ def decrypt_token(encrypted_token: str) -> str:
37
+ """Decrypts a token."""
38
+ fernet = _get_fernet()
39
+ if fernet is None:
40
+ raise ValueError("ENCRYPTION_KEY environment variable is not set or invalid")
41
+ return fernet.decrypt(encrypted_token.encode()).decode()