mcli-framework 7.0.0__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.

Potentially problematic release.


This version of mcli-framework might be problematic. Click here for more details.

Files changed (186) hide show
  1. mcli/app/chat_cmd.py +42 -0
  2. mcli/app/commands_cmd.py +226 -0
  3. mcli/app/completion_cmd.py +216 -0
  4. mcli/app/completion_helpers.py +288 -0
  5. mcli/app/cron_test_cmd.py +697 -0
  6. mcli/app/logs_cmd.py +419 -0
  7. mcli/app/main.py +492 -0
  8. mcli/app/model/model.py +1060 -0
  9. mcli/app/model_cmd.py +227 -0
  10. mcli/app/redis_cmd.py +269 -0
  11. mcli/app/video/video.py +1114 -0
  12. mcli/app/visual_cmd.py +303 -0
  13. mcli/chat/chat.py +2409 -0
  14. mcli/chat/command_rag.py +514 -0
  15. mcli/chat/enhanced_chat.py +652 -0
  16. mcli/chat/system_controller.py +1010 -0
  17. mcli/chat/system_integration.py +1016 -0
  18. mcli/cli.py +25 -0
  19. mcli/config.toml +20 -0
  20. mcli/lib/api/api.py +586 -0
  21. mcli/lib/api/daemon_client.py +203 -0
  22. mcli/lib/api/daemon_client_local.py +44 -0
  23. mcli/lib/api/daemon_decorator.py +217 -0
  24. mcli/lib/api/mcli_decorators.py +1032 -0
  25. mcli/lib/auth/auth.py +85 -0
  26. mcli/lib/auth/aws_manager.py +85 -0
  27. mcli/lib/auth/azure_manager.py +91 -0
  28. mcli/lib/auth/credential_manager.py +192 -0
  29. mcli/lib/auth/gcp_manager.py +93 -0
  30. mcli/lib/auth/key_manager.py +117 -0
  31. mcli/lib/auth/mcli_manager.py +93 -0
  32. mcli/lib/auth/token_manager.py +75 -0
  33. mcli/lib/auth/token_util.py +1011 -0
  34. mcli/lib/config/config.py +47 -0
  35. mcli/lib/discovery/__init__.py +1 -0
  36. mcli/lib/discovery/command_discovery.py +274 -0
  37. mcli/lib/erd/erd.py +1345 -0
  38. mcli/lib/erd/generate_graph.py +453 -0
  39. mcli/lib/files/files.py +76 -0
  40. mcli/lib/fs/fs.py +109 -0
  41. mcli/lib/lib.py +29 -0
  42. mcli/lib/logger/logger.py +611 -0
  43. mcli/lib/performance/optimizer.py +409 -0
  44. mcli/lib/performance/rust_bridge.py +502 -0
  45. mcli/lib/performance/uvloop_config.py +154 -0
  46. mcli/lib/pickles/pickles.py +50 -0
  47. mcli/lib/search/cached_vectorizer.py +479 -0
  48. mcli/lib/services/data_pipeline.py +460 -0
  49. mcli/lib/services/lsh_client.py +441 -0
  50. mcli/lib/services/redis_service.py +387 -0
  51. mcli/lib/shell/shell.py +137 -0
  52. mcli/lib/toml/toml.py +33 -0
  53. mcli/lib/ui/styling.py +47 -0
  54. mcli/lib/ui/visual_effects.py +634 -0
  55. mcli/lib/watcher/watcher.py +185 -0
  56. mcli/ml/api/app.py +215 -0
  57. mcli/ml/api/middleware.py +224 -0
  58. mcli/ml/api/routers/admin_router.py +12 -0
  59. mcli/ml/api/routers/auth_router.py +244 -0
  60. mcli/ml/api/routers/backtest_router.py +12 -0
  61. mcli/ml/api/routers/data_router.py +12 -0
  62. mcli/ml/api/routers/model_router.py +302 -0
  63. mcli/ml/api/routers/monitoring_router.py +12 -0
  64. mcli/ml/api/routers/portfolio_router.py +12 -0
  65. mcli/ml/api/routers/prediction_router.py +267 -0
  66. mcli/ml/api/routers/trade_router.py +12 -0
  67. mcli/ml/api/routers/websocket_router.py +76 -0
  68. mcli/ml/api/schemas.py +64 -0
  69. mcli/ml/auth/auth_manager.py +425 -0
  70. mcli/ml/auth/models.py +154 -0
  71. mcli/ml/auth/permissions.py +302 -0
  72. mcli/ml/backtesting/backtest_engine.py +502 -0
  73. mcli/ml/backtesting/performance_metrics.py +393 -0
  74. mcli/ml/cache.py +400 -0
  75. mcli/ml/cli/main.py +398 -0
  76. mcli/ml/config/settings.py +394 -0
  77. mcli/ml/configs/dvc_config.py +230 -0
  78. mcli/ml/configs/mlflow_config.py +131 -0
  79. mcli/ml/configs/mlops_manager.py +293 -0
  80. mcli/ml/dashboard/app.py +532 -0
  81. mcli/ml/dashboard/app_integrated.py +738 -0
  82. mcli/ml/dashboard/app_supabase.py +560 -0
  83. mcli/ml/dashboard/app_training.py +615 -0
  84. mcli/ml/dashboard/cli.py +51 -0
  85. mcli/ml/data_ingestion/api_connectors.py +501 -0
  86. mcli/ml/data_ingestion/data_pipeline.py +567 -0
  87. mcli/ml/data_ingestion/stream_processor.py +512 -0
  88. mcli/ml/database/migrations/env.py +94 -0
  89. mcli/ml/database/models.py +667 -0
  90. mcli/ml/database/session.py +200 -0
  91. mcli/ml/experimentation/ab_testing.py +845 -0
  92. mcli/ml/features/ensemble_features.py +607 -0
  93. mcli/ml/features/political_features.py +676 -0
  94. mcli/ml/features/recommendation_engine.py +809 -0
  95. mcli/ml/features/stock_features.py +573 -0
  96. mcli/ml/features/test_feature_engineering.py +346 -0
  97. mcli/ml/logging.py +85 -0
  98. mcli/ml/mlops/data_versioning.py +518 -0
  99. mcli/ml/mlops/experiment_tracker.py +377 -0
  100. mcli/ml/mlops/model_serving.py +481 -0
  101. mcli/ml/mlops/pipeline_orchestrator.py +614 -0
  102. mcli/ml/models/base_models.py +324 -0
  103. mcli/ml/models/ensemble_models.py +675 -0
  104. mcli/ml/models/recommendation_models.py +474 -0
  105. mcli/ml/models/test_models.py +487 -0
  106. mcli/ml/monitoring/drift_detection.py +676 -0
  107. mcli/ml/monitoring/metrics.py +45 -0
  108. mcli/ml/optimization/portfolio_optimizer.py +834 -0
  109. mcli/ml/preprocessing/data_cleaners.py +451 -0
  110. mcli/ml/preprocessing/feature_extractors.py +491 -0
  111. mcli/ml/preprocessing/ml_pipeline.py +382 -0
  112. mcli/ml/preprocessing/politician_trading_preprocessor.py +569 -0
  113. mcli/ml/preprocessing/test_preprocessing.py +294 -0
  114. mcli/ml/scripts/populate_sample_data.py +200 -0
  115. mcli/ml/tasks.py +400 -0
  116. mcli/ml/tests/test_integration.py +429 -0
  117. mcli/ml/tests/test_training_dashboard.py +387 -0
  118. mcli/public/oi/oi.py +15 -0
  119. mcli/public/public.py +4 -0
  120. mcli/self/self_cmd.py +1246 -0
  121. mcli/workflow/daemon/api_daemon.py +800 -0
  122. mcli/workflow/daemon/async_command_database.py +681 -0
  123. mcli/workflow/daemon/async_process_manager.py +591 -0
  124. mcli/workflow/daemon/client.py +530 -0
  125. mcli/workflow/daemon/commands.py +1196 -0
  126. mcli/workflow/daemon/daemon.py +905 -0
  127. mcli/workflow/daemon/daemon_api.py +59 -0
  128. mcli/workflow/daemon/enhanced_daemon.py +571 -0
  129. mcli/workflow/daemon/process_cli.py +244 -0
  130. mcli/workflow/daemon/process_manager.py +439 -0
  131. mcli/workflow/daemon/test_daemon.py +275 -0
  132. mcli/workflow/dashboard/dashboard_cmd.py +113 -0
  133. mcli/workflow/docker/docker.py +0 -0
  134. mcli/workflow/file/file.py +100 -0
  135. mcli/workflow/gcloud/config.toml +21 -0
  136. mcli/workflow/gcloud/gcloud.py +58 -0
  137. mcli/workflow/git_commit/ai_service.py +328 -0
  138. mcli/workflow/git_commit/commands.py +430 -0
  139. mcli/workflow/lsh_integration.py +355 -0
  140. mcli/workflow/model_service/client.py +594 -0
  141. mcli/workflow/model_service/download_and_run_efficient_models.py +288 -0
  142. mcli/workflow/model_service/lightweight_embedder.py +397 -0
  143. mcli/workflow/model_service/lightweight_model_server.py +714 -0
  144. mcli/workflow/model_service/lightweight_test.py +241 -0
  145. mcli/workflow/model_service/model_service.py +1955 -0
  146. mcli/workflow/model_service/ollama_efficient_runner.py +425 -0
  147. mcli/workflow/model_service/pdf_processor.py +386 -0
  148. mcli/workflow/model_service/test_efficient_runner.py +234 -0
  149. mcli/workflow/model_service/test_example.py +315 -0
  150. mcli/workflow/model_service/test_integration.py +131 -0
  151. mcli/workflow/model_service/test_new_features.py +149 -0
  152. mcli/workflow/openai/openai.py +99 -0
  153. mcli/workflow/politician_trading/commands.py +1790 -0
  154. mcli/workflow/politician_trading/config.py +134 -0
  155. mcli/workflow/politician_trading/connectivity.py +490 -0
  156. mcli/workflow/politician_trading/data_sources.py +395 -0
  157. mcli/workflow/politician_trading/database.py +410 -0
  158. mcli/workflow/politician_trading/demo.py +248 -0
  159. mcli/workflow/politician_trading/models.py +165 -0
  160. mcli/workflow/politician_trading/monitoring.py +413 -0
  161. mcli/workflow/politician_trading/scrapers.py +966 -0
  162. mcli/workflow/politician_trading/scrapers_california.py +412 -0
  163. mcli/workflow/politician_trading/scrapers_eu.py +377 -0
  164. mcli/workflow/politician_trading/scrapers_uk.py +350 -0
  165. mcli/workflow/politician_trading/scrapers_us_states.py +438 -0
  166. mcli/workflow/politician_trading/supabase_functions.py +354 -0
  167. mcli/workflow/politician_trading/workflow.py +852 -0
  168. mcli/workflow/registry/registry.py +180 -0
  169. mcli/workflow/repo/repo.py +223 -0
  170. mcli/workflow/scheduler/commands.py +493 -0
  171. mcli/workflow/scheduler/cron_parser.py +238 -0
  172. mcli/workflow/scheduler/job.py +182 -0
  173. mcli/workflow/scheduler/monitor.py +139 -0
  174. mcli/workflow/scheduler/persistence.py +324 -0
  175. mcli/workflow/scheduler/scheduler.py +679 -0
  176. mcli/workflow/sync/sync_cmd.py +437 -0
  177. mcli/workflow/sync/test_cmd.py +314 -0
  178. mcli/workflow/videos/videos.py +242 -0
  179. mcli/workflow/wakatime/wakatime.py +11 -0
  180. mcli/workflow/workflow.py +37 -0
  181. mcli_framework-7.0.0.dist-info/METADATA +479 -0
  182. mcli_framework-7.0.0.dist-info/RECORD +186 -0
  183. mcli_framework-7.0.0.dist-info/WHEEL +5 -0
  184. mcli_framework-7.0.0.dist-info/entry_points.txt +7 -0
  185. mcli_framework-7.0.0.dist-info/licenses/LICENSE +21 -0
  186. mcli_framework-7.0.0.dist-info/top_level.txt +1 -0
