mindroot 9.3.0__py3-none-any.whl → 9.6.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.
Files changed (183) hide show
  1. mindroot/coreplugins/admin/__init__.py +3 -1
  2. mindroot/coreplugins/admin/agent_router.py +250 -7
  3. mindroot/coreplugins/admin/asset_manager.py +164 -0
  4. mindroot/coreplugins/admin/command_router.py +236 -1
  5. mindroot/coreplugins/admin/mcp_catalog_routes.py +156 -0
  6. mindroot/coreplugins/admin/mcp_publish_routes.py +450 -0
  7. mindroot/coreplugins/admin/mcp_registry_routes.py +495 -0
  8. mindroot/coreplugins/admin/mcp_routes.py +216 -0
  9. mindroot/coreplugins/admin/mod.py +62 -0
  10. mindroot/coreplugins/admin/oauth_callback_router.py +84 -0
  11. mindroot/coreplugins/admin/persona_handler.py +15 -6
  12. mindroot/coreplugins/admin/persona_router.py +158 -2
  13. mindroot/coreplugins/admin/plugin_manager.py +105 -9
  14. mindroot/coreplugins/admin/plugin_router_fixed.py +23 -0
  15. mindroot/coreplugins/admin/plugin_router_new_not_working.py +145 -0
  16. mindroot/coreplugins/admin/plugin_routes.py +114 -0
  17. mindroot/coreplugins/admin/registry_settings_routes.py +140 -0
  18. mindroot/coreplugins/admin/router.py +116 -15
  19. mindroot/coreplugins/admin/service_models.py +1 -1
  20. mindroot/coreplugins/admin/settings_router.py +1 -0
  21. mindroot/coreplugins/admin/static/css/admin-custom.css +357 -2
  22. mindroot/coreplugins/admin/static/css/dark.css +1 -0
  23. mindroot/coreplugins/admin/static/css/default.css +4 -0
  24. mindroot/coreplugins/admin/static/js/about-info.js +367 -0
  25. mindroot/coreplugins/admin/static/js/agent-form.js +83 -3
  26. mindroot/coreplugins/admin/static/js/api-key-script.js +307 -0
  27. mindroot/coreplugins/admin/static/js/mcp-manager.js +348 -0
  28. mindroot/coreplugins/admin/static/js/mcp-publisher.js +780 -0
  29. mindroot/coreplugins/admin/static/js/persona-editor.js +34 -5
  30. mindroot/coreplugins/admin/static/js/plugin-toggle.js +1 -1
  31. mindroot/coreplugins/admin/static/js/recommended-plugin-install.js +63 -0
  32. mindroot/coreplugins/admin/static/js/registry-auth-section.js +132 -0
  33. mindroot/coreplugins/admin/static/js/registry-manager-base.js +613 -0
  34. mindroot/coreplugins/admin/static/js/registry-manager-publish-old-delete.js +166 -0
  35. mindroot/coreplugins/admin/static/js/registry-manager.js +351 -0
  36. mindroot/coreplugins/admin/static/js/registry-publish-section.js +377 -0
  37. mindroot/coreplugins/admin/static/js/registry-search-section.js +400 -0
  38. mindroot/coreplugins/admin/static/js/registry-search-section.js.bak +3 -0
  39. mindroot/coreplugins/admin/static/js/registry-settings.js +69 -0
  40. mindroot/coreplugins/admin/static/js/registry-shared-services.js +903 -0
  41. mindroot/coreplugins/admin/static/js/registry-simple-sections.js +85 -0
  42. mindroot/coreplugins/admin/static/js/secure-widget-manager.js +438 -0
  43. mindroot/coreplugins/admin/static/logo.png +0 -0
  44. mindroot/coreplugins/admin/templates/admin.jinja2 +275 -110
  45. mindroot/coreplugins/agent/Assistant/agent.json +27 -11
  46. mindroot/coreplugins/agent/agent.py +2 -2
  47. mindroot/coreplugins/agent/command_parser.py +25 -10
  48. mindroot/coreplugins/agent/templates/system.jinja2 +0 -12
  49. mindroot/coreplugins/chat/__init__.py +4 -1
  50. mindroot/coreplugins/chat/router.py +132 -20
  51. mindroot/coreplugins/chat/router_dedup_patch.py +20 -0
  52. mindroot/coreplugins/chat/services.py +31 -1
  53. mindroot/coreplugins/chat/static/css/action-fix.css +32 -0
  54. mindroot/coreplugins/chat/static/css/admin-custom.css +5 -3
  55. mindroot/coreplugins/chat/static/css/dark.css +24 -3
  56. mindroot/coreplugins/chat/static/css/default.css +24 -3
  57. mindroot/coreplugins/chat/static/css/main.css +1 -0
  58. mindroot/coreplugins/chat/static/js/action.js +137 -60
  59. mindroot/coreplugins/chat/static/js/chat-history.js +3 -0
  60. mindroot/coreplugins/chat/static/js/chat.js +59 -16
  61. mindroot/coreplugins/chat/static/js/chat.js.diff +221 -0
  62. mindroot/coreplugins/chat/static/js/chatform.js +2 -2
  63. mindroot/coreplugins/chat/static/site.webmanifest +1 -1
  64. mindroot/coreplugins/chat/templates/chat.jinja2 +3 -3
  65. mindroot/coreplugins/chat/widget_manager.py +139 -0
  66. mindroot/coreplugins/chat/widget_routes.py +287 -0
  67. mindroot/coreplugins/check_list/inject/admin.jinja2 +1 -1
  68. mindroot/coreplugins/email/__init__.py +2 -0
  69. mindroot/coreplugins/email/email_provider.py +2 -2
  70. mindroot/coreplugins/email/mod.py +100 -0
  71. mindroot/coreplugins/email/services.py +5 -3
  72. mindroot/coreplugins/email/smtp_handler.py +9 -3
  73. mindroot/coreplugins/email/test_email_service.py +75 -0
  74. mindroot/coreplugins/env_manager/mod.py +61 -25
  75. mindroot/coreplugins/home/router.py +37 -2
  76. mindroot/coreplugins/home/static/imgs/logo.png +0 -0
  77. mindroot/coreplugins/home/static/imgs/logo.png.bak +0 -0
  78. mindroot/coreplugins/home/static/imgs/logo_teal.png +0 -0
  79. mindroot/coreplugins/home/static/imgs/logo_teal2.png +0 -0
  80. mindroot/coreplugins/home/static/imgs/logo_teal_detailed.png +0 -0
  81. mindroot/coreplugins/home/static/imgs/logo_teal_python.png +0 -0
  82. mindroot/coreplugins/home/templates/home.jinja2 +15 -6
  83. mindroot/coreplugins/index/indices/default/index.json +39 -6
  84. mindroot/coreplugins/jwt_auth/middleware.py +47 -2
  85. mindroot/coreplugins/jwt_auth/mod.py +40 -17
  86. mindroot/coreplugins/l8n/__init__.py +6 -0
  87. mindroot/coreplugins/l8n/debug_loader.py +85 -0
  88. mindroot/coreplugins/l8n/debug_middleware.py +74 -0
  89. mindroot/coreplugins/l8n/l8n_constants.py +19 -0
  90. mindroot/coreplugins/l8n/language_detection.py +183 -0
  91. mindroot/coreplugins/l8n/middleware.py +151 -0
  92. mindroot/coreplugins/l8n/mod.py +277 -0
  93. mindroot/coreplugins/l8n/monkey_patch_to_delete.py +186 -0
  94. mindroot/coreplugins/l8n/test_enhanced.py +298 -0
  95. mindroot/coreplugins/l8n/test_l8n.py +95 -0
  96. mindroot/coreplugins/l8n/test_l8n_standalone.py +251 -0
  97. mindroot/coreplugins/l8n/test_middleware.py +272 -0
  98. mindroot/coreplugins/l8n/utils.py +232 -0
  99. mindroot/coreplugins/mcp_/__init__.py +14 -0
  100. mindroot/coreplugins/mcp_/catalog_commands.py +328 -0
  101. mindroot/coreplugins/mcp_/catalog_manager.py +263 -0
  102. mindroot/coreplugins/mcp_/dynamic_commands.py +154 -0
  103. mindroot/coreplugins/mcp_/mcp_manager.py +1031 -0
  104. mindroot/coreplugins/mcp_/mod.py +367 -0
  105. mindroot/coreplugins/mcp_/oauth_storage.py +144 -0
  106. mindroot/coreplugins/mcp_/server_installer.py +79 -0
  107. mindroot/coreplugins/mcp_/setup.py +26 -0
  108. mindroot/coreplugins/mcp_/test_dynamic_commands.py +134 -0
  109. mindroot/coreplugins/mcp_/testmcpclient.py +92 -0
  110. mindroot/coreplugins/persona/mod.py +12 -7
  111. mindroot/coreplugins/signup/templates/signup.jinja2 +1 -1
  112. mindroot/coreplugins/subscriptions/__init__.py +1 -0
  113. mindroot/coreplugins/subscriptions/mod.py +14 -3
  114. mindroot/coreplugins/subscriptions/router.py +3 -0
  115. mindroot/coreplugins/user_service/__init__.py +1 -2
  116. mindroot/coreplugins/user_service/admin_init.py +1 -0
  117. mindroot/coreplugins/user_service/email_service.py +72 -17
  118. mindroot/coreplugins/user_service/mod.py +10 -2
  119. mindroot/coreplugins/user_service/router.py +2 -0
  120. mindroot/lib/auth/api_key.py +28 -0
  121. mindroot/lib/cli/plugins.py +94 -0
  122. mindroot/lib/plugins/default_plugin_manifest.json +20 -0
  123. mindroot/lib/plugins/installation.py +5 -5
  124. mindroot/lib/plugins/l8n_static_handler.py +225 -0
  125. mindroot/lib/plugins/loader.py +33 -3
  126. mindroot/lib/plugins/loader_with_l8n.py +281 -0
  127. mindroot/lib/plugins/manifest.py +236 -24
  128. mindroot/lib/providers/commands.py +3 -1
  129. mindroot/lib/route_decorators.py +5 -5
  130. mindroot/lib/templates.py +183 -11
  131. mindroot/lib/utils/merge_arrays.py +1 -1
  132. mindroot/migrate.py +39 -20
  133. mindroot/registry/data_access.py +1 -1
  134. mindroot/server.py +42 -13
  135. mindroot/server_missing_normal_args.py +197 -0
  136. mindroot/server_prev.py +173 -0
  137. {mindroot-9.3.0.dist-info → mindroot-9.6.0.dist-info}/METADATA +7 -2
  138. {mindroot-9.3.0.dist-info → mindroot-9.6.0.dist-info}/RECORD +143 -113
  139. mindroot/coreplugins/admin/plugin_manager_backup.py +0 -615
  140. mindroot/coreplugins/admin/static/favicon/about.txt +0 -6
  141. mindroot/coreplugins/admin/static/favicon/android-chrome-512x512.png +0 -0
  142. mindroot/coreplugins/admin/static/favicon/apple-touch-icon.png +0 -0
  143. mindroot/coreplugins/admin/static/favicon/favicon-16x16.png +0 -0
  144. mindroot/coreplugins/admin/static/favicon/favicon-32x32.png +0 -0
  145. mindroot/coreplugins/admin/static/favicon/favicon.ico +0 -0
  146. mindroot/coreplugins/admin/static/favicon/favicon_io (1)/about.txt +0 -6
  147. mindroot/coreplugins/admin/static/favicon/favicon_io (1)/android-chrome-192x192.png +0 -0
  148. mindroot/coreplugins/admin/static/favicon/favicon_io (1)/android-chrome-512x512.png +0 -0
  149. mindroot/coreplugins/admin/static/favicon/favicon_io (1)/apple-touch-icon.png +0 -0
  150. mindroot/coreplugins/admin/static/favicon/favicon_io (1)/favicon-16x16.png +0 -0
  151. mindroot/coreplugins/admin/static/favicon/favicon_io (1)/favicon-32x32.png +0 -0
  152. mindroot/coreplugins/admin/static/favicon/favicon_io (1)/favicon.ico +0 -0
  153. mindroot/coreplugins/admin/static/favicon/favicon_io (1)/site.webmanifest +0 -1
  154. mindroot/coreplugins/admin/static/favicon/logo.png +0 -0
  155. mindroot/coreplugins/admin/static/favicon/site.webmanifest +0 -1
  156. mindroot/coreplugins/admin/static/js/backup/agent-editor.js +0 -186
  157. mindroot/coreplugins/admin/static/js/backup/agent-form.js +0 -1133
  158. mindroot/coreplugins/admin/static/js/backup/agent-list.js +0 -94
  159. mindroot/coreplugins/chat/static/favicon/about.txt +0 -6
  160. mindroot/coreplugins/chat/static/favicon/android-chrome-192x192.png +0 -0
  161. mindroot/coreplugins/chat/static/favicon/android-chrome-512x512.png +0 -0
  162. mindroot/coreplugins/chat/static/favicon/apple-touch-icon.png +0 -0
  163. mindroot/coreplugins/chat/static/favicon/favicon-16x16.png +0 -0
  164. mindroot/coreplugins/chat/static/favicon/favicon-32x32.png +0 -0
  165. mindroot/coreplugins/chat/static/favicon/favicon.ico +0 -0
  166. mindroot/coreplugins/chat/static/favicon/favicon_io (1)/about.txt +0 -6
  167. mindroot/coreplugins/chat/static/favicon/favicon_io (1)/android-chrome-192x192.png +0 -0
  168. mindroot/coreplugins/chat/static/favicon/favicon_io (1)/android-chrome-512x512.png +0 -0
  169. mindroot/coreplugins/chat/static/favicon/favicon_io (1)/apple-touch-icon.png +0 -0
  170. mindroot/coreplugins/chat/static/favicon/favicon_io (1)/favicon-16x16.png +0 -0
  171. mindroot/coreplugins/chat/static/favicon/favicon_io (1)/favicon-32x32.png +0 -0
  172. mindroot/coreplugins/chat/static/favicon/favicon_io (1)/favicon.ico +0 -0
  173. mindroot/coreplugins/chat/static/favicon/favicon_io (1)/site.webmanifest +0 -1
  174. mindroot/coreplugins/chat/static/favicon/logo.png +0 -0
  175. mindroot/coreplugins/chat/static/favicon/site.webmanifest +0 -1
  176. mindroot/coreplugins/index/default.json +0 -76
  177. mindroot/coreplugins/user_service/file_trigger_service.py +0 -12
  178. mindroot/coreplugins/user_service/hooks.py +0 -23
  179. /mindroot/coreplugins/{admin/static/favicon/android-chrome-192x192.png → home/static/imgs/backuplogo.png} +0 -0
  180. {mindroot-9.3.0.dist-info → mindroot-9.6.0.dist-info}/WHEEL +0 -0
  181. {mindroot-9.3.0.dist-info → mindroot-9.6.0.dist-info}/entry_points.txt +0 -0
  182. {mindroot-9.3.0.dist-info → mindroot-9.6.0.dist-info}/licenses/LICENSE +0 -0
  183. {mindroot-9.3.0.dist-info → mindroot-9.6.0.dist-info}/top_level.txt +0 -0
