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
@@ -0,0 +1,355 @@
1
+ """
2
+ LSH Integration Commands for mcli
3
+ Provides CLI interface for managing LSH daemon integration and data pipeline
4
+ """
5
+
6
+ import asyncio
7
+ import json
8
+ import os
9
+ import sys
10
+ from pathlib import Path
11
+ from typing import Dict, Any, Optional
12
+
13
+ import click
14
+
15
+ from mcli.lib.api import mcli_decorators as mcli
16
+ from mcli.lib.logger.logger import get_logger
17
+ from mcli.lib.services.lsh_client import LSHClient, LSHEventProcessor
18
+ from mcli.lib.services.data_pipeline import LSHDataPipeline, DataPipelineConfig
19
+
20
+ logger = get_logger(__name__)
21
+
22
+
23
+ @mcli.command(
24
+ name="lsh-status",
25
+ help="Check LSH daemon connection and status"
26
+ )
27
+ @mcli.option("--url", default=None, help="LSH API URL (default: $LSH_API_URL or http://localhost:3030)")
28
+ @mcli.option("--api-key", default=None, help="LSH API key (default: $LSH_API_KEY)")
29
+ async def lsh_status(url: Optional[str], api_key: Optional[str]):
30
+ """Check LSH daemon connection and status"""
31
+ try:
32
+ async with LSHClient(base_url=url, api_key=api_key) as client:
33
+ # Test connection
34
+ is_healthy = await client.health_check()
35
+ if not is_healthy:
36
+ mcli.echo(mcli.style("āŒ LSH daemon is not healthy", fg="red"))
37
+ sys.exit(1)
38
+
39
+ # Get status
40
+ status = await client.get_status()
41
+
42
+ mcli.echo(mcli.style("āœ… LSH Daemon Status", fg="green", bold=True))
43
+ mcli.echo(f"URL: {client.base_url}")
44
+ mcli.echo(f"PID: {status.get('pid', 'unknown')}")
45
+ mcli.echo(f"Uptime: {status.get('uptime', 0) // 60} minutes")
46
+ mcli.echo(f"Memory: {status.get('memoryUsage', {}).get('heapUsed', 0) // 1024 // 1024} MB")
47
+
48
+ # Get jobs summary
49
+ jobs = await client.list_jobs()
50
+ running_jobs = [j for j in jobs if j.get('status') == 'running']
51
+
52
+ mcli.echo(f"Total Jobs: {len(jobs)}")
53
+ mcli.echo(f"Running Jobs: {len(running_jobs)}")
54
+
55
+ except Exception as e:
56
+ mcli.echo(mcli.style(f"āŒ Error connecting to LSH daemon: {e}", fg="red"))
57
+ sys.exit(1)
58
+
59
+
60
+ @mcli.command(
61
+ name="lsh-jobs",
62
+ help="List and manage LSH jobs"
63
+ )
64
+ @mcli.option("--status", help="Filter by job status")
65
+ @mcli.option("--format", type=mcli.Choice(["table", "json"]), default="table", help="Output format")
66
+ @mcli.option("--url", default=None, help="LSH API URL")
67
+ @mcli.option("--api-key", default=None, help="LSH API key")
68
+ async def lsh_jobs(status: Optional[str], format: str, url: Optional[str], api_key: Optional[str]):
69
+ """List LSH jobs"""
70
+ try:
71
+ async with LSHClient(base_url=url, api_key=api_key) as client:
72
+ filter_params = {}
73
+ if status:
74
+ filter_params['status'] = status
75
+
76
+ jobs = await client.list_jobs(filter_params)
77
+
78
+ if format == "json":
79
+ mcli.echo(json.dumps(jobs, indent=2))
80
+ else:
81
+ # Table format
82
+ mcli.echo(mcli.style("\nšŸ“‹ LSH Jobs", fg="blue", bold=True))
83
+ mcli.echo("-" * 80)
84
+ mcli.echo(f"{'ID':<20} {'Name':<25} {'Status':<10} {'Type':<15}")
85
+ mcli.echo("-" * 80)
86
+
87
+ for job in jobs:
88
+ job_id = job.get('id', 'unknown')[:18]
89
+ name = job.get('name', 'unknown')[:23]
90
+ job_status = job.get('status', 'unknown')
91
+ job_type = job.get('type', 'unknown')[:13]
92
+
93
+ status_color = {
94
+ 'running': 'green',
95
+ 'completed': 'blue',
96
+ 'failed': 'red',
97
+ 'pending': 'yellow'
98
+ }.get(job_status, 'white')
99
+
100
+ mcli.echo(f"{job_id:<20} {name:<25} {mcli.style(job_status, fg=status_color):<20} {job_type:<15}")
101
+
102
+ except Exception as e:
103
+ mcli.echo(mcli.style(f"āŒ Error listing jobs: {e}", fg="red"))
104
+ sys.exit(1)
105
+
106
+
107
+ @mcli.command(
108
+ name="lsh-create-job",
109
+ help="Create a new LSH job"
110
+ )
111
+ @mcli.option("--name", required=True, help="Job name")
112
+ @mcli.option("--command", required=True, help="Command to execute")
113
+ @mcli.option("--schedule", help="Cron schedule (e.g., '0 */6 * * *')")
114
+ @mcli.option("--type", default="shell", help="Job type")
115
+ @mcli.option("--description", help="Job description")
116
+ @mcli.option("--tags", help="Comma-separated tags")
117
+ @mcli.option("--database-sync", is_flag=True, help="Enable database synchronization")
118
+ @mcli.option("--url", default=None, help="LSH API URL")
119
+ @mcli.option("--api-key", default=None, help="LSH API key")
120
+ async def lsh_create_job(
121
+ name: str,
122
+ command: str,
123
+ schedule: Optional[str],
124
+ type: str,
125
+ description: Optional[str],
126
+ tags: Optional[str],
127
+ database_sync: bool,
128
+ url: Optional[str],
129
+ api_key: Optional[str]
130
+ ):
131
+ """Create a new LSH job"""
132
+ try:
133
+ async with LSHClient(base_url=url, api_key=api_key) as client:
134
+ job_spec = {
135
+ "name": name,
136
+ "command": command,
137
+ "type": type,
138
+ "databaseSync": database_sync
139
+ }
140
+
141
+ if schedule:
142
+ job_spec["schedule"] = {"cron": schedule}
143
+
144
+ if description:
145
+ job_spec["description"] = description
146
+
147
+ if tags:
148
+ job_spec["tags"] = [tag.strip() for tag in tags.split(",")]
149
+
150
+ job = await client.create_job(job_spec)
151
+
152
+ mcli.echo(mcli.style("āœ… Job created successfully", fg="green"))
153
+ mcli.echo(f"Job ID: {job.get('id')}")
154
+ mcli.echo(f"Name: {job.get('name')}")
155
+ mcli.echo(f"Status: {job.get('status')}")
156
+
157
+ except Exception as e:
158
+ mcli.echo(mcli.style(f"āŒ Error creating job: {e}", fg="red"))
159
+ sys.exit(1)
160
+
161
+
162
+ @mcli.command(
163
+ name="lsh-pipeline",
164
+ help="Start LSH data pipeline listener"
165
+ )
166
+ @mcli.option("--batch-size", default=100, help="Batch size for processing")
167
+ @mcli.option("--batch-timeout", default=30, help="Batch timeout in seconds")
168
+ @mcli.option("--output-dir", default="./data/processed", help="Output directory for processed data")
169
+ @mcli.option("--disable-validation", is_flag=True, help="Disable data validation")
170
+ @mcli.option("--disable-enrichment", is_flag=True, help="Disable data enrichment")
171
+ @mcli.option("--url", default=None, help="LSH API URL")
172
+ @mcli.option("--api-key", default=None, help="LSH API key")
173
+ async def lsh_pipeline(
174
+ batch_size: int,
175
+ batch_timeout: int,
176
+ output_dir: str,
177
+ disable_validation: bool,
178
+ disable_enrichment: bool,
179
+ url: Optional[str],
180
+ api_key: Optional[str]
181
+ ):
182
+ """Start LSH data pipeline listener"""
183
+ try:
184
+ # Configure pipeline
185
+ config = DataPipelineConfig()
186
+ config.batch_size = batch_size
187
+ config.batch_timeout = batch_timeout
188
+ config.output_dir = Path(output_dir)
189
+ config.enable_validation = not disable_validation
190
+ config.enable_enrichment = not disable_enrichment
191
+
192
+ mcli.echo(mcli.style("šŸš€ Starting LSH Data Pipeline", fg="green", bold=True))
193
+ mcli.echo(f"LSH API: {url or os.getenv('LSH_API_URL', 'http://localhost:3030')}")
194
+ mcli.echo(f"Batch Size: {batch_size}")
195
+ mcli.echo(f"Batch Timeout: {batch_timeout}s")
196
+ mcli.echo(f"Output Directory: {output_dir}")
197
+ mcli.echo(f"Validation: {'enabled' if config.enable_validation else 'disabled'}")
198
+ mcli.echo(f"Enrichment: {'enabled' if config.enable_enrichment else 'disabled'}")
199
+
200
+ # Initialize client and pipeline
201
+ lsh_client = LSHClient(base_url=url, api_key=api_key)
202
+ pipeline = LSHDataPipeline(lsh_client, config)
203
+
204
+ # Setup graceful shutdown
205
+ def signal_handler(signum, frame):
206
+ mcli.echo(mcli.style("\nšŸ›‘ Stopping pipeline...", fg="yellow"))
207
+ asyncio.create_task(pipeline.stop())
208
+ sys.exit(0)
209
+
210
+ import signal
211
+ signal.signal(signal.SIGINT, signal_handler)
212
+ signal.signal(signal.SIGTERM, signal_handler)
213
+
214
+ # Start pipeline
215
+ await pipeline.start()
216
+
217
+ except KeyboardInterrupt:
218
+ mcli.echo(mcli.style("\nšŸ›‘ Pipeline stopped by user", fg="yellow"))
219
+ except Exception as e:
220
+ mcli.echo(mcli.style(f"āŒ Pipeline error: {e}", fg="red"))
221
+ sys.exit(1)
222
+
223
+
224
+ @mcli.command(
225
+ name="lsh-listen",
226
+ help="Listen to LSH events (for debugging)"
227
+ )
228
+ @mcli.option("--url", default=None, help="LSH API URL")
229
+ @mcli.option("--api-key", default=None, help="LSH API key")
230
+ @mcli.option("--filter", help="Event type filter (e.g., 'job:completed')")
231
+ async def lsh_listen(url: Optional[str], api_key: Optional[str], filter: Optional[str]):
232
+ """Listen to LSH events for debugging"""
233
+ try:
234
+ mcli.echo(mcli.style("šŸ‘‚ Listening to LSH events...", fg="blue", bold=True))
235
+ mcli.echo("Press Ctrl+C to stop")
236
+
237
+ lsh_client = LSHClient(base_url=url, api_key=api_key)
238
+
239
+ def print_event(event_data: Dict[str, Any]):
240
+ event_type = event_data.get("type", "unknown")
241
+ timestamp = event_data.get("timestamp", "")
242
+
243
+ if filter and filter not in event_type:
244
+ return
245
+
246
+ mcli.echo(f"\nšŸ”” Event: {mcli.style(event_type, fg='cyan', bold=True)}")
247
+ mcli.echo(f"Time: {timestamp}")
248
+ mcli.echo(f"Data: {json.dumps(event_data.get('data', {}), indent=2)}")
249
+
250
+ lsh_client.on("*", print_event)
251
+
252
+ await lsh_client.connect()
253
+ await lsh_client.stream_events()
254
+
255
+ except KeyboardInterrupt:
256
+ mcli.echo(mcli.style("\nšŸ‘‹ Stopped listening", fg="yellow"))
257
+ except Exception as e:
258
+ mcli.echo(mcli.style(f"āŒ Error listening to events: {e}", fg="red"))
259
+ sys.exit(1)
260
+
261
+
262
+ @mcli.command(
263
+ name="lsh-webhook",
264
+ help="Manage LSH webhooks"
265
+ )
266
+ @mcli.option("--action", type=mcli.Choice(["list", "add"]), required=True, help="Action to perform")
267
+ @mcli.option("--endpoint", help="Webhook endpoint URL (for add action)")
268
+ @mcli.option("--url", default=None, help="LSH API URL")
269
+ @mcli.option("--api-key", default=None, help="LSH API key")
270
+ async def lsh_webhook(action: str, endpoint: Optional[str], url: Optional[str], api_key: Optional[str]):
271
+ """Manage LSH webhooks"""
272
+ try:
273
+ async with LSHClient(base_url=url, api_key=api_key) as client:
274
+ if action == "list":
275
+ webhooks = await client.list_webhooks()
276
+ mcli.echo(mcli.style("šŸ“® LSH Webhooks", fg="blue", bold=True))
277
+ mcli.echo(f"Enabled: {webhooks.get('enabled', False)}")
278
+ mcli.echo("Endpoints:")
279
+ for endpoint in webhooks.get("endpoints", []):
280
+ mcli.echo(f" • {endpoint}")
281
+
282
+ elif action == "add":
283
+ if not endpoint:
284
+ mcli.echo(mcli.style("āŒ Endpoint URL required for add action", fg="red"))
285
+ sys.exit(1)
286
+
287
+ result = await client.add_webhook(endpoint)
288
+ mcli.echo(mcli.style("āœ… Webhook added successfully", fg="green"))
289
+ mcli.echo(f"Endpoints: {result.get('endpoints', [])}")
290
+
291
+ except Exception as e:
292
+ mcli.echo(mcli.style(f"āŒ Error managing webhooks: {e}", fg="red"))
293
+ sys.exit(1)
294
+
295
+
296
+ @mcli.command(
297
+ name="lsh-config",
298
+ help="Configure LSH integration settings"
299
+ )
300
+ @mcli.option("--set-url", help="Set LSH API URL")
301
+ @mcli.option("--set-api-key", help="Set LSH API key")
302
+ @mcli.option("--show", is_flag=True, help="Show current configuration")
303
+ def lsh_config(set_url: Optional[str], set_api_key: Optional[str], show: bool):
304
+ """Configure LSH integration settings"""
305
+ env_file = Path.home() / ".mcli" / "lsh.env"
306
+ env_file.parent.mkdir(exist_ok=True)
307
+
308
+ if show:
309
+ mcli.echo(mcli.style("āš™ļø LSH Configuration", fg="blue", bold=True))
310
+ mcli.echo(f"API URL: {os.getenv('LSH_API_URL', 'not set')}")
311
+ mcli.echo(f"API Key: {'set' if os.getenv('LSH_API_KEY') else 'not set'}")
312
+ mcli.echo(f"Config file: {env_file}")
313
+ return
314
+
315
+ if set_url:
316
+ # Update env file
317
+ config = {}
318
+ if env_file.exists():
319
+ with open(env_file, 'r') as f:
320
+ for line in f:
321
+ if '=' in line:
322
+ key, value = line.strip().split('=', 1)
323
+ config[key] = value
324
+
325
+ config['LSH_API_URL'] = set_url
326
+
327
+ with open(env_file, 'w') as f:
328
+ for key, value in config.items():
329
+ f.write(f"{key}={value}\n")
330
+
331
+ mcli.echo(mcli.style(f"āœ… LSH API URL set to: {set_url}", fg="green"))
332
+
333
+ if set_api_key:
334
+ # Update env file
335
+ config = {}
336
+ if env_file.exists():
337
+ with open(env_file, 'r') as f:
338
+ for line in f:
339
+ if '=' in line:
340
+ key, value = line.strip().split('=', 1)
341
+ config[key] = value
342
+
343
+ config['LSH_API_KEY'] = set_api_key
344
+
345
+ with open(env_file, 'w') as f:
346
+ for key, value in config.items():
347
+ f.write(f"{key}={value}\n")
348
+
349
+ mcli.echo(mcli.style("āœ… LSH API key updated", fg="green"))
350
+
351
+
352
+ # Register all commands with mcli
353
+ def register_lsh_commands():
354
+ """Register LSH integration commands with mcli"""
355
+ pass # Commands are automatically registered via decorators