mcli/lib/auth/auth.py ADDED
@@ -0,0 +1,85 @@
1
+ from typing import Optional
2
+
3
+ import click
4
+
5
+ from .token_manager import TokenManager
6
+
7
+
8
+ def configure():
9
+ """Configure authentication token"""
10
+ token_manager = TokenManager()
11
+
12
+ # Prompt for token, hiding input
13
+ token = click.prompt(
14
+ "Please enter your authentication token",
15
+ hide_input=True,
16
+ confirmation_prompt=True,
17
+ )
18
+
19
+ try:
20
+ token_manager.save_token(token)
21
+ click.echo("Token saved successfully!")
22
+ except Exception as e:
23
+ click.echo(f"Error saving token: {str(e)}", err=True)
24
+ return
25
+
26
+
27
+ def show_token():
28
+ """Display the current authentication token"""
29
+ token_manager = TokenManager()
30
+ token = token_manager.get_token()
31
+
32
+ if token:
33
+ click.echo(f"Current token: {token}")
34
+ else:
35
+ click.echo("No token configured. Run `configure` to set up a token.")
36
+
37
+
38
+ def clear_token():
39
+ """Clear the stored authentication token"""
40
+ token_manager = TokenManager()
41
+
42
+ if click.confirm("Are you sure you want to clear the stored token?"):
43
+ try:
44
+ if token_manager.config_file.exists():
45
+ token_manager.config_file.unlink()
46
+ click.echo("Token cleared successfully!")
47
+ except Exception as e:
48
+ click.echo(f"Error clearing token: {str(e)}", err=True)
49
+
50
+
51
+ def get_current_token() -> Optional[str]:
52
+ """
53
+ Utility function to get the current token.
54
+ Can be used by other parts of your application.
55
+ """
56
+ token_manager = TokenManager()
57
+ return token_manager.get_token()
58
+
59
+
60
+ def get_current_url() -> Optional[str]:
61
+ """
62
+ Utility function to get the current token.
63
+ Can be used by other parts of your application.
64
+ """
65
+ token_manager = TokenManager()
66
+ return token_manager.get_url()
67
+
68
+
69
+ def get_mcli_basic_auth() -> Optional[str]:
70
+ """
71
+ Utility function to get the current token.
72
+ Can be used by other parts of your application.
73
+ """
74
+ token_manager = TokenManager()
75
+ return token_manager.get_mcli_basic_auth()
76
+
77
+
78
+ @click.group(name="auth")
79
+ def auth():
80
+ """Authentication commands"""
81
+ pass
82
+
83
+
84
+ if __name__ == "__main__":
85
+ auth()
@@ -0,0 +1,85 @@
1
+ import json
2
+ from typing import Optional
3
+
4
+ from mcli.lib.config import DEV_SECRETS_ROOT
5
+ from mcli.lib.fs import get_absolute_path
6
+ from mcli.lib.logger.logger import get_logger
7
+
8
+ from .credential_manager import CredentialManager
9
+
10
+ logger = get_logger(__name__)
11
+
12
+
13
+ class AwsManager(CredentialManager):
14
+ """
15
+ Specialized credential manager for handling authentication tokens and environment URLs.
16
+ """
17
+
18
+ def __init__(self, app_name: str = "mcli"):
19
+ """
20
+ Initialize AwsManager with a specific configuration filename.
21
+
22
+ Args:
23
+ app_name (str, optional): Name of the application. Defaults to "mcli".
24
+ """
25
+ super().__init__(app_name, config_filename="mcli.token.config.json")
26
+
27
+ def save_token(self, token: str):
28
+ """
29
+ Save authentication token to configuration.
30
+
31
+ Args:
32
+ token (str): Authentication token to save.
33
+
34
+ Raises:
35
+ ValueError: If token is empty or not a string.
36
+ """
37
+ if not token or not isinstance(token, str):
38
+ raise ValueError("Token must be a non-empty string")
39
+
40
+ try:
41
+ self.update_config("auth_token", token)
42
+ except Exception as e:
43
+ raise Exception(f"Failed to save token: {str(e)}")
44
+
45
+ def get_token(self) -> Optional[str]:
46
+ """
47
+ Retrieve the stored authentication token.
48
+
49
+ Returns:
50
+ Optional[str]: Stored authentication token or None if not found.
51
+ """
52
+ try:
53
+ logger.info("getting token")
54
+ return self.get_config_value("auth_token")
55
+ except Exception as e:
56
+ logger.info(f"Warning: Error retrieving token: {str(e)}")
57
+ return None
58
+
59
+ def clear_token(self):
60
+ """
61
+ Clear the stored authentication token.
62
+ Uses the base class clear_config method.
63
+ """
64
+ self.clear_config()
65
+
66
+ def get_url(self) -> Optional[str]:
67
+ """
68
+ Retrieve environment URL from configuration.
69
+
70
+ Returns:
71
+ Optional[str]: Stored environment URL or None if not found.
72
+ """
73
+ try:
74
+ logger.info("getting url")
75
+ return self.get_config_value("env_url")
76
+ except Exception as e:
77
+ logger.info(f"Warning: Error retrieving environment URL: {str(e)}")
78
+ return None
79
+
80
+ @staticmethod
81
+ def persist_aws_storage_creds(access_key, secret_key):
82
+ filepath = get_absolute_path(DEV_SECRETS_ROOT + "aws/aws.json")
83
+ with open(filepath, "w") as f:
84
+ json.dump({"access_key": access_key, "secret_key": secret_key}, f)
85
+ logger.info("Aws secrets have been persisted into:", filepath)
@@ -0,0 +1,91 @@
1
+ import json
2
+ from typing import Optional
3
+
4
+ from mcli.lib.config import DEV_SECRETS_ROOT
5
+ from mcli.lib.fs import get_absolute_path
6
+ from mcli.lib.logger.logger import get_logger
7
+
8
+ from .credential_manager import CredentialManager
9
+
10
+ logger = get_logger(__name__)
11
+
12
+
13
+ class AzureManager(CredentialManager):
14
+ """
15
+ Specialized credential manager for handling authentication tokens and environment URLs.
16
+ """
17
+
18
+ def __init__(self, app_name: str = "mcli"):
19
+ """
20
+ Initialize GcpManager with a specific configuration filename.
21
+
22
+ Args:
23
+ app_name (str, optional): Name of the application. Defaults to "mcli".
24
+ """
25
+ super().__init__(app_name, config_filename="mcli.token.config.json")
26
+
27
+ def save_token(self, token: str):
28
+ """
29
+ Save authentication token to configuration.
30
+
31
+ Args:
32
+ token (str): Authentication token to save.
33
+
34
+ Raises:
35
+ ValueError: If token is empty or not a string.
36
+ """
37
+ if not token or not isinstance(token, str):
38
+ raise ValueError("Token must be a non-empty string")
39
+
40
+ try:
41
+ self.update_config("auth_token", token)
42
+ except Exception as e:
43
+ raise Exception(f"Failed to save token: {str(e)}")
44
+
45
+ def get_token(self) -> Optional[str]:
46
+ """
47
+ Retrieve the stored authentication token.
48
+
49
+ Returns:
50
+ Optional[str]: Stored authentication token or None if not found.
51
+ """
52
+ try:
53
+ logger.info("getting token")
54
+ return self.get_config_value("auth_token")
55
+ except Exception as e:
56
+ logger.info(f"Warning: Error retrieving token: {str(e)}")
57
+ return None
58
+
59
+ def clear_token(self):
60
+ """
61
+ Clear the stored authentication token.
62
+ Uses the base class clear_config method.
63
+ """
64
+ self.clear_config()
65
+
66
+ def get_url(self) -> Optional[str]:
67
+ """
68
+ Retrieve environment URL from configuration.
69
+
70
+ Returns:
71
+ Optional[str]: Stored environment URL or None if not found.
72
+ """
73
+ try:
74
+ logger.info("getting url")
75
+ return self.get_config_value("env_url")
76
+ except Exception as e:
77
+ logger.info(f"Warning: Error retrieving environment URL: {str(e)}")
78
+ return None
79
+
80
+ @staticmethod
81
+ def persist_azure_storage_creds(account_name, access_key):
82
+ filepath = get_absolute_path(DEV_SECRETS_ROOT + "azure/azure.json")
83
+ with open(filepath, "w") as f:
84
+ json.dump(
85
+ {
86
+ "storage_account_name": account_name,
87
+ "storage_access_key": access_key,
88
+ },
89
+ f,
90
+ )
91
+ logger.info("Azure secrets have been persisted into:", filepath)
@@ -0,0 +1,192 @@
1
+ import base64
2
+ import json
3
+ import math
4
+ import os
5
+ import time
6
+ from pathlib import Path
7
+ from typing import Any, Dict, Optional
8
+
9
+ from mcli.lib.config import DEV_SECRETS_ROOT
10
+ from mcli.lib.fs import get_absolute_path
11
+ from mcli.lib.logger.logger import get_logger
12
+
13
+ logger = get_logger(__name__)
14
+
15
+
16
+ class CredentialManager:
17
+ """
18
+ A base class for managing credentials and configuration files
19
+ with secure file handling and permissions.
20
+ """
21
+
22
+ def __init__(self, app_name: str = "mcli", config_filename: str = "mcli.key.config.json"):
23
+ """
24
+ Initialize the CredentialManager.
25
+
26
+ Args:
27
+ app_name (str, optional): Name of the application. Defaults to "mcli".
28
+ config_filename (str, optional): Name of the configuration file. Defaults to "mcli.key.config.json".
29
+ """
30
+ self.config_dir = Path.home() / ".config" / app_name
31
+ self.config_file = self.config_dir / config_filename
32
+ self._ensure_config_dir()
33
+ # logger.info("config file:", self.config_file)
34
+
35
+ def _ensure_config_dir(self):
36
+ """
37
+ Create config directory with secure permissions.
38
+ Ensures parent directories exist and sets proper access modes.
39
+ """
40
+ logger.info("Insure of ensure config dir")
41
+ logger.info("config file:", self.config_file)
42
+ logger.info("config dir:", self.config_dir)
43
+ try:
44
+ # Create parent directories if they don't exist
45
+ # 0o700 means read, write, execute permissions for owner only
46
+ self.config_dir.mkdir(mode=0o700, parents=True, exist_ok=True)
47
+
48
+ # Set secure permissions for config file if it exists
49
+ if self.config_file.exists():
50
+ self.config_file.chmod(0o600)
51
+ except Exception as e:
52
+ raise Exception(f"Failed to create config directory: {str(e)}")
53
+
54
+ def read_config(self) -> Dict[str, Any]:
55
+ """
56
+ Read configuration from file.
57
+
58
+ Returns:
59
+ Dict[str, Any]: Configuration dictionary, empty if file doesn't exist or is invalid.
60
+ """
61
+ try:
62
+ logger.info("Reading config file:", self.config_file)
63
+ if not self.config_file.exists():
64
+ return {}
65
+
66
+ with open(self.config_file, "r", encoding="utf-8") as f:
67
+ config = json.load(f)
68
+ return config if isinstance(config, dict) else {}
69
+ except json.JSONDecodeError:
70
+ logger.info(
71
+ f"Warning: Config file {self.config_file} is corrupted, creating new configuration"
72
+ )
73
+ return {}
74
+ except Exception as e:
75
+ logger.info(f"Warning: Error reading config file: {str(e)}")
76
+ return {}
77
+
78
+ def write_config(self, config: Dict[str, Any]):
79
+ """
80
+ Write configuration to file with secure permissions.
81
+
82
+ Args:
83
+ config (Dict[str, Any]): Configuration dictionary to write.
84
+
85
+ Raises:
86
+ ValueError: If config is not a dictionary.
87
+ Exception: If writing configuration fails.
88
+ """
89
+ if not isinstance(config, dict):
90
+ raise ValueError("Configuration must be a dictionary")
91
+
92
+ try:
93
+ # Ensure directory exists before writing
94
+ self._ensure_config_dir()
95
+
96
+ # Write with proper encoding
97
+ with open(self.config_file, "w", encoding="utf-8") as f:
98
+ json.dump(config, f, indent=2)
99
+
100
+ # Set secure permissions (read/write only for owner)
101
+ self.config_file.chmod(0o600)
102
+ except Exception as e:
103
+ raise Exception(f"Failed to write configuration: {str(e)}")
104
+
105
+ def get_config_path(self) -> str:
106
+ """
107
+ Get the path to the configuration file.
108
+
109
+ Returns:
110
+ str: Path to the configuration file.
111
+ """
112
+ return str(self.config_file)
113
+
114
+ def clear_config(self):
115
+ """
116
+ Clear the stored configuration by removing the config file.
117
+ """
118
+ try:
119
+ if self.config_file.exists():
120
+ self.config_file.unlink()
121
+ except Exception as e:
122
+ raise Exception(f"Failed to clear configuration: {str(e)}")
123
+
124
+ def update_config(self, key: str, value: Any):
125
+ """
126
+ Update a specific key in the configuration.
127
+
128
+ Args:
129
+ key (str): Configuration key to update.
130
+ value (Any): Value to set for the key.
131
+ """
132
+ try:
133
+ config = self.read_config()
134
+ config[key] = value
135
+ self.write_config(config)
136
+ except Exception as e:
137
+ raise Exception(f"Failed to update configuration: {str(e)}")
138
+
139
+ def get_config_value(self, key: str, default: Optional[Any] = None) -> Optional[Any]:
140
+ """
141
+ Retrieve a specific value from the configuration.
142
+
143
+ Args:
144
+ key (str): Configuration key to retrieve.
145
+ default (Optional[Any], optional): Default value if key doesn't exist. Defaults to None.
146
+
147
+ Returns:
148
+ Optional[Any]: Value of the configuration key or default.
149
+ """
150
+ try:
151
+ config = self.read_config()
152
+ return config.get(key, default)
153
+ except Exception as e:
154
+ logger.info(f"Warning: Error retrieving configuration value: {str(e)}")
155
+ return default
156
+
157
+ @staticmethod
158
+ def generate_signature(private_key_path):
159
+ private_key_path = get_absolute_path(private_key_path)
160
+ if not os.path.exists(private_key_path):
161
+ raise Exception("Private key does not exist at path:" + private_key_path)
162
+ nonce = str(math.floor(time.time() * 1000))
163
+ # Generate the signature using the private key
164
+ sig = os.popen(
165
+ "logger.info('Generating signature') && echo -n "
166
+ + nonce
167
+ + " | openssl dgst -hex -sigopt rsa_padding_mode:pss -sha256 -sign "
168
+ + private_key_path
169
+ ).read()
170
+ # Remove the '(stdin)=' prefix from the output
171
+ sig = sig[len("SHA2-256(stdin)=") :].strip()
172
+ # Encode the nonce in hexadecimal format
173
+ hex_nonce = nonce.encode("ascii").hex()
174
+ return (sig, hex_nonce)
175
+
176
+ @staticmethod
177
+ def persist_generic_creds(thirdPartyApiKind, creds):
178
+ filepath = get_absolute_path(DEV_SECRETS_ROOT + "thirdParty/" + thirdPartyApiKind + ".txt")
179
+ with open(filepath, "w") as f:
180
+ f.write(json.dumps(str(creds)))
181
+ logger.info(thirdPartyApiKind + " secrets have been persisted into:", filepath)
182
+
183
+ @staticmethod
184
+ def create_key_auth_token(user_id, private_key_path):
185
+ sig, hex_nonce = CredentialManager.generate_signature(private_key_path)
186
+ key = user_id + ":" + hex_nonce + ":" + sig
187
+ key_bytes = key.encode("utf-8")
188
+ key_b64 = base64.b64encode(key_bytes).decode("ascii")
189
+ return key_b64
190
+
191
+
192
+ __all__ = ["CredentialManager"]
@@ -0,0 +1,93 @@
1
+ import json
2
+ from typing import Optional
3
+
4
+ from mcli.lib.config import DEV_SECRETS_ROOT
5
+ from mcli.lib.fs import get_absolute_path
6
+ from mcli.lib.logger.logger import get_logger
7
+
8
+ from .credential_manager import CredentialManager
9
+
10
+ logger = get_logger(__name__)
11
+
12
+
13
+ class GcpManager(CredentialManager):
14
+ """
15
+ Specialized credential manager for handling authentication tokens and environment URLs.
16
+ """
17
+
18
+ def __init__(self, app_name: str = "mcli"):
19
+ """
20
+ Initialize GcpManager with a specific configuration filename.
21
+
22
+ Args:
23
+ app_name (str, optional): Name of the application. Defaults to "mcli".
24
+ """
25
+ super().__init__(app_name, config_filename="mcli.token.config.json")
26
+
27
+ def save_token(self, token: str):
28
+ """
29
+ Save authentication token to configuration.
30
+
31
+ Args:
32
+ token (str): Authentication token to save.
33
+
34
+ Raises:
35
+ ValueError: If token is empty or not a string.
36
+ """
37
+ if not token or not isinstance(token, str):
38
+ raise ValueError("Token must be a non-empty string")
39
+
40
+ try:
41
+ self.update_config("auth_token", token)
42
+ except Exception as e:
43
+ raise Exception(f"Failed to save token: {str(e)}")
44
+
45
+ def get_token(self) -> Optional[str]:
46
+ """
47
+ Retrieve the stored authentication token.
48
+
49
+ Returns:
50
+ Optional[str]: Stored authentication token or None if not found.
51
+ """
52
+ try:
53
+ logger.info("getting token")
54
+ return self.get_config_value("auth_token")
55
+ except Exception as e:
56
+ logger.info(f"Warning: Error retrieving token: {str(e)}")
57
+ return None
58
+
59
+ def clear_token(self):
60
+ """
61
+ Clear the stored authentication token.
62
+ Uses the base class clear_config method.
63
+ """
64
+ self.clear_config()
65
+
66
+ def get_url(self) -> Optional[str]:
67
+ """
68
+ Retrieve environment URL from configuration.
69
+
70
+ Returns:
71
+ Optional[str]: Stored environment URL or None if not found.
72
+ """
73
+ try:
74
+ logger.info("getting url")
75
+ return self.get_config_value("env_url")
76
+ except Exception as e:
77
+ logger.info(f"Warning: Error retrieving environment URL: {str(e)}")
78
+ return None
79
+
80
+ @staticmethod
81
+ def persist_gcp_storage_creds(account_id, account_email, access_key, secret_key):
82
+ filepath = get_absolute_path(DEV_SECRETS_ROOT + "gcp/gcp.json")
83
+ with open(filepath, "w") as f:
84
+ json.dump(
85
+ {
86
+ "accountId": account_id,
87
+ "accountEmail": account_email,
88
+ "accessKey": access_key,
89
+ "secretKey": secret_key,
90
+ },
91
+ f,
92
+ )
93
+ logger.info("Gcp secrets have been persisted into:", filepath)
@@ -0,0 +1,117 @@
1
+ from typing import Optional
2
+
3
+ from mcli.lib.logger.logger import get_logger
4
+
5
+ from .credential_manager import CredentialManager
6
+
7
+ logger = get_logger(__name__)
8
+
9
+
10
+ ALLOWED_ACTIONS = [
11
+ "REVOKE_AZURE",
12
+ "REVOKE_AWS",
13
+ "PROVISION_AZURE",
14
+ "REVOKE_GCP",
15
+ "PROVISION_AWS",
16
+ "PROVISION_GCP",
17
+ "PROVISION_THIRDPARTY",
18
+ ]
19
+
20
+
21
+ class KeyManager(CredentialManager):
22
+ """
23
+ Specialized credential manager for handling authentication tokens and keys.
24
+ """
25
+
26
+ def save_key(self, token: str):
27
+ """
28
+ Save authentication token to configuration.
29
+
30
+ Args:
31
+ token (str): Authentication token to save.
32
+
33
+ Raises:
34
+ ValueError: If token is empty or not a string.
35
+ """
36
+ if not token or not isinstance(token, str):
37
+ raise ValueError("Token must be a non-empty string")
38
+
39
+ try:
40
+ self.update_config("auth_token", token)
41
+ except Exception as e:
42
+ raise Exception(f"Failed to save token: {str(e)}")
43
+
44
+ def get_key(self) -> Optional[str]:
45
+ """
46
+ Retrieve the stored authentication token.
47
+
48
+ Returns:
49
+ Optional[str]: Stored authentication token or None if not found.
50
+ """
51
+ try:
52
+ logger.info("getting token")
53
+ return self.get_config_value("auth_token")
54
+ except Exception as e:
55
+ logger.info(f"Warning: Error retrieving token: {str(e)}")
56
+ return None
57
+
58
+ def clear_key(self):
59
+ """
60
+ Clear the stored authentication token.
61
+ Uses the base class clear_config method.
62
+ """
63
+ self.clear_config()
64
+
65
+ def get_environment_url(self) -> Optional[str]:
66
+ """
67
+ Retrieve environment URL from configuration.
68
+
69
+ Returns:
70
+ Optional[str]: Environment URL or None if not found.
71
+ """
72
+ try:
73
+ logger.info("getting environment url")
74
+ return self.get_config_value("env_url")
75
+ except Exception as e:
76
+ logger.info(f"Warning: Error retrieving environment URL: {str(e)}")
77
+ return None
78
+
79
+ def create_key_pair(self) -> dict:
80
+ """
81
+ Create a key pair for secure authentication.
82
+ This is a placeholder implementation - should be replaced with actual crypto.
83
+
84
+ Returns:
85
+ dict: Dictionary containing public and private key information
86
+ """
87
+ logger.info("Creating key pair - placeholder implementation")
88
+ # In a real implementation, this would use cryptographic libraries
89
+ # like cryptography or pycryptodome to generate actual key pairs
90
+ return {
91
+ "public_key": "placeholder_public_key",
92
+ "private_key": "placeholder_private_key",
93
+ "algorithm": "placeholder_rsa",
94
+ "created_at": logger.info.__code__.co_filename, # Placeholder timestamp
95
+ }
96
+
97
+ @staticmethod
98
+ def allowed_action(action: str) -> bool:
99
+ """
100
+ Check if the given action is allowed.
101
+
102
+ Args:
103
+ action (str): Action to validate.
104
+
105
+ Returns:
106
+ bool: True if action is allowed, False otherwise.
107
+ """
108
+ return action in ALLOWED_ACTIONS
109
+
110
+ @staticmethod
111
+ def logger_info_allowed_actions():
112
+ """
113
+ logger.info the list of allowed actions.
114
+ """
115
+ logger.info("Allowed actions:")
116
+ for action in ALLOWED_ACTIONS:
117
+ logger.info(f" - {action}")