@@ -5,7 +5,12 @@ from lib.plugins.installation import plugin_install
5
5
  from lib.providers.missing import get_missing_commands
6
6
  from lib.plugins.mapping import get_command_plugin_mapping
7
7
  from pathlib import Path
8
- from lib.plugins.installation import install_recommended_plugins
8
+ from lib.plugins.installation import install_recommended_plugins, download_github_files
9
+ from lib.plugins.manifest import update_plugin_manifest
10
+ # For Server-Sent Events (streamed logs)
11
+ from sse_starlette.sse import EventSourceResponse
12
+ from lib.streamcmd import stream_command_as_events
13
+ import contextlib, io, json, asyncio, sys
9
14
 
10
15
  router = APIRouter()
11
16
 
@@ -20,6 +25,121 @@ async def missing_commands(agent_name: str):
20
25
  raise HTTPException(status_code=500, detail=result["error"])
21
26
  return result
22
27
 
28
+ # ------------------------------------------------------------------
29
+ # NEW: Stream installation of recommended plugins with live logs
30
+ # ------------------------------------------------------------------
31
+
32
+ @router.get("/admin/stream-install-recommended-plugins/{agent_name}", response_class=EventSourceResponse)
33
+ async def stream_install_recommended_plugins(agent_name: str):
34
+ """
35
+ SSE endpoint that installs all recommended plugins for `agent_name`
36
+ and streams stdout/stderr lines back to the browser. It finishes by
37
+ sending a JSON summary and the literal string 'END'.
38
+ """
39
+
40
+ async def event_generator():
41
+ try:
42
+ # Load agent data
43
+ agent_path = f"data/agents/local/{agent_name}/agent.json"
44
+ if not os.path.exists(agent_path):
45
+ agent_path = f"data/agents/shared/{agent_name}/agent.json"
46
+
47
+ if not os.path.exists(agent_path):
48
+ yield f"Error: Agent {agent_name} not found\n"
49
+ yield "END\n"
50
+ return
51
+
52
+ with open(agent_path, 'r') as f:
53
+ agent_data = json.load(f)
54
+
55
+ # Get recommended plugins
56
+ recommended_plugins = agent_data.get('recommended_plugins', agent_data.get('required_plugins', []))
57
+
58
+ if not recommended_plugins:
59
+ yield "No recommended plugins found for this agent\n"
60
+ yield "END\n"
61
+ return
62
+
63
+ yield f"Installing {len(recommended_plugins)} recommended plugins for {agent_name}...\n"
64
+
65
+ results = []
66
+
67
+ # Install each plugin and stream the output
68
+ for plugin_source in recommended_plugins:
69
+ plugin_name = plugin_source.split('/')[-1]
70
+
71
+ yield f"\n=== Installing {plugin_name} ===\n"
72
+
73
+ try:
74
+ # Check if already installed
75
+ import pkg_resources
76
+ try:
77
+ pkg_resources.get_distribution(plugin_name)
78
+ yield f"{plugin_name} is already installed\n"
79
+ results.append({"plugin": plugin_name, "status": "already_installed"})
80
+ continue
81
+ except pkg_resources.DistributionNotFound:
82
+ pass
83
+
84
+ # Determine installation source
85
+ if '/' in plugin_source: # GitHub format
86
+ yield f"Installing from GitHub: {plugin_source}\n"
87
+
88
+ # Download and extract
89
+ plugin_dir, _, plugin_info = download_github_files(plugin_source)
90
+
91
+ # Stream the pip install
92
+ cmd = [sys.executable, '-m', 'pip', 'install', '-e', plugin_dir, '-v']
93
+ async for event in stream_command_as_events(cmd):
94
+ if event.get('event') == 'message':
95
+ yield event['data'] + "\n"
96
+ elif event.get('event') == 'warning':
97
+ yield "WARNING: " + event['data'] + "\n"
98
+ elif event.get('event') == 'error':
99
+ yield "ERROR: " + event['data'] + "\n"
100
+ elif event.get('event') == 'complete':
101
+ # Update the plugin manifest after successful installation
102
+ update_plugin_manifest(
103
+ plugin_info['name'],
104
+ 'github',
105
+ os.path.abspath(plugin_dir),
106
+ remote_source=plugin_source,
107
+ version=plugin_info.get('version', '0.0.1'),
108
+ metadata=plugin_info
109
+ )
110
+
111
+ results.append({"plugin": plugin_name, "status": "success", "source": "github", "source_path": plugin_source})
112
+ else:
113
+ # PyPI installation
114
+ yield f"Installing from PyPI: {plugin_name}\n"
115
+ cmd = [sys.executable, '-m', 'pip', 'install', plugin_name, '-v']
116
+ async for event in stream_command_as_events(cmd):
117
+ if event.get('event') == 'message':
118
+ yield event['data'] + "\n"
119
+ elif event.get('event') == 'warning':
120
+ yield "WARNING: " + event['data'] + "\n"
121
+ elif event.get('event') == 'error':
122
+ yield "ERROR: " + event['data'] + "\n"
123
+ elif event.get('event') == 'complete':
124
+ # Update the plugin manifest for PyPI install
125
+ update_plugin_manifest(plugin_name, 'pypi', None)
126
+
127
+ results.append({"plugin": plugin_name, "status": "success", "source": "pypi"})
128
+
129
+ except Exception as e:
130
+ yield f"ERROR: Failed to install {plugin_name}: {str(e)}\n"
131
+ results.append({"plugin": plugin_name, "status": "error", "message": str(e)})
132
+
133
+ # Send results and END
134
+ yield "\n" + json.dumps({"results": results}) + "\n"
135
+ yield "END"
136
+
137
+ except Exception as e:
138
+ yield f"ERROR: {str(e)}\n"
139
+ yield "END"
140
+
141
+ return EventSourceResponse(event_generator())
142
+
23
143
  @router.get("/admin/command-plugin-mapping")
