xenfra 0.2.5__py3-none-any.whl → 0.2.6__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/commands/auth.py CHANGED
@@ -15,9 +15,9 @@ import keyring
15
15
  from rich.console import Console
16
16
  from tenacity import (
17
17
  retry,
18
+ retry_if_exception_type,
18
19
  stop_after_attempt,
19
20
  wait_exponential,
20
- retry_if_exception_type,
21
21
  )
22
22
 
23
23
  from ..utils.auth import (
@@ -38,15 +38,19 @@ console = Console()
38
38
  HTTP_TIMEOUT = 30.0
39
39
 
40
40
 
41
+ @click.group()
42
+ def auth():
43
+ """Authentication commands (login, logout, whoami)."""
44
+ pass
45
+
46
+
41
47
  @retry(
42
48
  stop=stop_after_attempt(3),
43
49
  wait=wait_exponential(multiplier=1, min=2, max=10),
44
50
  retry=retry_if_exception_type((httpx.TimeoutException, httpx.NetworkError)),
45
51
  reraise=True,
46
52
  )
47
- def _exchange_code_for_tokens_with_retry(
48
- code: str, code_verifier: str, redirect_uri: str
49
- ) -> dict:
53
+ def _exchange_code_for_tokens_with_retry(code: str, code_verifier: str, redirect_uri: str) -> dict:
50
54
  """
51
55
  Exchange authorization code for tokens with retry logic.
52
56
 
@@ -141,9 +145,7 @@ def login():
141
145
  try:
142
146
  webbrowser.open(auth_url)
143
147
  except Exception as e:
144
- console.print(
145
- f"[yellow]Warning: Could not open browser automatically: {e}[/yellow]"
146
- )
148
+ console.print(f"[yellow]Warning: Could not open browser automatically: {e}[/yellow]")
147
149
  console.print(f"[dim]Please open the URL manually: {auth_url}[/dim]")
148
150
 
149
151
  # 5. Run local server to capture redirect
@@ -197,11 +199,11 @@ def login():
197
199
  try:
198
200
  keyring.set_password(SERVICE_ID, "access_token", access_token)
199
201
  keyring.set_password(SERVICE_ID, "refresh_token", refresh_token)
200
- console.print("[bold green]Login successful! Tokens saved securely.[/bold green]")
201
- except keyring.errors.KeyringError as e:
202
202
  console.print(
203
- f"[bold red]Failed to save tokens to keyring: {e}[/bold red]"
203
+ "[bold green]Login successful! Tokens saved securely.[/bold green]"
204
204
  )
205
+ except keyring.errors.KeyringError as e:
206
+ console.print(f"[bold red]Failed to save tokens to keyring: {e}[/bold red]")
205
207
  console.print("[yellow]Tokens were received but not saved.[/yellow]")
206
208
  else:
207
209
  console.print("[bold red]Login failed: No tokens received.[/bold red]")
xenfra/utils/auth.py CHANGED
@@ -11,9 +11,9 @@ import keyring
11
11
  from rich.console import Console
12
12
  from tenacity import (
13
13
  retry,
14
+ retry_if_exception_type,
14
15
  stop_after_attempt,
15
16
  wait_exponential,
16
- retry_if_exception_type,
17
17
  )
18
18
 
19
19
  from .security import validate_and_get_api_url
xenfra/utils/codebase.py CHANGED
@@ -80,6 +80,7 @@ def scan_codebase(max_files: int = 10, max_size: int = 50000) -> dict[str, str]:
80
80
  except (IOError, OSError, PermissionError, UnicodeDecodeError) as e:
81
81
  # Skip files that can't be read (log but don't crash)
82
82
  import logging
83
+
83
84
  logger = logging.getLogger(__name__)
84
85
  logger.debug(f"Skipping file {filename}: {type(e).__name__}")
85
86
  continue
@@ -112,6 +113,7 @@ def scan_codebase(max_files: int = 10, max_size: int = 50000) -> dict[str, str]:
112
113
  except (IOError, OSError, PermissionError, UnicodeDecodeError) as e:
113
114
  # Skip files that can't be read
114
115
  import logging
116
+
115
117
  logger = logging.getLogger(__name__)
116
118
  logger.debug(f"Skipping file {filepath}: {type(e).__name__}")
117
119
  continue
xenfra/utils/config.py CHANGED
@@ -7,8 +7,8 @@ import shutil
7
7
  from datetime import datetime
8
8
  from pathlib import Path
9
9
 
10
- import yaml
11
10
  import click
11
+ import yaml
12
12
  from rich.console import Console
13
13
  from rich.prompt import Confirm, IntPrompt, Prompt
14
14
  from xenfra_sdk import CodebaseAnalysisResponse
@@ -159,7 +159,9 @@ def apply_patch(patch: dict, target_file: str = None, create_backup_file: bool =
159
159
 
160
160
  operation = patch.get("operation")
161
161
  if operation not in ["add", "replace", "remove"]:
162
- raise ValueError(f"Invalid patch operation: {operation}. Must be 'add', 'replace', or 'remove'")
162
+ raise ValueError(
163
+ f"Invalid patch operation: {operation}. Must be 'add', 'replace', or 'remove'"
164
+ )
163
165
  """
164
166
  Apply a JSON patch to a configuration file with automatic backup.
165
167
 
@@ -228,6 +230,7 @@ def apply_patch(patch: dict, target_file: str = None, create_backup_file: bool =
228
230
  # For JSON files
229
231
  elif file_to_patch.endswith(".json"):
230
232
  import json
233
+
231
234
  with open(file_to_patch, "r") as f:
232
235
  config_data = json.load(f)
233
236
 
@@ -278,6 +281,9 @@ def apply_patch(patch: dict, target_file: str = None, create_backup_file: bool =
278
281
  with open(file_to_patch, "w") as f:
279
282
  f.write(str(value))
280
283
  else:
284
+ # Design decision: Only support auto-patching for common dependency files
285
+ # Other file types should be manually edited to avoid data loss
286
+ # See docs/future-enhancements.md #4 for potential extensions
281
287
  raise NotImplementedError(f"Patching not supported for file type: {file_to_patch}")
282
288
 
283
289
  return backup_path
@@ -309,6 +315,7 @@ def manual_prompt_for_config(filename: str = "xenfra.yaml") -> str:
309
315
  port = IntPrompt.ask("Application port", default=8000)
310
316
  # Validate port
311
317
  from .validation import validate_port
318
+
312
319
  is_valid, error_msg = validate_port(port)
313
320
  if not is_valid:
314
321
  console.print(f"[bold red]Invalid port: {error_msg}[/bold red]")
xenfra/utils/security.py CHANGED
@@ -220,8 +220,8 @@ def create_secure_client(url: str, token: str = None) -> httpx.Client:
220
220
  ssl_context.verify_mode = ssl.CERT_REQUIRED
221
221
 
222
222
  # Note: Full certificate pinning implementation would require custom verification
223
- # For now, we use strict certificate validation
224
- # TODO: Implement actual fingerprint verification if needed
223
+ # For now, we use strict certificate validation with system CA bundle
224
+ # Future enhancement: Certificate fingerprint pinning (see docs/future-enhancements.md #3)
225
225
 
226
226
  return httpx.Client(
227
227
  base_url=url,
@@ -109,7 +109,10 @@ def validate_project_name(project_name: str) -> tuple[bool, Optional[str]]:
109
109
 
110
110
  # Alphanumeric, hyphens, underscores, dots
111
111
  if not re.match(r"^[a-zA-Z0-9._-]+$", project_name):
112
- return False, "Project name can only contain alphanumeric characters, dots, hyphens, and underscores"
112
+ return (
113
+ False,
114
+ "Project name can only contain alphanumeric characters, dots, hyphens, and underscores",
115
+ )
113
116
 
114
117
  # Reserved names
115
118
  reserved_names = ["admin", "api", "www", "root", "system", "xenfra"]
@@ -135,7 +138,10 @@ def validate_branch_name(branch: str) -> tuple[bool, Optional[str]]:
135
138
 
136
139
  # Git branch name rules: no spaces, no special chars except /, -, _
137
140
  if not re.match(r"^[a-zA-Z0-9/._-]+$", branch):
138
- return False, "Branch name can only contain alphanumeric characters, slashes, dots, hyphens, and underscores"
141
+ return (
142
+ False,
143
+ "Branch name can only contain alphanumeric characters, slashes, dots, hyphens, and underscores",
144
+ )
139
145
 
140
146
  # Cannot start with . or end with .lock
141
147
  if branch.startswith(".") or branch.endswith(".lock"):
@@ -226,4 +232,3 @@ def validate_codebase_scan_limits(max_files: int, max_size: int) -> tuple[bool,
226
232
  return False, "max_size must be between 1KB and 10MB"
227
233
 
228
234
  return True, None
229
-
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: xenfra
3
- Version: 0.2.5
3
+ Version: 0.2.6
4
4
  Summary: A 'Zen Mode' infrastructure engine for Python developers.
5
5
  Author: xenfra-cloud
6
6
  Author-email: xenfra-cloud <xenfracloud@gmail.com>
@@ -0,0 +1,18 @@
1
+ xenfra/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ xenfra/commands/__init__.py,sha256=bdugTOErbWUhDvnwFl17KkGPPV7gtmDkSOzhF_NEHX0,40
3
+ xenfra/commands/auth.py,sha256=W7ncG9ombHRKpjxKEZov5Fj3Tlr8Cx0mbze5nrPedlw,10175
4
+ xenfra/commands/deployments.py,sha256=-185BevHVrUT-LAU2k_uZNpKJPCcwpCDEHOFPfD0Wmw,19348
5
+ xenfra/commands/intelligence.py,sha256=w8GxwGu63KQ5fwhPpTNTDeW1Xg5g3aFzzIBuP_CeRQo,13541
6
+ xenfra/commands/projects.py,sha256=O2tG--iDWN5oCcHOv1jp88kl9bAK61oGRCLJ60M0b7E,6492
7
+ xenfra/commands/security_cmd.py,sha256=MJxbjQksKrtRn21FSAhTY3ESn_S_tUCGfdNRWL7kNsc,7094
8
+ xenfra/main.py,sha256=bv6EslYtRMXShmHfcSuzecNjJWbCvPCi2LKI4kwL_oQ,1790
9
+ xenfra/utils/__init__.py,sha256=57o8j7Tibrhyid84zTFLHjFmRP5sCnNbtLEfpRqIpMk,42
10
+ xenfra/utils/auth.py,sha256=oDxDiIWC9851fu_gL-7TVJ60uJT3sZ_DvMIy69SUAEM,8308
11
+ xenfra/utils/codebase.py,sha256=vx-1pMpnefPJ_Xy1UoH7wgHJ2c5ZAsVX1g1IXAfkI28,4018
12
+ xenfra/utils/config.py,sha256=6A6WAggaH2Rco4RJydALxcKteOzXLCKDV0ZxjHhAJHk,11584
13
+ xenfra/utils/security.py,sha256=IR_Hzgfc4KeT-qr2LVBxSvBTO4AP4MCkIlU47_yKN_0,11947
14
+ xenfra/utils/validation.py,sha256=6mGC5CqAbx-CBp06omWLBpKjnEWXsEzlYWq71wjDeX8,6678
15
+ xenfra-0.2.6.dist-info/WHEEL,sha256=ZyFSCYkV2BrxH6-HRVRg3R9Fo7MALzer9KiPYqNxSbo,79
16
+ xenfra-0.2.6.dist-info/entry_points.txt,sha256=a_2cGhYK__X6eW05Ba8uB6RIM_61c2sHtXsPY8N0mic,45
17
+ xenfra-0.2.6.dist-info/METADATA,sha256=s0oMORzsqWGrFAFqp5_OOwJwRBIi8drY-BUEF6EujTk,3751
18
+ xenfra-0.2.6.dist-info/RECORD,,
@@ -1,18 +0,0 @@
1
- xenfra/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- xenfra/commands/__init__.py,sha256=bdugTOErbWUhDvnwFl17KkGPPV7gtmDkSOzhF_NEHX0,40
3
- xenfra/commands/auth.py,sha256=tG49BfzzivnE9ZN3QmVCZywOEhW6Ab1wgVxnTqTRnnQ,10114
4
- xenfra/commands/deployments.py,sha256=-185BevHVrUT-LAU2k_uZNpKJPCcwpCDEHOFPfD0Wmw,19348
5
- xenfra/commands/intelligence.py,sha256=w8GxwGu63KQ5fwhPpTNTDeW1Xg5g3aFzzIBuP_CeRQo,13541
6
- xenfra/commands/projects.py,sha256=O2tG--iDWN5oCcHOv1jp88kl9bAK61oGRCLJ60M0b7E,6492
7
- xenfra/commands/security_cmd.py,sha256=MJxbjQksKrtRn21FSAhTY3ESn_S_tUCGfdNRWL7kNsc,7094
8
- xenfra/main.py,sha256=bv6EslYtRMXShmHfcSuzecNjJWbCvPCi2LKI4kwL_oQ,1790
9
- xenfra/utils/__init__.py,sha256=57o8j7Tibrhyid84zTFLHjFmRP5sCnNbtLEfpRqIpMk,42
10
- xenfra/utils/auth.py,sha256=UIWx8m7I9Qm4UiKP8g7hCiVAXgkKo1JfSl4twChXkSA,8308
11
- xenfra/utils/codebase.py,sha256=Gw3e6N-8WfbBeDEMrTD9j3CGPFFJFgorwHXAmZ93vbw,4016
12
- xenfra/utils/config.py,sha256=EFmk6DVOIaskon6yWlYSWyveDYuG_zC587P1akllKjc,11336
13
- xenfra/utils/security.py,sha256=5_rE3hYMTfUzZ05XEoJwMQfQFZGElpw7wc_WbcpzW5M,11894
14
- xenfra/utils/validation.py,sha256=AslcMtdXFjXVdmCwjKWHhnlCW6g1QyTTj3wP7s1_bZU,6605
15
- xenfra-0.2.5.dist-info/WHEEL,sha256=ZyFSCYkV2BrxH6-HRVRg3R9Fo7MALzer9KiPYqNxSbo,79
16
- xenfra-0.2.5.dist-info/entry_points.txt,sha256=a_2cGhYK__X6eW05Ba8uB6RIM_61c2sHtXsPY8N0mic,45
17
- xenfra-0.2.5.dist-info/METADATA,sha256=C9Ns60_5AA99ATsa_-ljs1M9gNT2FcsfGyHgt05mSBU,3751
18
- xenfra-0.2.5.dist-info/RECORD,,
File without changes