24
144
  async def command_plugin_mapping():
25
145
  """Get mapping of commands to plugins that provide them."""
@@ -159,3 +279,118 @@ async def install_agent_recommended_plugins(agent_name: str):
159
279
  if "error" in result:
160
280
  raise HTTPException(status_code=500, detail=result["error"])
161
281
  return result
282
+
283
+ # ------------------------------------------------------------------
284
+ # NEW: Stream installation of recommended plugins with live logs
285
+ # ------------------------------------------------------------------
286
+
287
+ @router.get("/admin/stream-install-recommended-plugins/{agent_name}", response_class=EventSourceResponse)
288
+ async def stream_install_recommended_plugins(agent_name: str):
289
+ """
290
+ SSE endpoint that installs all recommended plugins for `agent_name`
291
+ and streams stdout/stderr lines back to the browser. It finishes by
292
+ sending a JSON summary and the literal string 'END'.
293
+ """
294
+
295
+ async def event_generator():
296
+ try:
297
+ # Load agent data
298
+ agent_path = f"data/agents/local/{agent_name}/agent.json"
299
+ if not os.path.exists(agent_path):
300
+ agent_path = f"data/agents/shared/{agent_name}/agent.json"
301
+
302
+ if not os.path.exists(agent_path):
303
+ yield f"Error: Agent {agent_name} not found\n"
304
+ yield "END\n"
305
+ return
306
+
307
+ with open(agent_path, 'r') as f:
308
+ agent_data = json.load(f)
309
+
310
+ # Get recommended plugins
311
+ recommended_plugins = agent_data.get('recommended_plugins', agent_data.get('required_plugins', []))
312
+
313
+ if not recommended_plugins:
314
+ yield "No recommended plugins found for this agent\n"
315
+ yield "END\n"
316
+ return
317
+
318
+ yield f"Installing {len(recommended_plugins)} recommended plugins for {agent_name}...\n"
319
+
320
+ results = []
321
+
322
+ # Install each plugin and stream the output
323
+ for plugin_source in recommended_plugins:
324
+ plugin_name = plugin_source.split('/')[-1]
325
+
326
+ yield f"\n=== Installing {plugin_name} ===\n"
327
+
328
+ try:
329
+ # Check if already installed
330
+ import pkg_resources
331
+ try:
332
+ pkg_resources.get_distribution(plugin_name)
333
+ yield f"{plugin_name} is already installed\n"
334
+ results.append({"plugin": plugin_name, "status": "already_installed"})
335
+ continue
336
+ except pkg_resources.DistributionNotFound:
337
+ pass
338
+
339
+ # Determine installation source
340
+ if '/' in plugin_source: # GitHub format
341
+ yield f"Installing from GitHub: {plugin_source}\n"
342
+
343
+ # Download and extract
344
+ plugin_dir, _, plugin_info = download_github_files(plugin_source)
345
+
346
+ # Stream the pip install
347
+ cmd = [sys.executable, '-m', 'pip', 'install', '-e', plugin_dir, '-v']
348
+ async for event in stream_command_as_events(cmd):
349
+ if event.get('event') == 'message':
350
+ yield event['data'] + "\n"
351
+ elif event.get('event') == 'warning':
352
+ yield "WARNING: " + event['data'] + "\n"
353
+ elif event.get('event') == 'error':
354
+ yield "ERROR: " + event['data'] + "\n"
355
+ elif event.get('event') == 'complete':
356
+ # Update the plugin manifest after successful installation
357
+ update_plugin_manifest(
358
+ plugin_info['name'],
359
+ 'github',
360
+ os.path.abspath(plugin_dir),
361
+ remote_source=plugin_source,
362
+ version=plugin_info.get('version', '0.0.1'),
363
+ metadata=plugin_info
364
+ )
365
+
366
+ results.append({"plugin": plugin_name, "status": "success", "source": "github", "source_path": plugin_source})
367
+ else:
368
+ # PyPI installation
369
+ yield f"Installing from PyPI: {plugin_name}\n"
370
+ cmd = [sys.executable, '-m', 'pip', 'install', plugin_name, '-v']
371
+ async for event in stream_command_as_events(cmd):
372
+ if event.get('event') == 'message':
373
+ yield event['data'] + "\n"
374
+ elif event.get('event') == 'warning':
375
+ yield "WARNING: " + event['data'] + "\n"
376
+ elif event.get('event') == 'error':
377
+ yield "ERROR: " + event['data'] + "\n"
378
+ elif event.get('event') == 'complete':
379
+ # Update the plugin manifest for PyPI install
380
+ update_plugin_manifest(plugin_name, 'pypi', None)
381
+
382
+ results.append({"plugin": plugin_name, "status": "success", "source": "pypi"})
383
+
384
+ except Exception as e:
385
+ yield f"ERROR: Failed to install {plugin_name}: {str(e)}\n"
386
+ results.append({"plugin": plugin_name, "status": "error", "message": str(e)})
387
+
388
+ # Send results and END
389
+ yield "\n" + json.dumps({"results": results}) + "\n"
390
+ yield "END"
391
+
392
+ except Exception as e:
393
+ yield f"ERROR: {str(e)}\n"
394
+ yield "END"
395
+
396
+ return EventSourceResponse(event_generator())
@@ -0,0 +1,156 @@
1
+ from fastapi import APIRouter, HTTPException
2
+ from typing import Optional
3
+ from lib.route_decorators import requires_role
4
+
5
+ # Import MCP components - use the actual MCP system
6
+ try:
7
+ from mindroot.coreplugins.mcp.mod import mcp_manager, MCPServer
8
+ from mindroot.coreplugins.mcp.catalog_manager import MCPCatalogManager
9
+ except ImportError:
10
+ # Mock objects if MCP plugin is not fully installed, to prevent startup crash
11
+ mcp_manager = None
12
+ MCPServer = None
13
+ MCPCatalogManager = None
14
+
15
+ # Create router with admin role requirement
16
+ router = APIRouter(
17
+ dependencies=[requires_role('admin')]
18
+ )
19
+
20
+ # --- MCP Catalog/Directory Routes ---
21
+
22
+ @router.get("/mcp/catalog")
23
+ async def get_mcp_catalog():
24
+ """Get the MCP server catalog/directory."""
25
+ if not MCPCatalogManager:
26
+ raise HTTPException(status_code=501, detail="MCP Catalog not available")
27
+
28
+ try:
29
+ catalog_manager = MCPCatalogManager()
30
+ catalog = catalog_manager.load_catalog()
31
+
32
+ # Update running status
33
+ catalog_manager.update_server_status()
34
+
35
+ return {
36
+ "success": True,
37
+ "data": catalog
38
+ }
39
+ except Exception as e:
40
+ raise HTTPException(status_code=500, detail=str(e))
41
+
42
+ @router.get("/mcp/catalog/search")
43
+ async def search_mcp_catalog(query: str = "", category: Optional[str] = None):
44
+ """Search the MCP server catalog."""
45
+ if not MCPCatalogManager:
46
+ raise HTTPException(status_code=501, detail="MCP Catalog not available")
47
+
48
+ try:
49
+ catalog_manager = MCPCatalogManager()
50
+
51
+ if query:
52
+ results = catalog_manager.search_servers(query)
53
+ elif category:
54
+ results = catalog_manager.get_servers_by_category(category)
55
+ else:
56
+ catalog = catalog_manager.load_catalog()
57
+ results = catalog.get("servers", {})
58
+
59
+ return {
60
+ "success": True,
61
+ "data": results
62
+ }
63
+ except Exception as e:
64
+ raise HTTPException(status_code=500, detail=str(e))
65
+
66
+ @router.get("/mcp/catalog/categories")
67
+ async def get_mcp_categories():
68
+ """Get available MCP server categories."""
69
+ if not MCPCatalogManager:
70
+ raise HTTPException(status_code=501, detail="MCP Catalog not available")
71
+
72
+ try:
73
+ catalog_manager = MCPCatalogManager()
74
+ categories = catalog_manager.get_categories()
75
+
76
+ return {
77
+ "success": True,
78
+ "data": categories
79
+ }
80
+ except Exception as e:
81
+ raise HTTPException(status_code=500, detail=str(e))
82
+
83
+ @router.post("/mcp/catalog/install")
84
+ async def install_from_catalog(server_name: str):
85
+ """Install an MCP server from the catalog."""
86
+ if not MCPCatalogManager or not mcp_manager:
87
+ raise HTTPException(status_code=501, detail="MCP system not available")
88
+
89
+ try:
90
+ catalog_manager = MCPCatalogManager()
91
+ server_info = catalog_manager.get_server_info(server_name)
92
+
93
+ if not server_info:
94
+ raise HTTPException(status_code=404, detail=f"MCP server '{server_name}' not found in catalog")
95
+
96
+ # Create MCPServer object from catalog info
97
+ server = MCPServer(
98
+ name=server_name,
99
+ description=server_info.get("description", ""),
100
+ command=server_info.get("command", ""),
101
+ args=server_info.get("args", []),
102
+ env=server_info.get("env", {}),
103
+ transport=server_info.get("transport", "stdio"),
104
+ url=server_info.get("url")
105
+ )
106
+
107
+ # Add to MCP manager
108
+ mcp_manager.add_server(server_name, server)
109
+
110
+ # Mark as installed in catalog
111
+ catalog_manager.mark_server_installed(server_name, True)
112
+
113
+ return {
114
+ "success": True,
115
+ "message": f"MCP server '{server_name}' installed from catalog successfully."
116
+ }
117
+ except Exception as e:
118
+ raise HTTPException(status_code=500, detail=str(e))
119
+
120
+ @router.get("/mcp/catalog/server/{server_name}")
121
+ async def get_catalog_server_info(server_name: str):
122
+ """Get detailed information about a specific server from the catalog."""
123
+ if not MCPCatalogManager:
124
+ raise HTTPException(status_code=501, detail="MCP Catalog not available")
125
+
126
+ try:
127
+ catalog_manager = MCPCatalogManager()
128
+ server_info = catalog_manager.get_server_info(server_name)
129
+
130
+ if not server_info:
131
+ raise HTTPException(status_code=404, detail=f"MCP server '{server_name}' not found in catalog")
132
+
133
+ return {
134
+ "success": True,
135
+ "data": server_info
136
+ }
137
+ except Exception as e:
138
+ raise HTTPException(status_code=500, detail=str(e))
139
+
140
+ @router.post("/mcp/catalog/refresh")
141
+ async def refresh_catalog_status():
142
+ """Refresh the running status of all servers in the catalog."""
143
+ if not MCPCatalogManager:
144
+ raise HTTPException(status_code=501, detail="MCP Catalog not available")
145
+
146
+ try:
147
+ catalog_manager = MCPCatalogManager()
148
+ updated_catalog = catalog_manager.update_server_status()
149
+
150
+ return {
151
+ "success": True,
152
+ "message": "Catalog status refreshed successfully.",
153
+ "data": updated_catalog
154
+ }
155
+ except Exception as e:
156
+ raise HTTPException(status_code=500, detail=str(e))